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
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
566 // (these values are only valid for BD style levels)
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
570 &li.bd_diagonal_movements, FALSE
573 // (the following values are related to various game elements)
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_EMERALD], 10
583 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
584 &li.score[SC_DIAMOND], 10
589 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
590 &li.score[SC_BUG], 10
595 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
596 &li.score[SC_SPACESHIP], 10
601 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
602 &li.score[SC_PACMAN], 10
607 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
608 &li.score[SC_NUT], 10
613 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
614 &li.score[SC_DYNAMITE], 10
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
620 &li.score[SC_KEY], 10
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_PEARL], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_CRYSTAL], 10
637 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
638 &li.amoeba_content, EL_DIAMOND
642 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
647 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
648 &li.grow_into_diggable, TRUE
653 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
654 &li.yamyam_content, EL_ROCK, NULL,
655 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.score[SC_YAMYAM], 10
665 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
666 &li.score[SC_ROBOT], 10
670 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
676 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
683 &li.time_magic_wall, 10
688 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
689 &li.game_of_life[0], 2
693 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
694 &li.game_of_life[1], 3
698 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
699 &li.game_of_life[2], 3
703 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
704 &li.game_of_life[3], 3
708 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
709 &li.use_life_bugs, FALSE
714 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
719 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
724 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
729 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
734 EL_TIMEGATE_SWITCH, -1,
735 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 &li.time_timegate, 10
740 EL_LIGHT_SWITCH_ACTIVE, -1,
741 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
746 EL_SHIELD_NORMAL, -1,
747 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
748 &li.shield_normal_time, 10
751 EL_SHIELD_NORMAL, -1,
752 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
753 &li.score[SC_SHIELD], 10
757 EL_SHIELD_DEADLY, -1,
758 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
759 &li.shield_deadly_time, 10
762 EL_SHIELD_DEADLY, -1,
763 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
764 &li.score[SC_SHIELD], 10
769 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
774 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
775 &li.extra_time_score, 10
779 EL_TIME_ORB_FULL, -1,
780 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
781 &li.time_orb_time, 10
784 EL_TIME_ORB_FULL, -1,
785 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
786 &li.use_time_orb_bug, FALSE
791 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
792 &li.use_spring_bug, FALSE
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
798 &li.android_move_time, 10
802 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
803 &li.android_clone_time, 10
806 EL_EMC_ANDROID, SAVE_CONF_NEVER,
807 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
808 &li.android_clone_element[0], EL_EMPTY, NULL,
809 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
813 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
814 &li.android_clone_element[0], EL_EMPTY, NULL,
815 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
820 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
825 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
830 EL_EMC_MAGNIFIER, -1,
831 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
832 &li.magnify_score, 10
835 EL_EMC_MAGNIFIER, -1,
836 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
841 EL_EMC_MAGIC_BALL, -1,
842 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
846 EL_EMC_MAGIC_BALL, -1,
847 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
848 &li.ball_random, FALSE
851 EL_EMC_MAGIC_BALL, -1,
852 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
853 &li.ball_active_initial, FALSE
856 EL_EMC_MAGIC_BALL, -1,
857 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
858 &li.ball_content, EL_EMPTY, NULL,
859 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
863 EL_SOKOBAN_FIELD_EMPTY, -1,
864 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
865 &li.sb_fields_needed, TRUE
869 EL_SOKOBAN_OBJECT, -1,
870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
871 &li.sb_objects_needed, TRUE
876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
877 &li.mm_laser_red, FALSE
881 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
882 &li.mm_laser_green, FALSE
886 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
887 &li.mm_laser_blue, TRUE
892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
893 &li.df_laser_red, TRUE
897 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
898 &li.df_laser_green, TRUE
902 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
903 &li.df_laser_blue, FALSE
907 EL_MM_FUSE_ACTIVE, -1,
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
924 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
925 &li.mm_ball_choice_mode, ANIM_RANDOM
929 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
930 &li.mm_ball_content, EL_EMPTY, NULL,
931 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
936 &li.rotate_mm_ball_content, TRUE
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
941 &li.explode_mm_ball, FALSE
945 EL_MM_STEEL_BLOCK, -1,
946 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
947 &li.mm_time_block, 75
951 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
952 &li.score[SC_ELEM_BONUS], 10
955 // ---------- unused values -------------------------------------------------
958 EL_UNKNOWN, SAVE_CONF_NEVER,
959 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
960 &li.score[SC_UNKNOWN_15], 10
970 static struct LevelFileConfigInfo chunk_config_NOTE[] =
974 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
975 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
979 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
980 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
985 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
986 &xx_envelope.autowrap, FALSE
990 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
991 &xx_envelope.centered, FALSE
996 TYPE_STRING, CONF_VALUE_BYTES(1),
997 &xx_envelope.text, -1, NULL,
998 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
999 &xx_default_string_empty[0]
1009 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1013 TYPE_STRING, CONF_VALUE_BYTES(1),
1014 &xx_ei.description[0], -1,
1015 &yy_ei.description[0],
1016 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1017 &xx_default_description[0]
1022 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1023 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1024 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1026 #if ENABLE_RESERVED_CODE
1027 // (reserved for later use)
1030 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1031 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1032 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1038 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1039 &xx_ei.use_gfx_element, FALSE,
1040 &yy_ei.use_gfx_element
1044 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1045 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1046 &yy_ei.gfx_element_initial
1051 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1052 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1053 &yy_ei.access_direction
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1059 &xx_ei.collect_score_initial, 10,
1060 &yy_ei.collect_score_initial
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1065 &xx_ei.collect_count_initial, 1,
1066 &yy_ei.collect_count_initial
1071 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1072 &xx_ei.ce_value_fixed_initial, 0,
1073 &yy_ei.ce_value_fixed_initial
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1078 &xx_ei.ce_value_random_initial, 0,
1079 &yy_ei.ce_value_random_initial
1083 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1084 &xx_ei.use_last_ce_value, FALSE,
1085 &yy_ei.use_last_ce_value
1090 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1091 &xx_ei.push_delay_fixed, 8,
1092 &yy_ei.push_delay_fixed
1096 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1097 &xx_ei.push_delay_random, 8,
1098 &yy_ei.push_delay_random
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1103 &xx_ei.drop_delay_fixed, 0,
1104 &yy_ei.drop_delay_fixed
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1109 &xx_ei.drop_delay_random, 0,
1110 &yy_ei.drop_delay_random
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1115 &xx_ei.move_delay_fixed, 0,
1116 &yy_ei.move_delay_fixed
1120 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1121 &xx_ei.move_delay_random, 0,
1122 &yy_ei.move_delay_random
1126 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1127 &xx_ei.step_delay_fixed, 0,
1128 &yy_ei.step_delay_fixed
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1133 &xx_ei.step_delay_random, 0,
1134 &yy_ei.step_delay_random
1139 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1140 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1145 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1146 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1147 &yy_ei.move_direction_initial
1151 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1152 &xx_ei.move_stepsize, TILEX / 8,
1153 &yy_ei.move_stepsize
1158 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1159 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1160 &yy_ei.move_enter_element
1164 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1165 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1166 &yy_ei.move_leave_element
1170 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1171 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1172 &yy_ei.move_leave_type
1177 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1178 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1179 &yy_ei.slippery_type
1184 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1185 &xx_ei.explosion_type, EXPLODES_3X3,
1186 &yy_ei.explosion_type
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1191 &xx_ei.explosion_delay, 16,
1192 &yy_ei.explosion_delay
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1197 &xx_ei.ignition_delay, 8,
1198 &yy_ei.ignition_delay
1203 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1204 &xx_ei.content, EL_EMPTY_SPACE,
1206 &xx_num_contents, 1, 1
1209 // ---------- "num_change_pages" must be the last entry ---------------------
1212 -1, SAVE_CONF_ALWAYS,
1213 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1214 &xx_ei.num_change_pages, 1,
1215 &yy_ei.num_change_pages
1226 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1228 // ---------- "current_change_page" must be the first entry -----------------
1231 -1, SAVE_CONF_ALWAYS,
1232 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1233 &xx_current_change_page, -1
1236 // ---------- (the remaining entries can be in any order) -------------------
1240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1241 &xx_change.can_change, FALSE
1246 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1247 &xx_event_bits[0], 0
1251 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1252 &xx_event_bits[1], 0
1257 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1258 &xx_change.trigger_player, CH_PLAYER_ANY
1262 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1263 &xx_change.trigger_side, CH_SIDE_ANY
1267 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1268 &xx_change.trigger_page, CH_PAGE_ANY
1273 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1274 &xx_change.target_element, EL_EMPTY_SPACE
1279 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1280 &xx_change.delay_fixed, 0
1284 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1285 &xx_change.delay_random, 0
1289 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1290 &xx_change.delay_frames, FRAMES_PER_SECOND
1295 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1296 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1302 &xx_change.explode, FALSE
1306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1307 &xx_change.use_target_content, FALSE
1311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1312 &xx_change.only_if_complete, FALSE
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1317 &xx_change.use_random_replace, FALSE
1321 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1322 &xx_change.random_percentage, 100
1326 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1327 &xx_change.replace_when, CP_WHEN_EMPTY
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1333 &xx_change.has_action, FALSE
1337 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1338 &xx_change.action_type, CA_NO_ACTION
1342 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1343 &xx_change.action_mode, CA_MODE_UNDEFINED
1347 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1348 &xx_change.action_arg, CA_ARG_UNDEFINED
1353 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1354 &xx_change.action_element, EL_EMPTY_SPACE
1359 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1360 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1361 &xx_num_contents, 1, 1
1371 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1375 TYPE_STRING, CONF_VALUE_BYTES(1),
1376 &xx_ei.description[0], -1, NULL,
1377 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1378 &xx_default_description[0]
1383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1384 &xx_ei.use_gfx_element, FALSE
1388 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1389 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1394 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1395 &xx_group.choice_mode, ANIM_RANDOM
1400 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1401 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1402 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1412 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1417 &xx_ei.use_gfx_element, FALSE
1421 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1422 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1432 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1437 &li.block_snap_field, TRUE
1441 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1442 &li.continuous_snapping, TRUE
1446 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1447 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1452 &li.use_start_element[0], FALSE
1456 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1457 &li.start_element[0], EL_PLAYER_1
1461 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1462 &li.use_artwork_element[0], FALSE
1466 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1467 &li.artwork_element[0], EL_PLAYER_1
1471 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1472 &li.use_explosion_element[0], FALSE
1476 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1477 &li.explosion_element[0], EL_PLAYER_1
1492 filetype_id_list[] =
1494 { LEVEL_FILE_TYPE_RND, "RND" },
1495 { LEVEL_FILE_TYPE_BD, "BD" },
1496 { LEVEL_FILE_TYPE_EM, "EM" },
1497 { LEVEL_FILE_TYPE_SP, "SP" },
1498 { LEVEL_FILE_TYPE_DX, "DX" },
1499 { LEVEL_FILE_TYPE_SB, "SB" },
1500 { LEVEL_FILE_TYPE_DC, "DC" },
1501 { LEVEL_FILE_TYPE_MM, "MM" },
1502 { LEVEL_FILE_TYPE_MM, "DF" },
1507 // ============================================================================
1508 // level file functions
1509 // ============================================================================
1511 static boolean check_special_flags(char *flag)
1513 if (strEqual(options.special_flags, flag) ||
1514 strEqual(leveldir_current->special_flags, flag))
1520 static struct DateInfo getCurrentDate(void)
1522 time_t epoch_seconds = time(NULL);
1523 struct tm *now = localtime(&epoch_seconds);
1524 struct DateInfo date;
1526 date.year = now->tm_year + 1900;
1527 date.month = now->tm_mon + 1;
1528 date.day = now->tm_mday;
1530 date.src = DATE_SRC_CLOCK;
1535 static void resetEventFlags(struct ElementChangeInfo *change)
1539 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1540 change->has_event[i] = FALSE;
1543 static void resetEventBits(void)
1547 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1548 xx_event_bits[i] = 0;
1551 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1555 /* important: only change event flag if corresponding event bit is set
1556 (this is because all xx_event_bits[] values are loaded separately,
1557 and all xx_event_bits[] values are set back to zero before loading
1558 another value xx_event_bits[x] (each value representing 32 flags)) */
1560 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1561 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1562 change->has_event[i] = TRUE;
1565 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1569 /* in contrast to the above function setEventFlagsFromEventBits(), it
1570 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1571 depending on the corresponding change->has_event[i] values here, as
1572 all xx_event_bits[] values are reset in resetEventBits() before */
1574 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1575 if (change->has_event[i])
1576 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1579 static char *getDefaultElementDescription(struct ElementInfo *ei)
1581 static char description[MAX_ELEMENT_NAME_LEN + 1];
1582 char *default_description = (ei->custom_description != NULL ?
1583 ei->custom_description :
1584 ei->editor_description);
1587 // always start with reliable default values
1588 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1589 description[i] = '\0';
1591 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1592 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1594 return &description[0];
1597 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1599 char *default_description = getDefaultElementDescription(ei);
1602 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1603 ei->description[i] = default_description[i];
1606 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1610 for (i = 0; conf[i].data_type != -1; i++)
1612 int default_value = conf[i].default_value;
1613 int data_type = conf[i].data_type;
1614 int conf_type = conf[i].conf_type;
1615 int byte_mask = conf_type & CONF_MASK_BYTES;
1617 if (byte_mask == CONF_MASK_MULTI_BYTES)
1619 int default_num_entities = conf[i].default_num_entities;
1620 int max_num_entities = conf[i].max_num_entities;
1622 *(int *)(conf[i].num_entities) = default_num_entities;
1624 if (data_type == TYPE_STRING)
1626 char *default_string = conf[i].default_string;
1627 char *string = (char *)(conf[i].value);
1629 strncpy(string, default_string, max_num_entities);
1631 else if (data_type == TYPE_ELEMENT_LIST)
1633 int *element_array = (int *)(conf[i].value);
1636 for (j = 0; j < max_num_entities; j++)
1637 element_array[j] = default_value;
1639 else if (data_type == TYPE_CONTENT_LIST)
1641 struct Content *content = (struct Content *)(conf[i].value);
1644 for (c = 0; c < max_num_entities; c++)
1645 for (y = 0; y < 3; y++)
1646 for (x = 0; x < 3; x++)
1647 content[c].e[x][y] = default_value;
1650 else // constant size configuration data (1, 2 or 4 bytes)
1652 if (data_type == TYPE_BOOLEAN)
1653 *(boolean *)(conf[i].value) = default_value;
1655 *(int *) (conf[i].value) = default_value;
1660 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1664 for (i = 0; conf[i].data_type != -1; i++)
1666 int data_type = conf[i].data_type;
1667 int conf_type = conf[i].conf_type;
1668 int byte_mask = conf_type & CONF_MASK_BYTES;
1670 if (byte_mask == CONF_MASK_MULTI_BYTES)
1672 int max_num_entities = conf[i].max_num_entities;
1674 if (data_type == TYPE_STRING)
1676 char *string = (char *)(conf[i].value);
1677 char *string_copy = (char *)(conf[i].value_copy);
1679 strncpy(string_copy, string, max_num_entities);
1681 else if (data_type == TYPE_ELEMENT_LIST)
1683 int *element_array = (int *)(conf[i].value);
1684 int *element_array_copy = (int *)(conf[i].value_copy);
1687 for (j = 0; j < max_num_entities; j++)
1688 element_array_copy[j] = element_array[j];
1690 else if (data_type == TYPE_CONTENT_LIST)
1692 struct Content *content = (struct Content *)(conf[i].value);
1693 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1696 for (c = 0; c < max_num_entities; c++)
1697 for (y = 0; y < 3; y++)
1698 for (x = 0; x < 3; x++)
1699 content_copy[c].e[x][y] = content[c].e[x][y];
1702 else // constant size configuration data (1, 2 or 4 bytes)
1704 if (data_type == TYPE_BOOLEAN)
1705 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1707 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1712 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1716 xx_ei = *ei_from; // copy element data into temporary buffer
1717 yy_ei = *ei_to; // copy element data into temporary buffer
1719 copyConfigFromConfigList(chunk_config_CUSX_base);
1724 // ---------- reinitialize and copy change pages ----------
1726 ei_to->num_change_pages = ei_from->num_change_pages;
1727 ei_to->current_change_page = ei_from->current_change_page;
1729 setElementChangePages(ei_to, ei_to->num_change_pages);
1731 for (i = 0; i < ei_to->num_change_pages; i++)
1732 ei_to->change_page[i] = ei_from->change_page[i];
1734 // ---------- copy group element info ----------
1735 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1736 *ei_to->group = *ei_from->group;
1738 // mark this custom element as modified
1739 ei_to->modified_settings = TRUE;
1742 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1744 int change_page_size = sizeof(struct ElementChangeInfo);
1746 ei->num_change_pages = MAX(1, change_pages);
1749 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1751 if (ei->current_change_page >= ei->num_change_pages)
1752 ei->current_change_page = ei->num_change_pages - 1;
1754 ei->change = &ei->change_page[ei->current_change_page];
1757 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1759 xx_change = *change; // copy change data into temporary buffer
1761 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1763 *change = xx_change;
1765 resetEventFlags(change);
1767 change->direct_action = 0;
1768 change->other_action = 0;
1770 change->pre_change_function = NULL;
1771 change->change_function = NULL;
1772 change->post_change_function = NULL;
1775 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1779 li = *level; // copy level data into temporary buffer
1780 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1781 *level = li; // copy temporary buffer back to level data
1783 setLevelInfoToDefaults_BD();
1784 setLevelInfoToDefaults_EM();
1785 setLevelInfoToDefaults_SP();
1786 setLevelInfoToDefaults_MM();
1788 level->native_bd_level = &native_bd_level;
1789 level->native_em_level = &native_em_level;
1790 level->native_sp_level = &native_sp_level;
1791 level->native_mm_level = &native_mm_level;
1793 level->file_version = FILE_VERSION_ACTUAL;
1794 level->game_version = GAME_VERSION_ACTUAL;
1796 level->creation_date = getCurrentDate();
1798 level->encoding_16bit_field = TRUE;
1799 level->encoding_16bit_yamyam = TRUE;
1800 level->encoding_16bit_amoeba = TRUE;
1802 // clear level name and level author string buffers
1803 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1804 level->name[i] = '\0';
1805 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1806 level->author[i] = '\0';
1808 // set level name and level author to default values
1809 strcpy(level->name, NAMELESS_LEVEL_NAME);
1810 strcpy(level->author, ANONYMOUS_NAME);
1812 // set level playfield to playable default level with player and exit
1813 for (x = 0; x < MAX_LEV_FIELDX; x++)
1814 for (y = 0; y < MAX_LEV_FIELDY; y++)
1815 level->field[x][y] = EL_SAND;
1817 level->field[0][0] = EL_PLAYER_1;
1818 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1820 BorderElement = EL_STEELWALL;
1822 // detect custom elements when loading them
1823 level->file_has_custom_elements = FALSE;
1825 // set all bug compatibility flags to "false" => do not emulate this bug
1826 level->use_action_after_change_bug = FALSE;
1828 if (leveldir_current)
1830 // try to determine better author name than 'anonymous'
1831 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1833 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1834 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1838 switch (LEVELCLASS(leveldir_current))
1840 case LEVELCLASS_TUTORIAL:
1841 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1844 case LEVELCLASS_CONTRIB:
1845 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1846 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1849 case LEVELCLASS_PRIVATE:
1850 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1851 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1855 // keep default value
1862 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1864 static boolean clipboard_elements_initialized = FALSE;
1867 InitElementPropertiesStatic();
1869 li = *level; // copy level data into temporary buffer
1870 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1871 *level = li; // copy temporary buffer back to level data
1873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1876 struct ElementInfo *ei = &element_info[element];
1878 if (element == EL_MM_GRAY_BALL)
1880 struct LevelInfo_MM *level_mm = level->native_mm_level;
1883 for (j = 0; j < level->num_mm_ball_contents; j++)
1884 level->mm_ball_content[j] =
1885 map_element_MM_to_RND(level_mm->ball_content[j]);
1888 // never initialize clipboard elements after the very first time
1889 // (to be able to use clipboard elements between several levels)
1890 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1893 if (IS_ENVELOPE(element))
1895 int envelope_nr = element - EL_ENVELOPE_1;
1897 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1899 level->envelope[envelope_nr] = xx_envelope;
1902 if (IS_CUSTOM_ELEMENT(element) ||
1903 IS_GROUP_ELEMENT(element) ||
1904 IS_INTERNAL_ELEMENT(element))
1906 xx_ei = *ei; // copy element data into temporary buffer
1908 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1913 setElementChangePages(ei, 1);
1914 setElementChangeInfoToDefaults(ei->change);
1916 if (IS_CUSTOM_ELEMENT(element) ||
1917 IS_GROUP_ELEMENT(element))
1919 setElementDescriptionToDefault(ei);
1921 ei->modified_settings = FALSE;
1924 if (IS_CUSTOM_ELEMENT(element) ||
1925 IS_INTERNAL_ELEMENT(element))
1927 // internal values used in level editor
1929 ei->access_type = 0;
1930 ei->access_layer = 0;
1931 ei->access_protected = 0;
1932 ei->walk_to_action = 0;
1933 ei->smash_targets = 0;
1936 ei->can_explode_by_fire = FALSE;
1937 ei->can_explode_smashed = FALSE;
1938 ei->can_explode_impact = FALSE;
1940 ei->current_change_page = 0;
1943 if (IS_GROUP_ELEMENT(element) ||
1944 IS_INTERNAL_ELEMENT(element))
1946 struct ElementGroupInfo *group;
1948 // initialize memory for list of elements in group
1949 if (ei->group == NULL)
1950 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1954 xx_group = *group; // copy group data into temporary buffer
1956 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1961 if (IS_EMPTY_ELEMENT(element) ||
1962 IS_INTERNAL_ELEMENT(element))
1964 xx_ei = *ei; // copy element data into temporary buffer
1966 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1972 clipboard_elements_initialized = TRUE;
1975 static void setLevelInfoToDefaults(struct LevelInfo *level,
1976 boolean level_info_only,
1977 boolean reset_file_status)
1979 setLevelInfoToDefaults_Level(level);
1981 if (!level_info_only)
1982 setLevelInfoToDefaults_Elements(level);
1984 if (reset_file_status)
1986 level->no_valid_file = FALSE;
1987 level->no_level_file = FALSE;
1990 level->changed = FALSE;
1993 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1995 level_file_info->nr = 0;
1996 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1997 level_file_info->packed = FALSE;
1999 setString(&level_file_info->basename, NULL);
2000 setString(&level_file_info->filename, NULL);
2003 int getMappedElement_SB(int, boolean);
2005 static void ActivateLevelTemplate(void)
2009 if (check_special_flags("load_xsb_to_ces"))
2011 // fill smaller playfields with padding "beyond border wall" elements
2012 if (level.fieldx < level_template.fieldx ||
2013 level.fieldy < level_template.fieldy)
2015 short field[level.fieldx][level.fieldy];
2016 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2017 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2018 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2019 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2021 // copy old playfield (which is smaller than the visible area)
2022 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2023 field[x][y] = level.field[x][y];
2025 // fill new, larger playfield with "beyond border wall" elements
2026 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2027 level.field[x][y] = getMappedElement_SB('_', TRUE);
2029 // copy the old playfield to the middle of the new playfield
2030 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2031 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2033 level.fieldx = new_fieldx;
2034 level.fieldy = new_fieldy;
2038 // Currently there is no special action needed to activate the template
2039 // data, because 'element_info' property settings overwrite the original
2040 // level data, while all other variables do not change.
2042 // Exception: 'from_level_template' elements in the original level playfield
2043 // are overwritten with the corresponding elements at the same position in
2044 // playfield from the level template.
2046 for (x = 0; x < level.fieldx; x++)
2047 for (y = 0; y < level.fieldy; y++)
2048 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2049 level.field[x][y] = level_template.field[x][y];
2051 if (check_special_flags("load_xsb_to_ces"))
2053 struct LevelInfo level_backup = level;
2055 // overwrite all individual level settings from template level settings
2056 level = level_template;
2058 // restore level file info
2059 level.file_info = level_backup.file_info;
2061 // restore playfield size
2062 level.fieldx = level_backup.fieldx;
2063 level.fieldy = level_backup.fieldy;
2065 // restore playfield content
2066 for (x = 0; x < level.fieldx; x++)
2067 for (y = 0; y < level.fieldy; y++)
2068 level.field[x][y] = level_backup.field[x][y];
2070 // restore name and author from individual level
2071 strcpy(level.name, level_backup.name);
2072 strcpy(level.author, level_backup.author);
2074 // restore flag "use_custom_template"
2075 level.use_custom_template = level_backup.use_custom_template;
2079 static boolean checkForPackageFromBasename_BD(char *basename)
2081 // check for native BD level file extensions
2082 if (!strSuffixLower(basename, ".bd") &&
2083 !strSuffixLower(basename, ".bdr") &&
2084 !strSuffixLower(basename, ".brc") &&
2085 !strSuffixLower(basename, ".gds"))
2088 // check for standard single-level BD files (like "001.bd")
2089 if (strSuffixLower(basename, ".bd") &&
2090 strlen(basename) == 6 &&
2091 basename[0] >= '0' && basename[0] <= '9' &&
2092 basename[1] >= '0' && basename[1] <= '9' &&
2093 basename[2] >= '0' && basename[2] <= '9')
2096 // this is a level package in native BD file format
2100 static char *getLevelFilenameFromBasename(char *basename)
2102 static char *filename = NULL;
2104 checked_free(filename);
2106 filename = getPath2(getCurrentLevelDir(), basename);
2111 static int getFileTypeFromBasename(char *basename)
2113 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2115 static char *filename = NULL;
2116 struct stat file_status;
2118 // ---------- try to determine file type from filename ----------
2120 // check for typical filename of a Supaplex level package file
2121 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2122 return LEVEL_FILE_TYPE_SP;
2124 // check for typical filename of a Diamond Caves II level package file
2125 if (strSuffixLower(basename, ".dc") ||
2126 strSuffixLower(basename, ".dc2"))
2127 return LEVEL_FILE_TYPE_DC;
2129 // check for typical filename of a Sokoban level package file
2130 if (strSuffixLower(basename, ".xsb") &&
2131 strchr(basename, '%') == NULL)
2132 return LEVEL_FILE_TYPE_SB;
2134 // check for typical filename of a Boulder Dash (GDash) level package file
2135 if (checkForPackageFromBasename_BD(basename))
2136 return LEVEL_FILE_TYPE_BD;
2138 // ---------- try to determine file type from filesize ----------
2140 checked_free(filename);
2141 filename = getPath2(getCurrentLevelDir(), basename);
2143 if (stat(filename, &file_status) == 0)
2145 // check for typical filesize of a Supaplex level package file
2146 if (file_status.st_size == 170496)
2147 return LEVEL_FILE_TYPE_SP;
2150 return LEVEL_FILE_TYPE_UNKNOWN;
2153 static int getFileTypeFromMagicBytes(char *filename, int type)
2157 if ((file = openFile(filename, MODE_READ)))
2159 char chunk_name[CHUNK_ID_LEN + 1];
2161 getFileChunkBE(file, chunk_name, NULL);
2163 if (strEqual(chunk_name, "MMII") ||
2164 strEqual(chunk_name, "MIRR"))
2165 type = LEVEL_FILE_TYPE_MM;
2173 static boolean checkForPackageFromBasename(char *basename)
2175 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2176 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2178 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2181 static char *getSingleLevelBasenameExt(int nr, char *extension)
2183 static char basename[MAX_FILENAME_LEN];
2186 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2188 sprintf(basename, "%03d.%s", nr, extension);
2193 static char *getSingleLevelBasename(int nr)
2195 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2198 static char *getPackedLevelBasename(int type)
2200 static char basename[MAX_FILENAME_LEN];
2201 char *directory = getCurrentLevelDir();
2203 DirectoryEntry *dir_entry;
2205 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2207 if ((dir = openDirectory(directory)) == NULL)
2209 Warn("cannot read current level directory '%s'", directory);
2214 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2216 char *entry_basename = dir_entry->basename;
2217 int entry_type = getFileTypeFromBasename(entry_basename);
2219 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2221 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2224 strcpy(basename, entry_basename);
2231 closeDirectory(dir);
2236 static char *getSingleLevelFilename(int nr)
2238 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2241 #if ENABLE_UNUSED_CODE
2242 static char *getPackedLevelFilename(int type)
2244 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2248 char *getDefaultLevelFilename(int nr)
2250 return getSingleLevelFilename(nr);
2253 #if ENABLE_UNUSED_CODE
2254 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2258 lfi->packed = FALSE;
2260 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2261 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2265 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2266 int type, char *format, ...)
2268 static char basename[MAX_FILENAME_LEN];
2271 va_start(ap, format);
2272 vsprintf(basename, format, ap);
2276 lfi->packed = FALSE;
2278 setString(&lfi->basename, basename);
2279 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2282 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2288 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2289 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2292 static int getFiletypeFromID(char *filetype_id)
2294 char *filetype_id_lower;
2295 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2298 if (filetype_id == NULL)
2299 return LEVEL_FILE_TYPE_UNKNOWN;
2301 filetype_id_lower = getStringToLower(filetype_id);
2303 for (i = 0; filetype_id_list[i].id != NULL; i++)
2305 char *id_lower = getStringToLower(filetype_id_list[i].id);
2307 if (strEqual(filetype_id_lower, id_lower))
2308 filetype = filetype_id_list[i].filetype;
2312 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2316 free(filetype_id_lower);
2321 char *getLocalLevelTemplateFilename(void)
2323 return getDefaultLevelFilename(-1);
2326 char *getGlobalLevelTemplateFilename(void)
2328 // global variable "leveldir_current" must be modified in the loop below
2329 LevelDirTree *leveldir_current_last = leveldir_current;
2330 char *filename = NULL;
2332 // check for template level in path from current to topmost tree node
2334 while (leveldir_current != NULL)
2336 filename = getDefaultLevelFilename(-1);
2338 if (fileExists(filename))
2341 leveldir_current = leveldir_current->node_parent;
2344 // restore global variable "leveldir_current" modified in above loop
2345 leveldir_current = leveldir_current_last;
2350 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2354 // special case: level number is negative => check for level template file
2357 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2358 getSingleLevelBasename(-1));
2360 // replace local level template filename with global template filename
2361 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2363 // no fallback if template file not existing
2367 // special case: check for file name/pattern specified in "levelinfo.conf"
2368 if (leveldir_current->level_filename != NULL)
2370 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2372 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2373 leveldir_current->level_filename, nr);
2375 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2377 if (fileExists(lfi->filename))
2380 else if (leveldir_current->level_filetype != NULL)
2382 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2384 // check for specified native level file with standard file name
2385 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2386 "%03d.%s", nr, LEVELFILE_EXTENSION);
2387 if (fileExists(lfi->filename))
2391 // check for native Rocks'n'Diamonds level file
2392 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2393 "%03d.%s", nr, LEVELFILE_EXTENSION);
2394 if (fileExists(lfi->filename))
2397 // check for native Boulder Dash level file
2398 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2399 if (fileExists(lfi->filename))
2402 // check for Emerald Mine level file (V1)
2403 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2404 'a' + (nr / 10) % 26, '0' + nr % 10);
2405 if (fileExists(lfi->filename))
2407 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2408 'A' + (nr / 10) % 26, '0' + nr % 10);
2409 if (fileExists(lfi->filename))
2412 // check for Emerald Mine level file (V2 to V5)
2413 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2414 if (fileExists(lfi->filename))
2417 // check for Emerald Mine level file (V6 / single mode)
2418 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2419 if (fileExists(lfi->filename))
2421 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2422 if (fileExists(lfi->filename))
2425 // check for Emerald Mine level file (V6 / teamwork mode)
2426 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2427 if (fileExists(lfi->filename))
2429 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2430 if (fileExists(lfi->filename))
2433 // check for various packed level file formats
2434 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2435 if (fileExists(lfi->filename))
2438 // no known level file found -- use default values (and fail later)
2439 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2440 "%03d.%s", nr, LEVELFILE_EXTENSION);
2443 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2445 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2446 lfi->type = getFileTypeFromBasename(lfi->basename);
2448 if (lfi->type == LEVEL_FILE_TYPE_RND)
2449 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2452 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2454 // always start with reliable default values
2455 setFileInfoToDefaults(level_file_info);
2457 level_file_info->nr = nr; // set requested level number
2459 determineLevelFileInfo_Filename(level_file_info);
2460 determineLevelFileInfo_Filetype(level_file_info);
2463 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2464 struct LevelFileInfo *lfi_to)
2466 lfi_to->nr = lfi_from->nr;
2467 lfi_to->type = lfi_from->type;
2468 lfi_to->packed = lfi_from->packed;
2470 setString(&lfi_to->basename, lfi_from->basename);
2471 setString(&lfi_to->filename, lfi_from->filename);
2474 // ----------------------------------------------------------------------------
2475 // functions for loading R'n'D level
2476 // ----------------------------------------------------------------------------
2478 int getMappedElement(int element)
2480 // remap some (historic, now obsolete) elements
2484 case EL_PLAYER_OBSOLETE:
2485 element = EL_PLAYER_1;
2488 case EL_KEY_OBSOLETE:
2492 case EL_EM_KEY_1_FILE_OBSOLETE:
2493 element = EL_EM_KEY_1;
2496 case EL_EM_KEY_2_FILE_OBSOLETE:
2497 element = EL_EM_KEY_2;
2500 case EL_EM_KEY_3_FILE_OBSOLETE:
2501 element = EL_EM_KEY_3;
2504 case EL_EM_KEY_4_FILE_OBSOLETE:
2505 element = EL_EM_KEY_4;
2508 case EL_ENVELOPE_OBSOLETE:
2509 element = EL_ENVELOPE_1;
2517 if (element >= NUM_FILE_ELEMENTS)
2519 Warn("invalid level element %d", element);
2521 element = EL_UNKNOWN;
2529 static int getMappedElementByVersion(int element, int game_version)
2531 // remap some elements due to certain game version
2533 if (game_version <= VERSION_IDENT(2,2,0,0))
2535 // map game font elements
2536 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2537 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2538 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2539 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2542 if (game_version < VERSION_IDENT(3,0,0,0))
2544 // map Supaplex gravity tube elements
2545 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2546 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2547 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2548 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2555 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2557 level->file_version = getFileVersion(file);
2558 level->game_version = getFileVersion(file);
2563 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2565 level->creation_date.year = getFile16BitBE(file);
2566 level->creation_date.month = getFile8Bit(file);
2567 level->creation_date.day = getFile8Bit(file);
2569 level->creation_date.src = DATE_SRC_LEVELFILE;
2574 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2576 int initial_player_stepsize;
2577 int initial_player_gravity;
2580 level->fieldx = getFile8Bit(file);
2581 level->fieldy = getFile8Bit(file);
2583 level->time = getFile16BitBE(file);
2584 level->gems_needed = getFile16BitBE(file);
2586 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2587 level->name[i] = getFile8Bit(file);
2588 level->name[MAX_LEVEL_NAME_LEN] = 0;
2590 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2591 level->score[i] = getFile8Bit(file);
2593 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2594 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2595 for (y = 0; y < 3; y++)
2596 for (x = 0; x < 3; x++)
2597 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2599 level->amoeba_speed = getFile8Bit(file);
2600 level->time_magic_wall = getFile8Bit(file);
2601 level->time_wheel = getFile8Bit(file);
2602 level->amoeba_content = getMappedElement(getFile8Bit(file));
2604 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2607 for (i = 0; i < MAX_PLAYERS; i++)
2608 level->initial_player_stepsize[i] = initial_player_stepsize;
2610 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2612 for (i = 0; i < MAX_PLAYERS; i++)
2613 level->initial_player_gravity[i] = initial_player_gravity;
2615 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2616 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2618 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2620 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2621 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2622 level->can_move_into_acid_bits = getFile32BitBE(file);
2623 level->dont_collide_with_bits = getFile8Bit(file);
2625 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2626 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2628 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2629 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2630 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2632 level->game_engine_type = getFile8Bit(file);
2634 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2639 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2643 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2644 level->name[i] = getFile8Bit(file);
2645 level->name[MAX_LEVEL_NAME_LEN] = 0;
2650 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2654 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2655 level->author[i] = getFile8Bit(file);
2656 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2661 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2664 int chunk_size_expected = level->fieldx * level->fieldy;
2666 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2667 stored with 16-bit encoding (and should be twice as big then).
2668 Even worse, playfield data was stored 16-bit when only yamyam content
2669 contained 16-bit elements and vice versa. */
2671 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2672 chunk_size_expected *= 2;
2674 if (chunk_size_expected != chunk_size)
2676 ReadUnusedBytesFromFile(file, chunk_size);
2677 return chunk_size_expected;
2680 for (y = 0; y < level->fieldy; y++)
2681 for (x = 0; x < level->fieldx; x++)
2682 level->field[x][y] =
2683 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2688 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2691 int header_size = 4;
2692 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2693 int chunk_size_expected = header_size + content_size;
2695 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2696 stored with 16-bit encoding (and should be twice as big then).
2697 Even worse, playfield data was stored 16-bit when only yamyam content
2698 contained 16-bit elements and vice versa. */
2700 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2701 chunk_size_expected += content_size;
2703 if (chunk_size_expected != chunk_size)
2705 ReadUnusedBytesFromFile(file, chunk_size);
2706 return chunk_size_expected;
2710 level->num_yamyam_contents = getFile8Bit(file);
2714 // correct invalid number of content fields -- should never happen
2715 if (level->num_yamyam_contents < 1 ||
2716 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2717 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2719 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2720 for (y = 0; y < 3; y++)
2721 for (x = 0; x < 3; x++)
2722 level->yamyam_content[i].e[x][y] =
2723 getMappedElement(level->encoding_16bit_field ?
2724 getFile16BitBE(file) : getFile8Bit(file));
2728 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2733 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2735 element = getMappedElement(getFile16BitBE(file));
2736 num_contents = getFile8Bit(file);
2738 getFile8Bit(file); // content x size (unused)
2739 getFile8Bit(file); // content y size (unused)
2741 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2743 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2744 for (y = 0; y < 3; y++)
2745 for (x = 0; x < 3; x++)
2746 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2748 // correct invalid number of content fields -- should never happen
2749 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2750 num_contents = STD_ELEMENT_CONTENTS;
2752 if (element == EL_YAMYAM)
2754 level->num_yamyam_contents = num_contents;
2756 for (i = 0; i < num_contents; i++)
2757 for (y = 0; y < 3; y++)
2758 for (x = 0; x < 3; x++)
2759 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2761 else if (element == EL_BD_AMOEBA)
2763 level->amoeba_content = content_array[0][0][0];
2767 Warn("cannot load content for element '%d'", element);
2773 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2779 int chunk_size_expected;
2781 element = getMappedElement(getFile16BitBE(file));
2782 if (!IS_ENVELOPE(element))
2783 element = EL_ENVELOPE_1;
2785 envelope_nr = element - EL_ENVELOPE_1;
2787 envelope_len = getFile16BitBE(file);
2789 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2790 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2792 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2794 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2795 if (chunk_size_expected != chunk_size)
2797 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2798 return chunk_size_expected;
2801 for (i = 0; i < envelope_len; i++)
2802 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2807 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2809 int num_changed_custom_elements = getFile16BitBE(file);
2810 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2813 if (chunk_size_expected != chunk_size)
2815 ReadUnusedBytesFromFile(file, chunk_size - 2);
2816 return chunk_size_expected;
2819 for (i = 0; i < num_changed_custom_elements; i++)
2821 int element = getMappedElement(getFile16BitBE(file));
2822 int properties = getFile32BitBE(file);
2824 if (IS_CUSTOM_ELEMENT(element))
2825 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2827 Warn("invalid custom element number %d", element);
2829 // older game versions that wrote level files with CUS1 chunks used
2830 // different default push delay values (not yet stored in level file)
2831 element_info[element].push_delay_fixed = 2;
2832 element_info[element].push_delay_random = 8;
2835 level->file_has_custom_elements = TRUE;
2840 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2842 int num_changed_custom_elements = getFile16BitBE(file);
2843 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2846 if (chunk_size_expected != chunk_size)
2848 ReadUnusedBytesFromFile(file, chunk_size - 2);
2849 return chunk_size_expected;
2852 for (i = 0; i < num_changed_custom_elements; i++)
2854 int element = getMappedElement(getFile16BitBE(file));
2855 int custom_target_element = getMappedElement(getFile16BitBE(file));
2857 if (IS_CUSTOM_ELEMENT(element))
2858 element_info[element].change->target_element = custom_target_element;
2860 Warn("invalid custom element number %d", element);
2863 level->file_has_custom_elements = TRUE;
2868 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2870 int num_changed_custom_elements = getFile16BitBE(file);
2871 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2874 if (chunk_size_expected != chunk_size)
2876 ReadUnusedBytesFromFile(file, chunk_size - 2);
2877 return chunk_size_expected;
2880 for (i = 0; i < num_changed_custom_elements; i++)
2882 int element = getMappedElement(getFile16BitBE(file));
2883 struct ElementInfo *ei = &element_info[element];
2884 unsigned int event_bits;
2886 if (!IS_CUSTOM_ELEMENT(element))
2888 Warn("invalid custom element number %d", element);
2890 element = EL_INTERNAL_DUMMY;
2893 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2894 ei->description[j] = getFile8Bit(file);
2895 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2897 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2899 // some free bytes for future properties and padding
2900 ReadUnusedBytesFromFile(file, 7);
2902 ei->use_gfx_element = getFile8Bit(file);
2903 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2905 ei->collect_score_initial = getFile8Bit(file);
2906 ei->collect_count_initial = getFile8Bit(file);
2908 ei->push_delay_fixed = getFile16BitBE(file);
2909 ei->push_delay_random = getFile16BitBE(file);
2910 ei->move_delay_fixed = getFile16BitBE(file);
2911 ei->move_delay_random = getFile16BitBE(file);
2913 ei->move_pattern = getFile16BitBE(file);
2914 ei->move_direction_initial = getFile8Bit(file);
2915 ei->move_stepsize = getFile8Bit(file);
2917 for (y = 0; y < 3; y++)
2918 for (x = 0; x < 3; x++)
2919 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2921 // bits 0 - 31 of "has_event[]"
2922 event_bits = getFile32BitBE(file);
2923 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2924 if (event_bits & (1u << j))
2925 ei->change->has_event[j] = TRUE;
2927 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2929 ei->change->delay_fixed = getFile16BitBE(file);
2930 ei->change->delay_random = getFile16BitBE(file);
2931 ei->change->delay_frames = getFile16BitBE(file);
2933 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2935 ei->change->explode = getFile8Bit(file);
2936 ei->change->use_target_content = getFile8Bit(file);
2937 ei->change->only_if_complete = getFile8Bit(file);
2938 ei->change->use_random_replace = getFile8Bit(file);
2940 ei->change->random_percentage = getFile8Bit(file);
2941 ei->change->replace_when = getFile8Bit(file);
2943 for (y = 0; y < 3; y++)
2944 for (x = 0; x < 3; x++)
2945 ei->change->target_content.e[x][y] =
2946 getMappedElement(getFile16BitBE(file));
2948 ei->slippery_type = getFile8Bit(file);
2950 // some free bytes for future properties and padding
2951 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2953 // mark that this custom element has been modified
2954 ei->modified_settings = TRUE;
2957 level->file_has_custom_elements = TRUE;
2962 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2964 struct ElementInfo *ei;
2965 int chunk_size_expected;
2969 // ---------- custom element base property values (96 bytes) ----------------
2971 element = getMappedElement(getFile16BitBE(file));
2973 if (!IS_CUSTOM_ELEMENT(element))
2975 Warn("invalid custom element number %d", element);
2977 ReadUnusedBytesFromFile(file, chunk_size - 2);
2982 ei = &element_info[element];
2984 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2985 ei->description[i] = getFile8Bit(file);
2986 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2988 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2990 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2992 ei->num_change_pages = getFile8Bit(file);
2994 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2995 if (chunk_size_expected != chunk_size)
2997 ReadUnusedBytesFromFile(file, chunk_size - 43);
2998 return chunk_size_expected;
3001 ei->ce_value_fixed_initial = getFile16BitBE(file);
3002 ei->ce_value_random_initial = getFile16BitBE(file);
3003 ei->use_last_ce_value = getFile8Bit(file);
3005 ei->use_gfx_element = getFile8Bit(file);
3006 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3008 ei->collect_score_initial = getFile8Bit(file);
3009 ei->collect_count_initial = getFile8Bit(file);
3011 ei->drop_delay_fixed = getFile8Bit(file);
3012 ei->push_delay_fixed = getFile8Bit(file);
3013 ei->drop_delay_random = getFile8Bit(file);
3014 ei->push_delay_random = getFile8Bit(file);
3015 ei->move_delay_fixed = getFile16BitBE(file);
3016 ei->move_delay_random = getFile16BitBE(file);
3018 // bits 0 - 15 of "move_pattern" ...
3019 ei->move_pattern = getFile16BitBE(file);
3020 ei->move_direction_initial = getFile8Bit(file);
3021 ei->move_stepsize = getFile8Bit(file);
3023 ei->slippery_type = getFile8Bit(file);
3025 for (y = 0; y < 3; y++)
3026 for (x = 0; x < 3; x++)
3027 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3029 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3030 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3031 ei->move_leave_type = getFile8Bit(file);
3033 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3034 ei->move_pattern |= (getFile16BitBE(file) << 16);
3036 ei->access_direction = getFile8Bit(file);
3038 ei->explosion_delay = getFile8Bit(file);
3039 ei->ignition_delay = getFile8Bit(file);
3040 ei->explosion_type = getFile8Bit(file);
3042 // some free bytes for future custom property values and padding
3043 ReadUnusedBytesFromFile(file, 1);
3045 // ---------- change page property values (48 bytes) ------------------------
3047 setElementChangePages(ei, ei->num_change_pages);
3049 for (i = 0; i < ei->num_change_pages; i++)
3051 struct ElementChangeInfo *change = &ei->change_page[i];
3052 unsigned int event_bits;
3054 // always start with reliable default values
3055 setElementChangeInfoToDefaults(change);
3057 // bits 0 - 31 of "has_event[]" ...
3058 event_bits = getFile32BitBE(file);
3059 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3060 if (event_bits & (1u << j))
3061 change->has_event[j] = TRUE;
3063 change->target_element = getMappedElement(getFile16BitBE(file));
3065 change->delay_fixed = getFile16BitBE(file);
3066 change->delay_random = getFile16BitBE(file);
3067 change->delay_frames = getFile16BitBE(file);
3069 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3071 change->explode = getFile8Bit(file);
3072 change->use_target_content = getFile8Bit(file);
3073 change->only_if_complete = getFile8Bit(file);
3074 change->use_random_replace = getFile8Bit(file);
3076 change->random_percentage = getFile8Bit(file);
3077 change->replace_when = getFile8Bit(file);
3079 for (y = 0; y < 3; y++)
3080 for (x = 0; x < 3; x++)
3081 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3083 change->can_change = getFile8Bit(file);
3085 change->trigger_side = getFile8Bit(file);
3087 change->trigger_player = getFile8Bit(file);
3088 change->trigger_page = getFile8Bit(file);
3090 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3091 CH_PAGE_ANY : (1 << change->trigger_page));
3093 change->has_action = getFile8Bit(file);
3094 change->action_type = getFile8Bit(file);
3095 change->action_mode = getFile8Bit(file);
3096 change->action_arg = getFile16BitBE(file);
3098 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3099 event_bits = getFile8Bit(file);
3100 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3101 if (event_bits & (1u << (j - 32)))
3102 change->has_event[j] = TRUE;
3105 // mark this custom element as modified
3106 ei->modified_settings = TRUE;
3108 level->file_has_custom_elements = TRUE;
3113 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3115 struct ElementInfo *ei;
3116 struct ElementGroupInfo *group;
3120 element = getMappedElement(getFile16BitBE(file));
3122 if (!IS_GROUP_ELEMENT(element))
3124 Warn("invalid group element number %d", element);
3126 ReadUnusedBytesFromFile(file, chunk_size - 2);
3131 ei = &element_info[element];
3133 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3134 ei->description[i] = getFile8Bit(file);
3135 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3137 group = element_info[element].group;
3139 group->num_elements = getFile8Bit(file);
3141 ei->use_gfx_element = getFile8Bit(file);
3142 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3144 group->choice_mode = getFile8Bit(file);
3146 // some free bytes for future values and padding
3147 ReadUnusedBytesFromFile(file, 3);
3149 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3150 group->element[i] = getMappedElement(getFile16BitBE(file));
3152 // mark this group element as modified
3153 element_info[element].modified_settings = TRUE;
3155 level->file_has_custom_elements = TRUE;
3160 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3161 int element, int real_element)
3163 int micro_chunk_size = 0;
3164 int conf_type = getFile8Bit(file);
3165 int byte_mask = conf_type & CONF_MASK_BYTES;
3166 boolean element_found = FALSE;
3169 micro_chunk_size += 1;
3171 if (byte_mask == CONF_MASK_MULTI_BYTES)
3173 int num_bytes = getFile16BitBE(file);
3174 byte *buffer = checked_malloc(num_bytes);
3176 ReadBytesFromFile(file, buffer, num_bytes);
3178 for (i = 0; conf[i].data_type != -1; i++)
3180 if (conf[i].element == element &&
3181 conf[i].conf_type == conf_type)
3183 int data_type = conf[i].data_type;
3184 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3185 int max_num_entities = conf[i].max_num_entities;
3187 if (num_entities > max_num_entities)
3189 Warn("truncating number of entities for element %d from %d to %d",
3190 element, num_entities, max_num_entities);
3192 num_entities = max_num_entities;
3195 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3196 data_type == TYPE_CONTENT_LIST))
3198 // for element and content lists, zero entities are not allowed
3199 Warn("found empty list of entities for element %d", element);
3201 // do not set "num_entities" here to prevent reading behind buffer
3203 *(int *)(conf[i].num_entities) = 1; // at least one is required
3207 *(int *)(conf[i].num_entities) = num_entities;
3210 element_found = TRUE;
3212 if (data_type == TYPE_STRING)
3214 char *string = (char *)(conf[i].value);
3217 for (j = 0; j < max_num_entities; j++)
3218 string[j] = (j < num_entities ? buffer[j] : '\0');
3220 else if (data_type == TYPE_ELEMENT_LIST)
3222 int *element_array = (int *)(conf[i].value);
3225 for (j = 0; j < num_entities; j++)
3227 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3229 else if (data_type == TYPE_CONTENT_LIST)
3231 struct Content *content= (struct Content *)(conf[i].value);
3234 for (c = 0; c < num_entities; c++)
3235 for (y = 0; y < 3; y++)
3236 for (x = 0; x < 3; x++)
3237 content[c].e[x][y] =
3238 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3241 element_found = FALSE;
3247 checked_free(buffer);
3249 micro_chunk_size += 2 + num_bytes;
3251 else // constant size configuration data (1, 2 or 4 bytes)
3253 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3254 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3255 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3257 for (i = 0; conf[i].data_type != -1; i++)
3259 if (conf[i].element == element &&
3260 conf[i].conf_type == conf_type)
3262 int data_type = conf[i].data_type;
3264 if (data_type == TYPE_ELEMENT)
3265 value = getMappedElement(value);
3267 if (data_type == TYPE_BOOLEAN)
3268 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3270 *(int *) (conf[i].value) = value;
3272 element_found = TRUE;
3278 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3283 char *error_conf_chunk_bytes =
3284 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3285 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3286 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3287 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3288 int error_element = real_element;
3290 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3291 error_conf_chunk_bytes, error_conf_chunk_token,
3292 error_element, EL_NAME(error_element));
3295 return micro_chunk_size;
3298 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3300 int real_chunk_size = 0;
3302 li = *level; // copy level data into temporary buffer
3304 while (!checkEndOfFile(file))
3306 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3308 if (real_chunk_size >= chunk_size)
3312 *level = li; // copy temporary buffer back to level data
3314 return real_chunk_size;
3317 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3319 int real_chunk_size = 0;
3321 li = *level; // copy level data into temporary buffer
3323 while (!checkEndOfFile(file))
3325 int element = getMappedElement(getFile16BitBE(file));
3327 real_chunk_size += 2;
3328 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3330 if (real_chunk_size >= chunk_size)
3334 *level = li; // copy temporary buffer back to level data
3336 return real_chunk_size;
3339 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3341 int real_chunk_size = 0;
3343 li = *level; // copy level data into temporary buffer
3345 while (!checkEndOfFile(file))
3347 int element = getMappedElement(getFile16BitBE(file));
3349 real_chunk_size += 2;
3350 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3352 if (real_chunk_size >= chunk_size)
3356 *level = li; // copy temporary buffer back to level data
3358 return real_chunk_size;
3361 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3363 int element = getMappedElement(getFile16BitBE(file));
3364 int envelope_nr = element - EL_ENVELOPE_1;
3365 int real_chunk_size = 2;
3367 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3369 while (!checkEndOfFile(file))
3371 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3374 if (real_chunk_size >= chunk_size)
3378 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3380 return real_chunk_size;
3383 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3385 int element = getMappedElement(getFile16BitBE(file));
3386 int real_chunk_size = 2;
3387 struct ElementInfo *ei = &element_info[element];
3390 xx_ei = *ei; // copy element data into temporary buffer
3392 xx_ei.num_change_pages = -1;
3394 while (!checkEndOfFile(file))
3396 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3398 if (xx_ei.num_change_pages != -1)
3401 if (real_chunk_size >= chunk_size)
3407 if (ei->num_change_pages == -1)
3409 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3412 ei->num_change_pages = 1;
3414 setElementChangePages(ei, 1);
3415 setElementChangeInfoToDefaults(ei->change);
3417 return real_chunk_size;
3420 // initialize number of change pages stored for this custom element
3421 setElementChangePages(ei, ei->num_change_pages);
3422 for (i = 0; i < ei->num_change_pages; i++)
3423 setElementChangeInfoToDefaults(&ei->change_page[i]);
3425 // start with reading properties for the first change page
3426 xx_current_change_page = 0;
3428 while (!checkEndOfFile(file))
3430 // level file might contain invalid change page number
3431 if (xx_current_change_page >= ei->num_change_pages)
3434 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3436 xx_change = *change; // copy change data into temporary buffer
3438 resetEventBits(); // reset bits; change page might have changed
3440 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3443 *change = xx_change;
3445 setEventFlagsFromEventBits(change);
3447 if (real_chunk_size >= chunk_size)
3451 level->file_has_custom_elements = TRUE;
3453 return real_chunk_size;
3456 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3458 int element = getMappedElement(getFile16BitBE(file));
3459 int real_chunk_size = 2;
3460 struct ElementInfo *ei = &element_info[element];
3461 struct ElementGroupInfo *group = ei->group;
3466 xx_ei = *ei; // copy element data into temporary buffer
3467 xx_group = *group; // copy group data into temporary buffer
3469 while (!checkEndOfFile(file))
3471 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3474 if (real_chunk_size >= chunk_size)
3481 level->file_has_custom_elements = TRUE;
3483 return real_chunk_size;
3486 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3488 int element = getMappedElement(getFile16BitBE(file));
3489 int real_chunk_size = 2;
3490 struct ElementInfo *ei = &element_info[element];
3492 xx_ei = *ei; // copy element data into temporary buffer
3494 while (!checkEndOfFile(file))
3496 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3499 if (real_chunk_size >= chunk_size)
3505 level->file_has_custom_elements = TRUE;
3507 return real_chunk_size;
3510 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3511 struct LevelFileInfo *level_file_info,
3512 boolean level_info_only)
3514 char *filename = level_file_info->filename;
3515 char cookie[MAX_LINE_LEN];
3516 char chunk_name[CHUNK_ID_LEN + 1];
3520 if (!(file = openFile(filename, MODE_READ)))
3522 level->no_valid_file = TRUE;
3523 level->no_level_file = TRUE;
3525 if (level_info_only)
3528 Warn("cannot read level '%s' -- using empty level", filename);
3530 if (!setup.editor.use_template_for_new_levels)
3533 // if level file not found, try to initialize level data from template
3534 filename = getGlobalLevelTemplateFilename();
3536 if (!(file = openFile(filename, MODE_READ)))
3539 // default: for empty levels, use level template for custom elements
3540 level->use_custom_template = TRUE;
3542 level->no_valid_file = FALSE;
3545 getFileChunkBE(file, chunk_name, NULL);
3546 if (strEqual(chunk_name, "RND1"))
3548 getFile32BitBE(file); // not used
3550 getFileChunkBE(file, chunk_name, NULL);
3551 if (!strEqual(chunk_name, "CAVE"))
3553 level->no_valid_file = TRUE;
3555 Warn("unknown format of level file '%s'", filename);
3562 else // check for pre-2.0 file format with cookie string
3564 strcpy(cookie, chunk_name);
3565 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3567 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3568 cookie[strlen(cookie) - 1] = '\0';
3570 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3572 level->no_valid_file = TRUE;
3574 Warn("unknown format of level file '%s'", filename);
3581 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3583 level->no_valid_file = TRUE;
3585 Warn("unsupported version of level file '%s'", filename);
3592 // pre-2.0 level files have no game version, so use file version here
3593 level->game_version = level->file_version;
3596 if (level->file_version < FILE_VERSION_1_2)
3598 // level files from versions before 1.2.0 without chunk structure
3599 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3600 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3608 int (*loader)(File *, int, struct LevelInfo *);
3612 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3613 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3614 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3615 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3616 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3617 { "INFO", -1, LoadLevel_INFO },
3618 { "BODY", -1, LoadLevel_BODY },
3619 { "CONT", -1, LoadLevel_CONT },
3620 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3621 { "CNT3", -1, LoadLevel_CNT3 },
3622 { "CUS1", -1, LoadLevel_CUS1 },
3623 { "CUS2", -1, LoadLevel_CUS2 },
3624 { "CUS3", -1, LoadLevel_CUS3 },
3625 { "CUS4", -1, LoadLevel_CUS4 },
3626 { "GRP1", -1, LoadLevel_GRP1 },
3627 { "CONF", -1, LoadLevel_CONF },
3628 { "ELEM", -1, LoadLevel_ELEM },
3629 { "NOTE", -1, LoadLevel_NOTE },
3630 { "CUSX", -1, LoadLevel_CUSX },
3631 { "GRPX", -1, LoadLevel_GRPX },
3632 { "EMPX", -1, LoadLevel_EMPX },
3637 while (getFileChunkBE(file, chunk_name, &chunk_size))
3641 while (chunk_info[i].name != NULL &&
3642 !strEqual(chunk_name, chunk_info[i].name))
3645 if (chunk_info[i].name == NULL)
3647 Warn("unknown chunk '%s' in level file '%s'",
3648 chunk_name, filename);
3650 ReadUnusedBytesFromFile(file, chunk_size);
3652 else if (chunk_info[i].size != -1 &&
3653 chunk_info[i].size != chunk_size)
3655 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3656 chunk_size, chunk_name, filename);
3658 ReadUnusedBytesFromFile(file, chunk_size);
3662 // call function to load this level chunk
3663 int chunk_size_expected =
3664 (chunk_info[i].loader)(file, chunk_size, level);
3666 if (chunk_size_expected < 0)
3668 Warn("error reading chunk '%s' in level file '%s'",
3669 chunk_name, filename);
3674 // the size of some chunks cannot be checked before reading other
3675 // chunks first (like "HEAD" and "BODY") that contain some header
3676 // information, so check them here
3677 if (chunk_size_expected != chunk_size)
3679 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3680 chunk_size, chunk_name, filename);
3692 // ----------------------------------------------------------------------------
3693 // functions for loading BD level
3694 // ----------------------------------------------------------------------------
3696 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3698 struct LevelInfo_BD *level_bd = level->native_bd_level;
3699 GdCave *cave = NULL; // will be changed below
3700 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3701 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3704 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3706 // cave and map newly allocated when set to defaults above
3707 cave = level_bd->cave;
3709 for (i = 0; i < 5; i++)
3711 cave->level_time[i] = level->time;
3712 cave->level_diamonds[i] = level->gems_needed;
3713 cave->level_magic_wall_time[i] = level->time_magic_wall;
3714 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3717 cave->diamond_value = level->score[SC_DIAMOND];
3718 cave->extra_diamond_value = level->score[SC_DIAMOND];
3720 cave->level_speed[0] = 160; // set cave speed
3722 cave->diagonal_movements = level->bd_diagonal_movements;
3724 strncpy(cave->name, level->name, sizeof(GdString));
3725 cave->name[sizeof(GdString) - 1] = '\0';
3727 for (x = 0; x < cave->w; x++)
3728 for (y = 0; y < cave->h; y++)
3729 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3732 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3734 struct LevelInfo_BD *level_bd = level->native_bd_level;
3735 GdCave *cave = level_bd->cave;
3736 int bd_level_nr = level_bd->level_nr;
3739 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3740 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3742 level->time = cave->level_time[bd_level_nr];
3743 level->gems_needed = cave->level_diamonds[bd_level_nr];
3744 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3746 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3747 level->score[SC_DIAMOND] = cave->diamond_value;
3749 level->bd_diagonal_movements = cave->diagonal_movements;
3751 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3752 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3754 for (x = 0; x < level->fieldx; x++)
3755 for (y = 0; y < level->fieldy; y++)
3756 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3759 static void setTapeInfoToDefaults(void);
3761 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3763 struct LevelInfo_BD *level_bd = level->native_bd_level;
3764 GdCave *cave = level_bd->cave;
3765 GdReplay *replay = level_bd->replay;
3771 // always start with reliable default values
3772 setTapeInfoToDefaults();
3774 tape.level_nr = level_nr; // (currently not used)
3775 tape.random_seed = replay->seed;
3777 TapeSetDateFromIsoDateString(replay->date);
3780 tape.pos[tape.counter].delay = 0;
3782 tape.bd_replay = TRUE;
3784 // all time calculations only used to display approximate tape time
3785 int cave_speed = cave->speed;
3786 int milliseconds_game = 0;
3787 int milliseconds_elapsed = 20;
3789 for (i = 0; i < replay->movements->len; i++)
3791 int replay_action = replay->movements->data[i];
3792 int tape_action = map_action_BD_to_RND(replay_action);
3793 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3794 boolean success = 0;
3798 success = TapeAddAction(action);
3800 milliseconds_game += milliseconds_elapsed;
3802 if (milliseconds_game >= cave_speed)
3804 milliseconds_game -= cave_speed;
3811 tape.pos[tape.counter].delay = 0;
3812 tape.pos[tape.counter].action[0] = 0;
3816 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3822 TapeHaltRecording();
3826 // ----------------------------------------------------------------------------
3827 // functions for loading EM level
3828 // ----------------------------------------------------------------------------
3830 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3832 static int ball_xy[8][2] =
3843 struct LevelInfo_EM *level_em = level->native_em_level;
3844 struct CAVE *cav = level_em->cav;
3847 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3848 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3850 cav->time_seconds = level->time;
3851 cav->gems_needed = level->gems_needed;
3853 cav->emerald_score = level->score[SC_EMERALD];
3854 cav->diamond_score = level->score[SC_DIAMOND];
3855 cav->alien_score = level->score[SC_ROBOT];
3856 cav->tank_score = level->score[SC_SPACESHIP];
3857 cav->bug_score = level->score[SC_BUG];
3858 cav->eater_score = level->score[SC_YAMYAM];
3859 cav->nut_score = level->score[SC_NUT];
3860 cav->dynamite_score = level->score[SC_DYNAMITE];
3861 cav->key_score = level->score[SC_KEY];
3862 cav->exit_score = level->score[SC_TIME_BONUS];
3864 cav->num_eater_arrays = level->num_yamyam_contents;
3866 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3867 for (y = 0; y < 3; y++)
3868 for (x = 0; x < 3; x++)
3869 cav->eater_array[i][y * 3 + x] =
3870 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3872 cav->amoeba_time = level->amoeba_speed;
3873 cav->wonderwall_time = level->time_magic_wall;
3874 cav->wheel_time = level->time_wheel;
3876 cav->android_move_time = level->android_move_time;
3877 cav->android_clone_time = level->android_clone_time;
3878 cav->ball_random = level->ball_random;
3879 cav->ball_active = level->ball_active_initial;
3880 cav->ball_time = level->ball_time;
3881 cav->num_ball_arrays = level->num_ball_contents;
3883 cav->lenses_score = level->lenses_score;
3884 cav->magnify_score = level->magnify_score;
3885 cav->slurp_score = level->slurp_score;
3887 cav->lenses_time = level->lenses_time;
3888 cav->magnify_time = level->magnify_time;
3890 cav->wind_time = 9999;
3891 cav->wind_direction =
3892 map_direction_RND_to_EM(level->wind_direction_initial);
3894 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3895 for (j = 0; j < 8; j++)
3896 cav->ball_array[i][j] =
3897 map_element_RND_to_EM_cave(level->ball_content[i].
3898 e[ball_xy[j][0]][ball_xy[j][1]]);
3900 map_android_clone_elements_RND_to_EM(level);
3902 // first fill the complete playfield with the empty space element
3903 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3904 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3905 cav->cave[x][y] = Cblank;
3907 // then copy the real level contents from level file into the playfield
3908 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3910 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3912 if (level->field[x][y] == EL_AMOEBA_DEAD)
3913 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3915 cav->cave[x][y] = new_element;
3918 for (i = 0; i < MAX_PLAYERS; i++)
3920 cav->player_x[i] = -1;
3921 cav->player_y[i] = -1;
3924 // initialize player positions and delete players from the playfield
3925 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3927 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3929 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3931 cav->player_x[player_nr] = x;
3932 cav->player_y[player_nr] = y;
3934 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3939 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3941 static int ball_xy[8][2] =
3952 struct LevelInfo_EM *level_em = level->native_em_level;
3953 struct CAVE *cav = level_em->cav;
3956 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3957 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3959 level->time = cav->time_seconds;
3960 level->gems_needed = cav->gems_needed;
3962 sprintf(level->name, "Level %d", level->file_info.nr);
3964 level->score[SC_EMERALD] = cav->emerald_score;
3965 level->score[SC_DIAMOND] = cav->diamond_score;
3966 level->score[SC_ROBOT] = cav->alien_score;
3967 level->score[SC_SPACESHIP] = cav->tank_score;
3968 level->score[SC_BUG] = cav->bug_score;
3969 level->score[SC_YAMYAM] = cav->eater_score;
3970 level->score[SC_NUT] = cav->nut_score;
3971 level->score[SC_DYNAMITE] = cav->dynamite_score;
3972 level->score[SC_KEY] = cav->key_score;
3973 level->score[SC_TIME_BONUS] = cav->exit_score;
3975 level->num_yamyam_contents = cav->num_eater_arrays;
3977 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3978 for (y = 0; y < 3; y++)
3979 for (x = 0; x < 3; x++)
3980 level->yamyam_content[i].e[x][y] =
3981 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3983 level->amoeba_speed = cav->amoeba_time;
3984 level->time_magic_wall = cav->wonderwall_time;
3985 level->time_wheel = cav->wheel_time;
3987 level->android_move_time = cav->android_move_time;
3988 level->android_clone_time = cav->android_clone_time;
3989 level->ball_random = cav->ball_random;
3990 level->ball_active_initial = cav->ball_active;
3991 level->ball_time = cav->ball_time;
3992 level->num_ball_contents = cav->num_ball_arrays;
3994 level->lenses_score = cav->lenses_score;
3995 level->magnify_score = cav->magnify_score;
3996 level->slurp_score = cav->slurp_score;
3998 level->lenses_time = cav->lenses_time;
3999 level->magnify_time = cav->magnify_time;
4001 level->wind_direction_initial =
4002 map_direction_EM_to_RND(cav->wind_direction);
4004 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4005 for (j = 0; j < 8; j++)
4006 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4007 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4009 map_android_clone_elements_EM_to_RND(level);
4011 // convert the playfield (some elements need special treatment)
4012 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4014 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4016 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4017 new_element = EL_AMOEBA_DEAD;
4019 level->field[x][y] = new_element;
4022 for (i = 0; i < MAX_PLAYERS; i++)
4024 // in case of all players set to the same field, use the first player
4025 int nr = MAX_PLAYERS - i - 1;
4026 int jx = cav->player_x[nr];
4027 int jy = cav->player_y[nr];
4029 if (jx != -1 && jy != -1)
4030 level->field[jx][jy] = EL_PLAYER_1 + nr;
4033 // time score is counted for each 10 seconds left in Emerald Mine levels
4034 level->time_score_base = 10;
4038 // ----------------------------------------------------------------------------
4039 // functions for loading SP level
4040 // ----------------------------------------------------------------------------
4042 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4044 struct LevelInfo_SP *level_sp = level->native_sp_level;
4045 LevelInfoType *header = &level_sp->header;
4048 level_sp->width = level->fieldx;
4049 level_sp->height = level->fieldy;
4051 for (x = 0; x < level->fieldx; x++)
4052 for (y = 0; y < level->fieldy; y++)
4053 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4055 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4057 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4058 header->LevelTitle[i] = level->name[i];
4059 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4061 header->InfotronsNeeded = level->gems_needed;
4063 header->SpecialPortCount = 0;
4065 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4067 boolean gravity_port_found = FALSE;
4068 boolean gravity_port_valid = FALSE;
4069 int gravity_port_flag;
4070 int gravity_port_base_element;
4071 int element = level->field[x][y];
4073 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4074 element <= EL_SP_GRAVITY_ON_PORT_UP)
4076 gravity_port_found = TRUE;
4077 gravity_port_valid = TRUE;
4078 gravity_port_flag = 1;
4079 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4081 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4082 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4084 gravity_port_found = TRUE;
4085 gravity_port_valid = TRUE;
4086 gravity_port_flag = 0;
4087 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4089 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4090 element <= EL_SP_GRAVITY_PORT_UP)
4092 // change R'n'D style gravity inverting special port to normal port
4093 // (there are no gravity inverting ports in native Supaplex engine)
4095 gravity_port_found = TRUE;
4096 gravity_port_valid = FALSE;
4097 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4100 if (gravity_port_found)
4102 if (gravity_port_valid &&
4103 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4105 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4107 port->PortLocation = (y * level->fieldx + x) * 2;
4108 port->Gravity = gravity_port_flag;
4110 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4112 header->SpecialPortCount++;
4116 // change special gravity port to normal port
4118 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4121 level_sp->playfield[x][y] = element - EL_SP_START;
4126 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4128 struct LevelInfo_SP *level_sp = level->native_sp_level;
4129 LevelInfoType *header = &level_sp->header;
4130 boolean num_invalid_elements = 0;
4133 level->fieldx = level_sp->width;
4134 level->fieldy = level_sp->height;
4136 for (x = 0; x < level->fieldx; x++)
4138 for (y = 0; y < level->fieldy; y++)
4140 int element_old = level_sp->playfield[x][y];
4141 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4143 if (element_new == EL_UNKNOWN)
4145 num_invalid_elements++;
4147 Debug("level:native:SP", "invalid element %d at position %d, %d",
4151 level->field[x][y] = element_new;
4155 if (num_invalid_elements > 0)
4156 Warn("found %d invalid elements%s", num_invalid_elements,
4157 (!options.debug ? " (use '--debug' for more details)" : ""));
4159 for (i = 0; i < MAX_PLAYERS; i++)
4160 level->initial_player_gravity[i] =
4161 (header->InitialGravity == 1 ? TRUE : FALSE);
4163 // skip leading spaces
4164 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4165 if (header->LevelTitle[i] != ' ')
4169 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4170 level->name[j] = header->LevelTitle[i];
4171 level->name[j] = '\0';
4173 // cut trailing spaces
4175 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4176 level->name[j - 1] = '\0';
4178 level->gems_needed = header->InfotronsNeeded;
4180 for (i = 0; i < header->SpecialPortCount; i++)
4182 SpecialPortType *port = &header->SpecialPort[i];
4183 int port_location = port->PortLocation;
4184 int gravity = port->Gravity;
4185 int port_x, port_y, port_element;
4187 port_x = (port_location / 2) % level->fieldx;
4188 port_y = (port_location / 2) / level->fieldx;
4190 if (port_x < 0 || port_x >= level->fieldx ||
4191 port_y < 0 || port_y >= level->fieldy)
4193 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4198 port_element = level->field[port_x][port_y];
4200 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4201 port_element > EL_SP_GRAVITY_PORT_UP)
4203 Warn("no special port at position (%d, %d)", port_x, port_y);
4208 // change previous (wrong) gravity inverting special port to either
4209 // gravity enabling special port or gravity disabling special port
4210 level->field[port_x][port_y] +=
4211 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4212 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4215 // change special gravity ports without database entries to normal ports
4216 for (x = 0; x < level->fieldx; x++)
4217 for (y = 0; y < level->fieldy; y++)
4218 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4219 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4220 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4222 level->time = 0; // no time limit
4223 level->amoeba_speed = 0;
4224 level->time_magic_wall = 0;
4225 level->time_wheel = 0;
4226 level->amoeba_content = EL_EMPTY;
4228 // original Supaplex does not use score values -- rate by playing time
4229 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4230 level->score[i] = 0;
4232 level->rate_time_over_score = TRUE;
4234 // there are no yamyams in supaplex levels
4235 for (i = 0; i < level->num_yamyam_contents; i++)
4236 for (x = 0; x < 3; x++)
4237 for (y = 0; y < 3; y++)
4238 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4241 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4243 struct LevelInfo_SP *level_sp = level->native_sp_level;
4244 struct DemoInfo_SP *demo = &level_sp->demo;
4247 // always start with reliable default values
4248 demo->is_available = FALSE;
4251 if (TAPE_IS_EMPTY(tape))
4254 demo->level_nr = tape.level_nr; // (currently not used)
4256 level_sp->header.DemoRandomSeed = tape.random_seed;
4260 for (i = 0; i < tape.length; i++)
4262 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4263 int demo_repeat = tape.pos[i].delay;
4264 int demo_entries = (demo_repeat + 15) / 16;
4266 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4268 Warn("tape truncated: size exceeds maximum SP demo size %d",
4274 for (j = 0; j < demo_repeat / 16; j++)
4275 demo->data[demo->length++] = 0xf0 | demo_action;
4277 if (demo_repeat % 16)
4278 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4281 demo->is_available = TRUE;
4284 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4286 struct LevelInfo_SP *level_sp = level->native_sp_level;
4287 struct DemoInfo_SP *demo = &level_sp->demo;
4288 char *filename = level->file_info.filename;
4291 // always start with reliable default values
4292 setTapeInfoToDefaults();
4294 if (!demo->is_available)
4297 tape.level_nr = demo->level_nr; // (currently not used)
4298 tape.random_seed = level_sp->header.DemoRandomSeed;
4300 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4303 tape.pos[tape.counter].delay = 0;
4305 for (i = 0; i < demo->length; i++)
4307 int demo_action = demo->data[i] & 0x0f;
4308 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4309 int tape_action = map_key_SP_to_RND(demo_action);
4310 int tape_repeat = demo_repeat + 1;
4311 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4312 boolean success = 0;
4315 for (j = 0; j < tape_repeat; j++)
4316 success = TapeAddAction(action);
4320 Warn("SP demo truncated: size exceeds maximum tape size %d",
4327 TapeHaltRecording();
4331 // ----------------------------------------------------------------------------
4332 // functions for loading MM level
4333 // ----------------------------------------------------------------------------
4335 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4337 struct LevelInfo_MM *level_mm = level->native_mm_level;
4340 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4341 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4343 level_mm->time = level->time;
4344 level_mm->kettles_needed = level->gems_needed;
4345 level_mm->auto_count_kettles = level->auto_count_gems;
4347 level_mm->mm_laser_red = level->mm_laser_red;
4348 level_mm->mm_laser_green = level->mm_laser_green;
4349 level_mm->mm_laser_blue = level->mm_laser_blue;
4351 level_mm->df_laser_red = level->df_laser_red;
4352 level_mm->df_laser_green = level->df_laser_green;
4353 level_mm->df_laser_blue = level->df_laser_blue;
4355 strcpy(level_mm->name, level->name);
4356 strcpy(level_mm->author, level->author);
4358 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4359 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4360 level_mm->score[SC_KEY] = level->score[SC_KEY];
4361 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4362 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4364 level_mm->amoeba_speed = level->amoeba_speed;
4365 level_mm->time_fuse = level->mm_time_fuse;
4366 level_mm->time_bomb = level->mm_time_bomb;
4367 level_mm->time_ball = level->mm_time_ball;
4368 level_mm->time_block = level->mm_time_block;
4370 level_mm->num_ball_contents = level->num_mm_ball_contents;
4371 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4372 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4373 level_mm->explode_ball = level->explode_mm_ball;
4375 for (i = 0; i < level->num_mm_ball_contents; i++)
4376 level_mm->ball_content[i] =
4377 map_element_RND_to_MM(level->mm_ball_content[i]);
4379 for (x = 0; x < level->fieldx; x++)
4380 for (y = 0; y < level->fieldy; y++)
4382 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4385 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4387 struct LevelInfo_MM *level_mm = level->native_mm_level;
4390 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4391 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4393 level->time = level_mm->time;
4394 level->gems_needed = level_mm->kettles_needed;
4395 level->auto_count_gems = level_mm->auto_count_kettles;
4397 level->mm_laser_red = level_mm->mm_laser_red;
4398 level->mm_laser_green = level_mm->mm_laser_green;
4399 level->mm_laser_blue = level_mm->mm_laser_blue;
4401 level->df_laser_red = level_mm->df_laser_red;
4402 level->df_laser_green = level_mm->df_laser_green;
4403 level->df_laser_blue = level_mm->df_laser_blue;
4405 strcpy(level->name, level_mm->name);
4407 // only overwrite author from 'levelinfo.conf' if author defined in level
4408 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4409 strcpy(level->author, level_mm->author);
4411 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4412 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4413 level->score[SC_KEY] = level_mm->score[SC_KEY];
4414 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4415 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4417 level->amoeba_speed = level_mm->amoeba_speed;
4418 level->mm_time_fuse = level_mm->time_fuse;
4419 level->mm_time_bomb = level_mm->time_bomb;
4420 level->mm_time_ball = level_mm->time_ball;
4421 level->mm_time_block = level_mm->time_block;
4423 level->num_mm_ball_contents = level_mm->num_ball_contents;
4424 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4425 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4426 level->explode_mm_ball = level_mm->explode_ball;
4428 for (i = 0; i < level->num_mm_ball_contents; i++)
4429 level->mm_ball_content[i] =
4430 map_element_MM_to_RND(level_mm->ball_content[i]);
4432 for (x = 0; x < level->fieldx; x++)
4433 for (y = 0; y < level->fieldy; y++)
4434 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4438 // ----------------------------------------------------------------------------
4439 // functions for loading DC level
4440 // ----------------------------------------------------------------------------
4442 #define DC_LEVEL_HEADER_SIZE 344
4444 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4447 static int last_data_encoded;
4451 int diff_hi, diff_lo;
4452 int data_hi, data_lo;
4453 unsigned short data_decoded;
4457 last_data_encoded = 0;
4464 diff = data_encoded - last_data_encoded;
4465 diff_hi = diff & ~0xff;
4466 diff_lo = diff & 0xff;
4470 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4471 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4472 data_hi = data_hi & 0xff00;
4474 data_decoded = data_hi | data_lo;
4476 last_data_encoded = data_encoded;
4478 offset1 = (offset1 + 1) % 31;
4479 offset2 = offset2 & 0xff;
4481 return data_decoded;
4484 static int getMappedElement_DC(int element)
4492 // 0x0117 - 0x036e: (?)
4495 // 0x042d - 0x0684: (?)
4511 element = EL_CRYSTAL;
4514 case 0x0e77: // quicksand (boulder)
4515 element = EL_QUICKSAND_FAST_FULL;
4518 case 0x0e99: // slow quicksand (boulder)
4519 element = EL_QUICKSAND_FULL;
4523 element = EL_EM_EXIT_OPEN;
4527 element = EL_EM_EXIT_CLOSED;
4531 element = EL_EM_STEEL_EXIT_OPEN;
4535 element = EL_EM_STEEL_EXIT_CLOSED;
4538 case 0x0f4f: // dynamite (lit 1)
4539 element = EL_EM_DYNAMITE_ACTIVE;
4542 case 0x0f57: // dynamite (lit 2)
4543 element = EL_EM_DYNAMITE_ACTIVE;
4546 case 0x0f5f: // dynamite (lit 3)
4547 element = EL_EM_DYNAMITE_ACTIVE;
4550 case 0x0f67: // dynamite (lit 4)
4551 element = EL_EM_DYNAMITE_ACTIVE;
4558 element = EL_AMOEBA_WET;
4562 element = EL_AMOEBA_DROP;
4566 element = EL_DC_MAGIC_WALL;
4570 element = EL_SPACESHIP_UP;
4574 element = EL_SPACESHIP_DOWN;
4578 element = EL_SPACESHIP_LEFT;
4582 element = EL_SPACESHIP_RIGHT;
4586 element = EL_BUG_UP;
4590 element = EL_BUG_DOWN;
4594 element = EL_BUG_LEFT;
4598 element = EL_BUG_RIGHT;
4602 element = EL_MOLE_UP;
4606 element = EL_MOLE_DOWN;
4610 element = EL_MOLE_LEFT;
4614 element = EL_MOLE_RIGHT;
4622 element = EL_YAMYAM_UP;
4626 element = EL_SWITCHGATE_OPEN;
4630 element = EL_SWITCHGATE_CLOSED;
4634 element = EL_DC_SWITCHGATE_SWITCH_UP;
4638 element = EL_TIMEGATE_CLOSED;
4641 case 0x144c: // conveyor belt switch (green)
4642 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4645 case 0x144f: // conveyor belt switch (red)
4646 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4649 case 0x1452: // conveyor belt switch (blue)
4650 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4654 element = EL_CONVEYOR_BELT_3_MIDDLE;
4658 element = EL_CONVEYOR_BELT_3_LEFT;
4662 element = EL_CONVEYOR_BELT_3_RIGHT;
4666 element = EL_CONVEYOR_BELT_1_MIDDLE;
4670 element = EL_CONVEYOR_BELT_1_LEFT;
4674 element = EL_CONVEYOR_BELT_1_RIGHT;
4678 element = EL_CONVEYOR_BELT_4_MIDDLE;
4682 element = EL_CONVEYOR_BELT_4_LEFT;
4686 element = EL_CONVEYOR_BELT_4_RIGHT;
4690 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4694 element = EL_EXPANDABLE_WALL_VERTICAL;
4698 element = EL_EXPANDABLE_WALL_ANY;
4701 case 0x14ce: // growing steel wall (left/right)
4702 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4705 case 0x14df: // growing steel wall (up/down)
4706 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4709 case 0x14e8: // growing steel wall (up/down/left/right)
4710 element = EL_EXPANDABLE_STEELWALL_ANY;
4714 element = EL_SHIELD_DEADLY;
4718 element = EL_EXTRA_TIME;
4726 element = EL_EMPTY_SPACE;
4729 case 0x1578: // quicksand (empty)
4730 element = EL_QUICKSAND_FAST_EMPTY;
4733 case 0x1579: // slow quicksand (empty)
4734 element = EL_QUICKSAND_EMPTY;
4744 element = EL_EM_DYNAMITE;
4747 case 0x15a1: // key (red)
4748 element = EL_EM_KEY_1;
4751 case 0x15a2: // key (yellow)
4752 element = EL_EM_KEY_2;
4755 case 0x15a3: // key (blue)
4756 element = EL_EM_KEY_4;
4759 case 0x15a4: // key (green)
4760 element = EL_EM_KEY_3;
4763 case 0x15a5: // key (white)
4764 element = EL_DC_KEY_WHITE;
4768 element = EL_WALL_SLIPPERY;
4775 case 0x15a8: // wall (not round)
4779 case 0x15a9: // (blue)
4780 element = EL_CHAR_A;
4783 case 0x15aa: // (blue)
4784 element = EL_CHAR_B;
4787 case 0x15ab: // (blue)
4788 element = EL_CHAR_C;
4791 case 0x15ac: // (blue)
4792 element = EL_CHAR_D;
4795 case 0x15ad: // (blue)
4796 element = EL_CHAR_E;
4799 case 0x15ae: // (blue)
4800 element = EL_CHAR_F;
4803 case 0x15af: // (blue)
4804 element = EL_CHAR_G;
4807 case 0x15b0: // (blue)
4808 element = EL_CHAR_H;
4811 case 0x15b1: // (blue)
4812 element = EL_CHAR_I;
4815 case 0x15b2: // (blue)
4816 element = EL_CHAR_J;
4819 case 0x15b3: // (blue)
4820 element = EL_CHAR_K;
4823 case 0x15b4: // (blue)
4824 element = EL_CHAR_L;
4827 case 0x15b5: // (blue)
4828 element = EL_CHAR_M;
4831 case 0x15b6: // (blue)
4832 element = EL_CHAR_N;
4835 case 0x15b7: // (blue)
4836 element = EL_CHAR_O;
4839 case 0x15b8: // (blue)
4840 element = EL_CHAR_P;
4843 case 0x15b9: // (blue)
4844 element = EL_CHAR_Q;
4847 case 0x15ba: // (blue)
4848 element = EL_CHAR_R;
4851 case 0x15bb: // (blue)
4852 element = EL_CHAR_S;
4855 case 0x15bc: // (blue)
4856 element = EL_CHAR_T;
4859 case 0x15bd: // (blue)
4860 element = EL_CHAR_U;
4863 case 0x15be: // (blue)
4864 element = EL_CHAR_V;
4867 case 0x15bf: // (blue)
4868 element = EL_CHAR_W;
4871 case 0x15c0: // (blue)
4872 element = EL_CHAR_X;
4875 case 0x15c1: // (blue)
4876 element = EL_CHAR_Y;
4879 case 0x15c2: // (blue)
4880 element = EL_CHAR_Z;
4883 case 0x15c3: // (blue)
4884 element = EL_CHAR_AUMLAUT;
4887 case 0x15c4: // (blue)
4888 element = EL_CHAR_OUMLAUT;
4891 case 0x15c5: // (blue)
4892 element = EL_CHAR_UUMLAUT;
4895 case 0x15c6: // (blue)
4896 element = EL_CHAR_0;
4899 case 0x15c7: // (blue)
4900 element = EL_CHAR_1;
4903 case 0x15c8: // (blue)
4904 element = EL_CHAR_2;
4907 case 0x15c9: // (blue)
4908 element = EL_CHAR_3;
4911 case 0x15ca: // (blue)
4912 element = EL_CHAR_4;
4915 case 0x15cb: // (blue)
4916 element = EL_CHAR_5;
4919 case 0x15cc: // (blue)
4920 element = EL_CHAR_6;
4923 case 0x15cd: // (blue)
4924 element = EL_CHAR_7;
4927 case 0x15ce: // (blue)
4928 element = EL_CHAR_8;
4931 case 0x15cf: // (blue)
4932 element = EL_CHAR_9;
4935 case 0x15d0: // (blue)
4936 element = EL_CHAR_PERIOD;
4939 case 0x15d1: // (blue)
4940 element = EL_CHAR_EXCLAM;
4943 case 0x15d2: // (blue)
4944 element = EL_CHAR_COLON;
4947 case 0x15d3: // (blue)
4948 element = EL_CHAR_LESS;
4951 case 0x15d4: // (blue)
4952 element = EL_CHAR_GREATER;
4955 case 0x15d5: // (blue)
4956 element = EL_CHAR_QUESTION;
4959 case 0x15d6: // (blue)
4960 element = EL_CHAR_COPYRIGHT;
4963 case 0x15d7: // (blue)
4964 element = EL_CHAR_UP;
4967 case 0x15d8: // (blue)
4968 element = EL_CHAR_DOWN;
4971 case 0x15d9: // (blue)
4972 element = EL_CHAR_BUTTON;
4975 case 0x15da: // (blue)
4976 element = EL_CHAR_PLUS;
4979 case 0x15db: // (blue)
4980 element = EL_CHAR_MINUS;
4983 case 0x15dc: // (blue)
4984 element = EL_CHAR_APOSTROPHE;
4987 case 0x15dd: // (blue)
4988 element = EL_CHAR_PARENLEFT;
4991 case 0x15de: // (blue)
4992 element = EL_CHAR_PARENRIGHT;
4995 case 0x15df: // (green)
4996 element = EL_CHAR_A;
4999 case 0x15e0: // (green)
5000 element = EL_CHAR_B;
5003 case 0x15e1: // (green)
5004 element = EL_CHAR_C;
5007 case 0x15e2: // (green)
5008 element = EL_CHAR_D;
5011 case 0x15e3: // (green)
5012 element = EL_CHAR_E;
5015 case 0x15e4: // (green)
5016 element = EL_CHAR_F;
5019 case 0x15e5: // (green)
5020 element = EL_CHAR_G;
5023 case 0x15e6: // (green)
5024 element = EL_CHAR_H;
5027 case 0x15e7: // (green)
5028 element = EL_CHAR_I;
5031 case 0x15e8: // (green)
5032 element = EL_CHAR_J;
5035 case 0x15e9: // (green)
5036 element = EL_CHAR_K;
5039 case 0x15ea: // (green)
5040 element = EL_CHAR_L;
5043 case 0x15eb: // (green)
5044 element = EL_CHAR_M;
5047 case 0x15ec: // (green)
5048 element = EL_CHAR_N;
5051 case 0x15ed: // (green)
5052 element = EL_CHAR_O;
5055 case 0x15ee: // (green)
5056 element = EL_CHAR_P;
5059 case 0x15ef: // (green)
5060 element = EL_CHAR_Q;
5063 case 0x15f0: // (green)
5064 element = EL_CHAR_R;
5067 case 0x15f1: // (green)
5068 element = EL_CHAR_S;
5071 case 0x15f2: // (green)
5072 element = EL_CHAR_T;
5075 case 0x15f3: // (green)
5076 element = EL_CHAR_U;
5079 case 0x15f4: // (green)
5080 element = EL_CHAR_V;
5083 case 0x15f5: // (green)
5084 element = EL_CHAR_W;
5087 case 0x15f6: // (green)
5088 element = EL_CHAR_X;
5091 case 0x15f7: // (green)
5092 element = EL_CHAR_Y;
5095 case 0x15f8: // (green)
5096 element = EL_CHAR_Z;
5099 case 0x15f9: // (green)
5100 element = EL_CHAR_AUMLAUT;
5103 case 0x15fa: // (green)
5104 element = EL_CHAR_OUMLAUT;
5107 case 0x15fb: // (green)
5108 element = EL_CHAR_UUMLAUT;
5111 case 0x15fc: // (green)
5112 element = EL_CHAR_0;
5115 case 0x15fd: // (green)
5116 element = EL_CHAR_1;
5119 case 0x15fe: // (green)
5120 element = EL_CHAR_2;
5123 case 0x15ff: // (green)
5124 element = EL_CHAR_3;
5127 case 0x1600: // (green)
5128 element = EL_CHAR_4;
5131 case 0x1601: // (green)
5132 element = EL_CHAR_5;
5135 case 0x1602: // (green)
5136 element = EL_CHAR_6;
5139 case 0x1603: // (green)
5140 element = EL_CHAR_7;
5143 case 0x1604: // (green)
5144 element = EL_CHAR_8;
5147 case 0x1605: // (green)
5148 element = EL_CHAR_9;
5151 case 0x1606: // (green)
5152 element = EL_CHAR_PERIOD;
5155 case 0x1607: // (green)
5156 element = EL_CHAR_EXCLAM;
5159 case 0x1608: // (green)
5160 element = EL_CHAR_COLON;
5163 case 0x1609: // (green)
5164 element = EL_CHAR_LESS;
5167 case 0x160a: // (green)
5168 element = EL_CHAR_GREATER;
5171 case 0x160b: // (green)
5172 element = EL_CHAR_QUESTION;
5175 case 0x160c: // (green)
5176 element = EL_CHAR_COPYRIGHT;
5179 case 0x160d: // (green)
5180 element = EL_CHAR_UP;
5183 case 0x160e: // (green)
5184 element = EL_CHAR_DOWN;
5187 case 0x160f: // (green)
5188 element = EL_CHAR_BUTTON;
5191 case 0x1610: // (green)
5192 element = EL_CHAR_PLUS;
5195 case 0x1611: // (green)
5196 element = EL_CHAR_MINUS;
5199 case 0x1612: // (green)
5200 element = EL_CHAR_APOSTROPHE;
5203 case 0x1613: // (green)
5204 element = EL_CHAR_PARENLEFT;
5207 case 0x1614: // (green)
5208 element = EL_CHAR_PARENRIGHT;
5211 case 0x1615: // (blue steel)
5212 element = EL_STEEL_CHAR_A;
5215 case 0x1616: // (blue steel)
5216 element = EL_STEEL_CHAR_B;
5219 case 0x1617: // (blue steel)
5220 element = EL_STEEL_CHAR_C;
5223 case 0x1618: // (blue steel)
5224 element = EL_STEEL_CHAR_D;
5227 case 0x1619: // (blue steel)
5228 element = EL_STEEL_CHAR_E;
5231 case 0x161a: // (blue steel)
5232 element = EL_STEEL_CHAR_F;
5235 case 0x161b: // (blue steel)
5236 element = EL_STEEL_CHAR_G;
5239 case 0x161c: // (blue steel)
5240 element = EL_STEEL_CHAR_H;
5243 case 0x161d: // (blue steel)
5244 element = EL_STEEL_CHAR_I;
5247 case 0x161e: // (blue steel)
5248 element = EL_STEEL_CHAR_J;
5251 case 0x161f: // (blue steel)
5252 element = EL_STEEL_CHAR_K;
5255 case 0x1620: // (blue steel)
5256 element = EL_STEEL_CHAR_L;
5259 case 0x1621: // (blue steel)
5260 element = EL_STEEL_CHAR_M;
5263 case 0x1622: // (blue steel)
5264 element = EL_STEEL_CHAR_N;
5267 case 0x1623: // (blue steel)
5268 element = EL_STEEL_CHAR_O;
5271 case 0x1624: // (blue steel)
5272 element = EL_STEEL_CHAR_P;
5275 case 0x1625: // (blue steel)
5276 element = EL_STEEL_CHAR_Q;
5279 case 0x1626: // (blue steel)
5280 element = EL_STEEL_CHAR_R;
5283 case 0x1627: // (blue steel)
5284 element = EL_STEEL_CHAR_S;
5287 case 0x1628: // (blue steel)
5288 element = EL_STEEL_CHAR_T;
5291 case 0x1629: // (blue steel)
5292 element = EL_STEEL_CHAR_U;
5295 case 0x162a: // (blue steel)
5296 element = EL_STEEL_CHAR_V;
5299 case 0x162b: // (blue steel)
5300 element = EL_STEEL_CHAR_W;
5303 case 0x162c: // (blue steel)
5304 element = EL_STEEL_CHAR_X;
5307 case 0x162d: // (blue steel)
5308 element = EL_STEEL_CHAR_Y;
5311 case 0x162e: // (blue steel)
5312 element = EL_STEEL_CHAR_Z;
5315 case 0x162f: // (blue steel)
5316 element = EL_STEEL_CHAR_AUMLAUT;
5319 case 0x1630: // (blue steel)
5320 element = EL_STEEL_CHAR_OUMLAUT;
5323 case 0x1631: // (blue steel)
5324 element = EL_STEEL_CHAR_UUMLAUT;
5327 case 0x1632: // (blue steel)
5328 element = EL_STEEL_CHAR_0;
5331 case 0x1633: // (blue steel)
5332 element = EL_STEEL_CHAR_1;
5335 case 0x1634: // (blue steel)
5336 element = EL_STEEL_CHAR_2;
5339 case 0x1635: // (blue steel)
5340 element = EL_STEEL_CHAR_3;
5343 case 0x1636: // (blue steel)
5344 element = EL_STEEL_CHAR_4;
5347 case 0x1637: // (blue steel)
5348 element = EL_STEEL_CHAR_5;
5351 case 0x1638: // (blue steel)
5352 element = EL_STEEL_CHAR_6;
5355 case 0x1639: // (blue steel)
5356 element = EL_STEEL_CHAR_7;
5359 case 0x163a: // (blue steel)
5360 element = EL_STEEL_CHAR_8;
5363 case 0x163b: // (blue steel)
5364 element = EL_STEEL_CHAR_9;
5367 case 0x163c: // (blue steel)
5368 element = EL_STEEL_CHAR_PERIOD;
5371 case 0x163d: // (blue steel)
5372 element = EL_STEEL_CHAR_EXCLAM;
5375 case 0x163e: // (blue steel)
5376 element = EL_STEEL_CHAR_COLON;
5379 case 0x163f: // (blue steel)
5380 element = EL_STEEL_CHAR_LESS;
5383 case 0x1640: // (blue steel)
5384 element = EL_STEEL_CHAR_GREATER;
5387 case 0x1641: // (blue steel)
5388 element = EL_STEEL_CHAR_QUESTION;
5391 case 0x1642: // (blue steel)
5392 element = EL_STEEL_CHAR_COPYRIGHT;
5395 case 0x1643: // (blue steel)
5396 element = EL_STEEL_CHAR_UP;
5399 case 0x1644: // (blue steel)
5400 element = EL_STEEL_CHAR_DOWN;
5403 case 0x1645: // (blue steel)
5404 element = EL_STEEL_CHAR_BUTTON;
5407 case 0x1646: // (blue steel)
5408 element = EL_STEEL_CHAR_PLUS;
5411 case 0x1647: // (blue steel)
5412 element = EL_STEEL_CHAR_MINUS;
5415 case 0x1648: // (blue steel)
5416 element = EL_STEEL_CHAR_APOSTROPHE;
5419 case 0x1649: // (blue steel)
5420 element = EL_STEEL_CHAR_PARENLEFT;
5423 case 0x164a: // (blue steel)
5424 element = EL_STEEL_CHAR_PARENRIGHT;
5427 case 0x164b: // (green steel)
5428 element = EL_STEEL_CHAR_A;
5431 case 0x164c: // (green steel)
5432 element = EL_STEEL_CHAR_B;
5435 case 0x164d: // (green steel)
5436 element = EL_STEEL_CHAR_C;
5439 case 0x164e: // (green steel)
5440 element = EL_STEEL_CHAR_D;
5443 case 0x164f: // (green steel)
5444 element = EL_STEEL_CHAR_E;
5447 case 0x1650: // (green steel)
5448 element = EL_STEEL_CHAR_F;
5451 case 0x1651: // (green steel)
5452 element = EL_STEEL_CHAR_G;
5455 case 0x1652: // (green steel)
5456 element = EL_STEEL_CHAR_H;
5459 case 0x1653: // (green steel)
5460 element = EL_STEEL_CHAR_I;
5463 case 0x1654: // (green steel)
5464 element = EL_STEEL_CHAR_J;
5467 case 0x1655: // (green steel)
5468 element = EL_STEEL_CHAR_K;
5471 case 0x1656: // (green steel)
5472 element = EL_STEEL_CHAR_L;
5475 case 0x1657: // (green steel)
5476 element = EL_STEEL_CHAR_M;
5479 case 0x1658: // (green steel)
5480 element = EL_STEEL_CHAR_N;
5483 case 0x1659: // (green steel)
5484 element = EL_STEEL_CHAR_O;
5487 case 0x165a: // (green steel)
5488 element = EL_STEEL_CHAR_P;
5491 case 0x165b: // (green steel)
5492 element = EL_STEEL_CHAR_Q;
5495 case 0x165c: // (green steel)
5496 element = EL_STEEL_CHAR_R;
5499 case 0x165d: // (green steel)
5500 element = EL_STEEL_CHAR_S;
5503 case 0x165e: // (green steel)
5504 element = EL_STEEL_CHAR_T;
5507 case 0x165f: // (green steel)
5508 element = EL_STEEL_CHAR_U;
5511 case 0x1660: // (green steel)
5512 element = EL_STEEL_CHAR_V;
5515 case 0x1661: // (green steel)
5516 element = EL_STEEL_CHAR_W;
5519 case 0x1662: // (green steel)
5520 element = EL_STEEL_CHAR_X;
5523 case 0x1663: // (green steel)
5524 element = EL_STEEL_CHAR_Y;
5527 case 0x1664: // (green steel)
5528 element = EL_STEEL_CHAR_Z;
5531 case 0x1665: // (green steel)
5532 element = EL_STEEL_CHAR_AUMLAUT;
5535 case 0x1666: // (green steel)
5536 element = EL_STEEL_CHAR_OUMLAUT;
5539 case 0x1667: // (green steel)
5540 element = EL_STEEL_CHAR_UUMLAUT;
5543 case 0x1668: // (green steel)
5544 element = EL_STEEL_CHAR_0;
5547 case 0x1669: // (green steel)
5548 element = EL_STEEL_CHAR_1;
5551 case 0x166a: // (green steel)
5552 element = EL_STEEL_CHAR_2;
5555 case 0x166b: // (green steel)
5556 element = EL_STEEL_CHAR_3;
5559 case 0x166c: // (green steel)
5560 element = EL_STEEL_CHAR_4;
5563 case 0x166d: // (green steel)
5564 element = EL_STEEL_CHAR_5;
5567 case 0x166e: // (green steel)
5568 element = EL_STEEL_CHAR_6;
5571 case 0x166f: // (green steel)
5572 element = EL_STEEL_CHAR_7;
5575 case 0x1670: // (green steel)
5576 element = EL_STEEL_CHAR_8;
5579 case 0x1671: // (green steel)
5580 element = EL_STEEL_CHAR_9;
5583 case 0x1672: // (green steel)
5584 element = EL_STEEL_CHAR_PERIOD;
5587 case 0x1673: // (green steel)
5588 element = EL_STEEL_CHAR_EXCLAM;
5591 case 0x1674: // (green steel)
5592 element = EL_STEEL_CHAR_COLON;
5595 case 0x1675: // (green steel)
5596 element = EL_STEEL_CHAR_LESS;
5599 case 0x1676: // (green steel)
5600 element = EL_STEEL_CHAR_GREATER;
5603 case 0x1677: // (green steel)
5604 element = EL_STEEL_CHAR_QUESTION;
5607 case 0x1678: // (green steel)
5608 element = EL_STEEL_CHAR_COPYRIGHT;
5611 case 0x1679: // (green steel)
5612 element = EL_STEEL_CHAR_UP;
5615 case 0x167a: // (green steel)
5616 element = EL_STEEL_CHAR_DOWN;
5619 case 0x167b: // (green steel)
5620 element = EL_STEEL_CHAR_BUTTON;
5623 case 0x167c: // (green steel)
5624 element = EL_STEEL_CHAR_PLUS;
5627 case 0x167d: // (green steel)
5628 element = EL_STEEL_CHAR_MINUS;
5631 case 0x167e: // (green steel)
5632 element = EL_STEEL_CHAR_APOSTROPHE;
5635 case 0x167f: // (green steel)
5636 element = EL_STEEL_CHAR_PARENLEFT;
5639 case 0x1680: // (green steel)
5640 element = EL_STEEL_CHAR_PARENRIGHT;
5643 case 0x1681: // gate (red)
5644 element = EL_EM_GATE_1;
5647 case 0x1682: // secret gate (red)
5648 element = EL_EM_GATE_1_GRAY;
5651 case 0x1683: // gate (yellow)
5652 element = EL_EM_GATE_2;
5655 case 0x1684: // secret gate (yellow)
5656 element = EL_EM_GATE_2_GRAY;
5659 case 0x1685: // gate (blue)
5660 element = EL_EM_GATE_4;
5663 case 0x1686: // secret gate (blue)
5664 element = EL_EM_GATE_4_GRAY;
5667 case 0x1687: // gate (green)
5668 element = EL_EM_GATE_3;
5671 case 0x1688: // secret gate (green)
5672 element = EL_EM_GATE_3_GRAY;
5675 case 0x1689: // gate (white)
5676 element = EL_DC_GATE_WHITE;
5679 case 0x168a: // secret gate (white)
5680 element = EL_DC_GATE_WHITE_GRAY;
5683 case 0x168b: // secret gate (no key)
5684 element = EL_DC_GATE_FAKE_GRAY;
5688 element = EL_ROBOT_WHEEL;
5692 element = EL_DC_TIMEGATE_SWITCH;
5696 element = EL_ACID_POOL_BOTTOM;
5700 element = EL_ACID_POOL_TOPLEFT;
5704 element = EL_ACID_POOL_TOPRIGHT;
5708 element = EL_ACID_POOL_BOTTOMLEFT;
5712 element = EL_ACID_POOL_BOTTOMRIGHT;
5716 element = EL_STEELWALL;
5720 element = EL_STEELWALL_SLIPPERY;
5723 case 0x1695: // steel wall (not round)
5724 element = EL_STEELWALL;
5727 case 0x1696: // steel wall (left)
5728 element = EL_DC_STEELWALL_1_LEFT;
5731 case 0x1697: // steel wall (bottom)
5732 element = EL_DC_STEELWALL_1_BOTTOM;
5735 case 0x1698: // steel wall (right)
5736 element = EL_DC_STEELWALL_1_RIGHT;
5739 case 0x1699: // steel wall (top)
5740 element = EL_DC_STEELWALL_1_TOP;
5743 case 0x169a: // steel wall (left/bottom)
5744 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5747 case 0x169b: // steel wall (right/bottom)
5748 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5751 case 0x169c: // steel wall (right/top)
5752 element = EL_DC_STEELWALL_1_TOPRIGHT;
5755 case 0x169d: // steel wall (left/top)
5756 element = EL_DC_STEELWALL_1_TOPLEFT;
5759 case 0x169e: // steel wall (right/bottom small)
5760 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5763 case 0x169f: // steel wall (left/bottom small)
5764 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5767 case 0x16a0: // steel wall (right/top small)
5768 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5771 case 0x16a1: // steel wall (left/top small)
5772 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5775 case 0x16a2: // steel wall (left/right)
5776 element = EL_DC_STEELWALL_1_VERTICAL;
5779 case 0x16a3: // steel wall (top/bottom)
5780 element = EL_DC_STEELWALL_1_HORIZONTAL;
5783 case 0x16a4: // steel wall 2 (left end)
5784 element = EL_DC_STEELWALL_2_LEFT;
5787 case 0x16a5: // steel wall 2 (right end)
5788 element = EL_DC_STEELWALL_2_RIGHT;
5791 case 0x16a6: // steel wall 2 (top end)
5792 element = EL_DC_STEELWALL_2_TOP;
5795 case 0x16a7: // steel wall 2 (bottom end)
5796 element = EL_DC_STEELWALL_2_BOTTOM;
5799 case 0x16a8: // steel wall 2 (left/right)
5800 element = EL_DC_STEELWALL_2_HORIZONTAL;
5803 case 0x16a9: // steel wall 2 (up/down)
5804 element = EL_DC_STEELWALL_2_VERTICAL;
5807 case 0x16aa: // steel wall 2 (mid)
5808 element = EL_DC_STEELWALL_2_MIDDLE;
5812 element = EL_SIGN_EXCLAMATION;
5816 element = EL_SIGN_RADIOACTIVITY;
5820 element = EL_SIGN_STOP;
5824 element = EL_SIGN_WHEELCHAIR;
5828 element = EL_SIGN_PARKING;
5832 element = EL_SIGN_NO_ENTRY;
5836 element = EL_SIGN_HEART;
5840 element = EL_SIGN_GIVE_WAY;
5844 element = EL_SIGN_ENTRY_FORBIDDEN;
5848 element = EL_SIGN_EMERGENCY_EXIT;
5852 element = EL_SIGN_YIN_YANG;
5856 element = EL_WALL_EMERALD;
5860 element = EL_WALL_DIAMOND;
5864 element = EL_WALL_PEARL;
5868 element = EL_WALL_CRYSTAL;
5872 element = EL_INVISIBLE_WALL;
5876 element = EL_INVISIBLE_STEELWALL;
5880 // EL_INVISIBLE_SAND
5883 element = EL_LIGHT_SWITCH;
5887 element = EL_ENVELOPE_1;
5891 if (element >= 0x0117 && element <= 0x036e) // (?)
5892 element = EL_DIAMOND;
5893 else if (element >= 0x042d && element <= 0x0684) // (?)
5894 element = EL_EMERALD;
5895 else if (element >= 0x157c && element <= 0x158b)
5897 else if (element >= 0x1590 && element <= 0x159f)
5898 element = EL_DC_LANDMINE;
5899 else if (element >= 0x16bc && element <= 0x16cb)
5900 element = EL_INVISIBLE_SAND;
5903 Warn("unknown Diamond Caves element 0x%04x", element);
5905 element = EL_UNKNOWN;
5910 return getMappedElement(element);
5913 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5915 byte header[DC_LEVEL_HEADER_SIZE];
5917 int envelope_header_pos = 62;
5918 int envelope_content_pos = 94;
5919 int level_name_pos = 251;
5920 int level_author_pos = 292;
5921 int envelope_header_len;
5922 int envelope_content_len;
5924 int level_author_len;
5926 int num_yamyam_contents;
5929 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5931 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5933 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5935 header[i * 2 + 0] = header_word >> 8;
5936 header[i * 2 + 1] = header_word & 0xff;
5939 // read some values from level header to check level decoding integrity
5940 fieldx = header[6] | (header[7] << 8);
5941 fieldy = header[8] | (header[9] << 8);
5942 num_yamyam_contents = header[60] | (header[61] << 8);
5944 // do some simple sanity checks to ensure that level was correctly decoded
5945 if (fieldx < 1 || fieldx > 256 ||
5946 fieldy < 1 || fieldy > 256 ||
5947 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5949 level->no_valid_file = TRUE;
5951 Warn("cannot decode level from stream -- using empty level");
5956 // maximum envelope header size is 31 bytes
5957 envelope_header_len = header[envelope_header_pos];
5958 // maximum envelope content size is 110 (156?) bytes
5959 envelope_content_len = header[envelope_content_pos];
5961 // maximum level title size is 40 bytes
5962 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5963 // maximum level author size is 30 (51?) bytes
5964 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5968 for (i = 0; i < envelope_header_len; i++)
5969 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5970 level->envelope[0].text[envelope_size++] =
5971 header[envelope_header_pos + 1 + i];
5973 if (envelope_header_len > 0 && envelope_content_len > 0)
5975 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5976 level->envelope[0].text[envelope_size++] = '\n';
5977 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5978 level->envelope[0].text[envelope_size++] = '\n';
5981 for (i = 0; i < envelope_content_len; i++)
5982 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5983 level->envelope[0].text[envelope_size++] =
5984 header[envelope_content_pos + 1 + i];
5986 level->envelope[0].text[envelope_size] = '\0';
5988 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5989 level->envelope[0].ysize = 10;
5990 level->envelope[0].autowrap = TRUE;
5991 level->envelope[0].centered = TRUE;
5993 for (i = 0; i < level_name_len; i++)
5994 level->name[i] = header[level_name_pos + 1 + i];
5995 level->name[level_name_len] = '\0';
5997 for (i = 0; i < level_author_len; i++)
5998 level->author[i] = header[level_author_pos + 1 + i];
5999 level->author[level_author_len] = '\0';
6001 num_yamyam_contents = header[60] | (header[61] << 8);
6002 level->num_yamyam_contents =
6003 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6005 for (i = 0; i < num_yamyam_contents; i++)
6007 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6009 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6010 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6012 if (i < MAX_ELEMENT_CONTENTS)
6013 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6017 fieldx = header[6] | (header[7] << 8);
6018 fieldy = header[8] | (header[9] << 8);
6019 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6020 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6022 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6024 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6025 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6027 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6028 level->field[x][y] = getMappedElement_DC(element_dc);
6031 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6032 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6033 level->field[x][y] = EL_PLAYER_1;
6035 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6036 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6037 level->field[x][y] = EL_PLAYER_2;
6039 level->gems_needed = header[18] | (header[19] << 8);
6041 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6042 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6043 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6044 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6045 level->score[SC_NUT] = header[28] | (header[29] << 8);
6046 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6047 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6048 level->score[SC_BUG] = header[34] | (header[35] << 8);
6049 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6050 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6051 level->score[SC_KEY] = header[40] | (header[41] << 8);
6052 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6054 level->time = header[44] | (header[45] << 8);
6056 level->amoeba_speed = header[46] | (header[47] << 8);
6057 level->time_light = header[48] | (header[49] << 8);
6058 level->time_timegate = header[50] | (header[51] << 8);
6059 level->time_wheel = header[52] | (header[53] << 8);
6060 level->time_magic_wall = header[54] | (header[55] << 8);
6061 level->extra_time = header[56] | (header[57] << 8);
6062 level->shield_normal_time = header[58] | (header[59] << 8);
6064 // shield and extra time elements do not have a score
6065 level->score[SC_SHIELD] = 0;
6066 level->extra_time_score = 0;
6068 // set time for normal and deadly shields to the same value
6069 level->shield_deadly_time = level->shield_normal_time;
6071 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6072 // can slip down from flat walls, like normal walls and steel walls
6073 level->em_slippery_gems = TRUE;
6075 // time score is counted for each 10 seconds left in Diamond Caves levels
6076 level->time_score_base = 10;
6079 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6080 struct LevelFileInfo *level_file_info,
6081 boolean level_info_only)
6083 char *filename = level_file_info->filename;
6085 int num_magic_bytes = 8;
6086 char magic_bytes[num_magic_bytes + 1];
6087 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6089 if (!(file = openFile(filename, MODE_READ)))
6091 level->no_valid_file = TRUE;
6093 if (!level_info_only)
6094 Warn("cannot read level '%s' -- using empty level", filename);
6099 // fseek(file, 0x0000, SEEK_SET);
6101 if (level_file_info->packed)
6103 // read "magic bytes" from start of file
6104 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6105 magic_bytes[0] = '\0';
6107 // check "magic bytes" for correct file format
6108 if (!strPrefix(magic_bytes, "DC2"))
6110 level->no_valid_file = TRUE;
6112 Warn("unknown DC level file '%s' -- using empty level", filename);
6117 if (strPrefix(magic_bytes, "DC2Win95") ||
6118 strPrefix(magic_bytes, "DC2Win98"))
6120 int position_first_level = 0x00fa;
6121 int extra_bytes = 4;
6124 // advance file stream to first level inside the level package
6125 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6127 // each block of level data is followed by block of non-level data
6128 num_levels_to_skip *= 2;
6130 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6131 while (num_levels_to_skip >= 0)
6133 // advance file stream to next level inside the level package
6134 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6136 level->no_valid_file = TRUE;
6138 Warn("cannot fseek in file '%s' -- using empty level", filename);
6143 // skip apparently unused extra bytes following each level
6144 ReadUnusedBytesFromFile(file, extra_bytes);
6146 // read size of next level in level package
6147 skip_bytes = getFile32BitLE(file);
6149 num_levels_to_skip--;
6154 level->no_valid_file = TRUE;
6156 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6162 LoadLevelFromFileStream_DC(file, level);
6168 // ----------------------------------------------------------------------------
6169 // functions for loading SB level
6170 // ----------------------------------------------------------------------------
6172 int getMappedElement_SB(int element_ascii, boolean use_ces)
6180 sb_element_mapping[] =
6182 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6183 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6184 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6185 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6186 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6187 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6188 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6189 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6196 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6197 if (element_ascii == sb_element_mapping[i].ascii)
6198 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6200 return EL_UNDEFINED;
6203 static void SetLevelSettings_SB(struct LevelInfo *level)
6207 level->use_step_counter = TRUE;
6210 level->score[SC_TIME_BONUS] = 0;
6211 level->time_score_base = 1;
6212 level->rate_time_over_score = TRUE;
6215 level->auto_exit_sokoban = TRUE;
6218 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6219 struct LevelFileInfo *level_file_info,
6220 boolean level_info_only)
6222 char *filename = level_file_info->filename;
6223 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6224 char last_comment[MAX_LINE_LEN];
6225 char level_name[MAX_LINE_LEN];
6228 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6229 boolean read_continued_line = FALSE;
6230 boolean reading_playfield = FALSE;
6231 boolean got_valid_playfield_line = FALSE;
6232 boolean invalid_playfield_char = FALSE;
6233 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6234 int file_level_nr = 0;
6235 int x = 0, y = 0; // initialized to make compilers happy
6237 last_comment[0] = '\0';
6238 level_name[0] = '\0';
6240 if (!(file = openFile(filename, MODE_READ)))
6242 level->no_valid_file = TRUE;
6244 if (!level_info_only)
6245 Warn("cannot read level '%s' -- using empty level", filename);
6250 while (!checkEndOfFile(file))
6252 // level successfully read, but next level may follow here
6253 if (!got_valid_playfield_line && reading_playfield)
6255 // read playfield from single level file -- skip remaining file
6256 if (!level_file_info->packed)
6259 if (file_level_nr >= num_levels_to_skip)
6264 last_comment[0] = '\0';
6265 level_name[0] = '\0';
6267 reading_playfield = FALSE;
6270 got_valid_playfield_line = FALSE;
6272 // read next line of input file
6273 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6276 // cut trailing line break (this can be newline and/or carriage return)
6277 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6278 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6281 // copy raw input line for later use (mainly debugging output)
6282 strcpy(line_raw, line);
6284 if (read_continued_line)
6286 // append new line to existing line, if there is enough space
6287 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6288 strcat(previous_line, line_ptr);
6290 strcpy(line, previous_line); // copy storage buffer to line
6292 read_continued_line = FALSE;
6295 // if the last character is '\', continue at next line
6296 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6298 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6299 strcpy(previous_line, line); // copy line to storage buffer
6301 read_continued_line = TRUE;
6307 if (line[0] == '\0')
6310 // extract comment text from comment line
6313 for (line_ptr = line; *line_ptr; line_ptr++)
6314 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6317 strcpy(last_comment, line_ptr);
6322 // extract level title text from line containing level title
6323 if (line[0] == '\'')
6325 strcpy(level_name, &line[1]);
6327 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6328 level_name[strlen(level_name) - 1] = '\0';
6333 // skip lines containing only spaces (or empty lines)
6334 for (line_ptr = line; *line_ptr; line_ptr++)
6335 if (*line_ptr != ' ')
6337 if (*line_ptr == '\0')
6340 // at this point, we have found a line containing part of a playfield
6342 got_valid_playfield_line = TRUE;
6344 if (!reading_playfield)
6346 reading_playfield = TRUE;
6347 invalid_playfield_char = FALSE;
6349 for (x = 0; x < MAX_LEV_FIELDX; x++)
6350 for (y = 0; y < MAX_LEV_FIELDY; y++)
6351 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6356 // start with topmost tile row
6360 // skip playfield line if larger row than allowed
6361 if (y >= MAX_LEV_FIELDY)
6364 // start with leftmost tile column
6367 // read playfield elements from line
6368 for (line_ptr = line; *line_ptr; line_ptr++)
6370 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6372 // stop parsing playfield line if larger column than allowed
6373 if (x >= MAX_LEV_FIELDX)
6376 if (mapped_sb_element == EL_UNDEFINED)
6378 invalid_playfield_char = TRUE;
6383 level->field[x][y] = mapped_sb_element;
6385 // continue with next tile column
6388 level->fieldx = MAX(x, level->fieldx);
6391 if (invalid_playfield_char)
6393 // if first playfield line, treat invalid lines as comment lines
6395 reading_playfield = FALSE;
6400 // continue with next tile row
6408 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6409 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6411 if (!reading_playfield)
6413 level->no_valid_file = TRUE;
6415 Warn("cannot read level '%s' -- using empty level", filename);
6420 if (*level_name != '\0')
6422 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6423 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6425 else if (*last_comment != '\0')
6427 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6428 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6432 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6435 // set all empty fields beyond the border walls to invisible steel wall
6436 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6438 if ((x == 0 || x == level->fieldx - 1 ||
6439 y == 0 || y == level->fieldy - 1) &&
6440 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6441 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6442 level->field, level->fieldx, level->fieldy);
6445 // set special level settings for Sokoban levels
6446 SetLevelSettings_SB(level);
6448 if (load_xsb_to_ces)
6450 // special global settings can now be set in level template
6451 level->use_custom_template = TRUE;
6456 // -------------------------------------------------------------------------
6457 // functions for handling native levels
6458 // -------------------------------------------------------------------------
6460 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6461 struct LevelFileInfo *level_file_info,
6462 boolean level_info_only)
6466 // determine position of requested level inside level package
6467 if (level_file_info->packed)
6468 pos = level_file_info->nr - leveldir_current->first_level;
6470 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6471 level->no_valid_file = TRUE;
6474 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6475 struct LevelFileInfo *level_file_info,
6476 boolean level_info_only)
6478 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6479 level->no_valid_file = TRUE;
6482 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6483 struct LevelFileInfo *level_file_info,
6484 boolean level_info_only)
6488 // determine position of requested level inside level package
6489 if (level_file_info->packed)
6490 pos = level_file_info->nr - leveldir_current->first_level;
6492 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6493 level->no_valid_file = TRUE;
6496 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6497 struct LevelFileInfo *level_file_info,
6498 boolean level_info_only)
6500 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6501 level->no_valid_file = TRUE;
6504 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6506 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6507 CopyNativeLevel_RND_to_BD(level);
6508 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6509 CopyNativeLevel_RND_to_EM(level);
6510 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6511 CopyNativeLevel_RND_to_SP(level);
6512 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6513 CopyNativeLevel_RND_to_MM(level);
6516 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6518 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6519 CopyNativeLevel_BD_to_RND(level);
6520 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6521 CopyNativeLevel_EM_to_RND(level);
6522 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6523 CopyNativeLevel_SP_to_RND(level);
6524 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6525 CopyNativeLevel_MM_to_RND(level);
6528 void SaveNativeLevel(struct LevelInfo *level)
6530 // saving native level files only supported for some game engines
6531 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6532 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6535 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6536 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6537 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6538 char *filename = getLevelFilenameFromBasename(basename);
6540 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6543 boolean success = FALSE;
6545 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6547 CopyNativeLevel_RND_to_BD(level);
6548 // CopyNativeTape_RND_to_BD(level);
6550 success = SaveNativeLevel_BD(filename);
6552 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6554 CopyNativeLevel_RND_to_SP(level);
6555 CopyNativeTape_RND_to_SP(level);
6557 success = SaveNativeLevel_SP(filename);
6561 Request("Native level file saved!", REQ_CONFIRM);
6563 Request("Failed to save native level file!", REQ_CONFIRM);
6567 // ----------------------------------------------------------------------------
6568 // functions for loading generic level
6569 // ----------------------------------------------------------------------------
6571 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6572 struct LevelFileInfo *level_file_info,
6573 boolean level_info_only)
6575 // always start with reliable default values
6576 setLevelInfoToDefaults(level, level_info_only, TRUE);
6578 switch (level_file_info->type)
6580 case LEVEL_FILE_TYPE_RND:
6581 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6584 case LEVEL_FILE_TYPE_BD:
6585 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6586 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6589 case LEVEL_FILE_TYPE_EM:
6590 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6591 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6594 case LEVEL_FILE_TYPE_SP:
6595 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6596 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6599 case LEVEL_FILE_TYPE_MM:
6600 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6601 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6604 case LEVEL_FILE_TYPE_DC:
6605 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6608 case LEVEL_FILE_TYPE_SB:
6609 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6613 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6617 // if level file is invalid, restore level structure to default values
6618 if (level->no_valid_file)
6619 setLevelInfoToDefaults(level, level_info_only, FALSE);
6621 if (check_special_flags("use_native_bd_game_engine"))
6622 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6624 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6625 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6627 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6628 CopyNativeLevel_Native_to_RND(level);
6631 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6633 static struct LevelFileInfo level_file_info;
6635 // always start with reliable default values
6636 setFileInfoToDefaults(&level_file_info);
6638 level_file_info.nr = 0; // unknown level number
6639 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6641 setString(&level_file_info.filename, filename);
6643 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6646 static void LoadLevel_InitVersion(struct LevelInfo *level)
6650 if (leveldir_current == NULL) // only when dumping level
6653 // all engine modifications also valid for levels which use latest engine
6654 if (level->game_version < VERSION_IDENT(3,2,0,5))
6656 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6657 level->time_score_base = 10;
6660 if (leveldir_current->latest_engine)
6662 // ---------- use latest game engine --------------------------------------
6664 /* For all levels which are forced to use the latest game engine version
6665 (normally all but user contributed, private and undefined levels), set
6666 the game engine version to the actual version; this allows for actual
6667 corrections in the game engine to take effect for existing, converted
6668 levels (from "classic" or other existing games) to make the emulation
6669 of the corresponding game more accurate, while (hopefully) not breaking
6670 existing levels created from other players. */
6672 level->game_version = GAME_VERSION_ACTUAL;
6674 /* Set special EM style gems behaviour: EM style gems slip down from
6675 normal, steel and growing wall. As this is a more fundamental change,
6676 it seems better to set the default behaviour to "off" (as it is more
6677 natural) and make it configurable in the level editor (as a property
6678 of gem style elements). Already existing converted levels (neither
6679 private nor contributed levels) are changed to the new behaviour. */
6681 if (level->file_version < FILE_VERSION_2_0)
6682 level->em_slippery_gems = TRUE;
6687 // ---------- use game engine the level was created with --------------------
6689 /* For all levels which are not forced to use the latest game engine
6690 version (normally user contributed, private and undefined levels),
6691 use the version of the game engine the levels were created for.
6693 Since 2.0.1, the game engine version is now directly stored
6694 in the level file (chunk "VERS"), so there is no need anymore
6695 to set the game version from the file version (except for old,
6696 pre-2.0 levels, where the game version is still taken from the
6697 file format version used to store the level -- see above). */
6699 // player was faster than enemies in 1.0.0 and before
6700 if (level->file_version == FILE_VERSION_1_0)
6701 for (i = 0; i < MAX_PLAYERS; i++)
6702 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6704 // default behaviour for EM style gems was "slippery" only in 2.0.1
6705 if (level->game_version == VERSION_IDENT(2,0,1,0))
6706 level->em_slippery_gems = TRUE;
6708 // springs could be pushed over pits before (pre-release version) 2.2.0
6709 if (level->game_version < VERSION_IDENT(2,2,0,0))
6710 level->use_spring_bug = TRUE;
6712 if (level->game_version < VERSION_IDENT(3,2,0,5))
6714 // time orb caused limited time in endless time levels before 3.2.0-5
6715 level->use_time_orb_bug = TRUE;
6717 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6718 level->block_snap_field = FALSE;
6720 // extra time score was same value as time left score before 3.2.0-5
6721 level->extra_time_score = level->score[SC_TIME_BONUS];
6724 if (level->game_version < VERSION_IDENT(3,2,0,7))
6726 // default behaviour for snapping was "not continuous" before 3.2.0-7
6727 level->continuous_snapping = FALSE;
6730 // only few elements were able to actively move into acid before 3.1.0
6731 // trigger settings did not exist before 3.1.0; set to default "any"
6732 if (level->game_version < VERSION_IDENT(3,1,0,0))
6734 // correct "can move into acid" settings (all zero in old levels)
6736 level->can_move_into_acid_bits = 0; // nothing can move into acid
6737 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6739 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6740 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6741 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6742 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6744 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6745 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6747 // correct trigger settings (stored as zero == "none" in old levels)
6749 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6751 int element = EL_CUSTOM_START + i;
6752 struct ElementInfo *ei = &element_info[element];
6754 for (j = 0; j < ei->num_change_pages; j++)
6756 struct ElementChangeInfo *change = &ei->change_page[j];
6758 change->trigger_player = CH_PLAYER_ANY;
6759 change->trigger_page = CH_PAGE_ANY;
6764 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6766 int element = EL_CUSTOM_256;
6767 struct ElementInfo *ei = &element_info[element];
6768 struct ElementChangeInfo *change = &ei->change_page[0];
6770 /* This is needed to fix a problem that was caused by a bugfix in function
6771 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6772 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6773 not replace walkable elements, but instead just placed the player on it,
6774 without placing the Sokoban field under the player). Unfortunately, this
6775 breaks "Snake Bite" style levels when the snake is halfway through a door
6776 that just closes (the snake head is still alive and can be moved in this
6777 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6778 player (without Sokoban element) which then gets killed as designed). */
6780 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6781 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6782 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6783 change->target_element = EL_PLAYER_1;
6786 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6787 if (level->game_version < VERSION_IDENT(3,2,5,0))
6789 /* This is needed to fix a problem that was caused by a bugfix in function
6790 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6791 corrects the behaviour when a custom element changes to another custom
6792 element with a higher element number that has change actions defined.
6793 Normally, only one change per frame is allowed for custom elements.
6794 Therefore, it is checked if a custom element already changed in the
6795 current frame; if it did, subsequent changes are suppressed.
6796 Unfortunately, this is only checked for element changes, but not for
6797 change actions, which are still executed. As the function above loops
6798 through all custom elements from lower to higher, an element change
6799 resulting in a lower CE number won't be checked again, while a target
6800 element with a higher number will also be checked, and potential change
6801 actions will get executed for this CE, too (which is wrong), while
6802 further changes are ignored (which is correct). As this bugfix breaks
6803 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6804 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6805 behaviour for existing levels and tapes that make use of this bug */
6807 level->use_action_after_change_bug = TRUE;
6810 // not centering level after relocating player was default only in 3.2.3
6811 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6812 level->shifted_relocation = TRUE;
6814 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6815 if (level->game_version < VERSION_IDENT(3,2,6,0))
6816 level->em_explodes_by_fire = TRUE;
6818 // levels were solved by the first player entering an exit up to 4.1.0.0
6819 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6820 level->solved_by_one_player = TRUE;
6822 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6823 if (level->game_version < VERSION_IDENT(4,1,1,1))
6824 level->use_life_bugs = TRUE;
6826 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6827 if (level->game_version < VERSION_IDENT(4,1,1,1))
6828 level->sb_objects_needed = FALSE;
6830 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6831 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6832 level->finish_dig_collect = FALSE;
6834 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6835 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6836 level->keep_walkable_ce = TRUE;
6839 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6841 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6844 // check if this level is (not) a Sokoban level
6845 for (y = 0; y < level->fieldy; y++)
6846 for (x = 0; x < level->fieldx; x++)
6847 if (!IS_SB_ELEMENT(Tile[x][y]))
6848 is_sokoban_level = FALSE;
6850 if (is_sokoban_level)
6852 // set special level settings for Sokoban levels
6853 SetLevelSettings_SB(level);
6857 static void LoadLevel_InitSettings(struct LevelInfo *level)
6859 // adjust level settings for (non-native) Sokoban-style levels
6860 LoadLevel_InitSettings_SB(level);
6862 // rename levels with title "nameless level" or if renaming is forced
6863 if (leveldir_current->empty_level_name != NULL &&
6864 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6865 leveldir_current->force_level_name))
6866 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6867 leveldir_current->empty_level_name, level_nr);
6870 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6874 // map elements that have changed in newer versions
6875 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6876 level->game_version);
6877 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6878 for (x = 0; x < 3; x++)
6879 for (y = 0; y < 3; y++)
6880 level->yamyam_content[i].e[x][y] =
6881 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6882 level->game_version);
6886 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6890 // map custom element change events that have changed in newer versions
6891 // (these following values were accidentally changed in version 3.0.1)
6892 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6893 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6895 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6897 int element = EL_CUSTOM_START + i;
6899 // order of checking and copying events to be mapped is important
6900 // (do not change the start and end value -- they are constant)
6901 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6903 if (HAS_CHANGE_EVENT(element, j - 2))
6905 SET_CHANGE_EVENT(element, j - 2, FALSE);
6906 SET_CHANGE_EVENT(element, j, TRUE);
6910 // order of checking and copying events to be mapped is important
6911 // (do not change the start and end value -- they are constant)
6912 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6914 if (HAS_CHANGE_EVENT(element, j - 1))
6916 SET_CHANGE_EVENT(element, j - 1, FALSE);
6917 SET_CHANGE_EVENT(element, j, TRUE);
6923 // initialize "can_change" field for old levels with only one change page
6924 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6926 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6928 int element = EL_CUSTOM_START + i;
6930 if (CAN_CHANGE(element))
6931 element_info[element].change->can_change = TRUE;
6935 // correct custom element values (for old levels without these options)
6936 if (level->game_version < VERSION_IDENT(3,1,1,0))
6938 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6940 int element = EL_CUSTOM_START + i;
6941 struct ElementInfo *ei = &element_info[element];
6943 if (ei->access_direction == MV_NO_DIRECTION)
6944 ei->access_direction = MV_ALL_DIRECTIONS;
6948 // correct custom element values (fix invalid values for all versions)
6951 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6953 int element = EL_CUSTOM_START + i;
6954 struct ElementInfo *ei = &element_info[element];
6956 for (j = 0; j < ei->num_change_pages; j++)
6958 struct ElementChangeInfo *change = &ei->change_page[j];
6960 if (change->trigger_player == CH_PLAYER_NONE)
6961 change->trigger_player = CH_PLAYER_ANY;
6963 if (change->trigger_side == CH_SIDE_NONE)
6964 change->trigger_side = CH_SIDE_ANY;
6969 // initialize "can_explode" field for old levels which did not store this
6970 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6971 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6973 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6975 int element = EL_CUSTOM_START + i;
6977 if (EXPLODES_1X1_OLD(element))
6978 element_info[element].explosion_type = EXPLODES_1X1;
6980 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6981 EXPLODES_SMASHED(element) ||
6982 EXPLODES_IMPACT(element)));
6986 // correct previously hard-coded move delay values for maze runner style
6987 if (level->game_version < VERSION_IDENT(3,1,1,0))
6989 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6991 int element = EL_CUSTOM_START + i;
6993 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6995 // previously hard-coded and therefore ignored
6996 element_info[element].move_delay_fixed = 9;
6997 element_info[element].move_delay_random = 0;
7002 // set some other uninitialized values of custom elements in older levels
7003 if (level->game_version < VERSION_IDENT(3,1,0,0))
7005 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7007 int element = EL_CUSTOM_START + i;
7009 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7011 element_info[element].explosion_delay = 17;
7012 element_info[element].ignition_delay = 8;
7016 // set mouse click change events to work for left/middle/right mouse button
7017 if (level->game_version < VERSION_IDENT(4,2,3,0))
7019 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7021 int element = EL_CUSTOM_START + i;
7022 struct ElementInfo *ei = &element_info[element];
7024 for (j = 0; j < ei->num_change_pages; j++)
7026 struct ElementChangeInfo *change = &ei->change_page[j];
7028 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7029 change->has_event[CE_PRESSED_BY_MOUSE] ||
7030 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7031 change->has_event[CE_MOUSE_PRESSED_ON_X])
7032 change->trigger_side = CH_SIDE_ANY;
7038 static void LoadLevel_InitElements(struct LevelInfo *level)
7040 LoadLevel_InitStandardElements(level);
7042 if (level->file_has_custom_elements)
7043 LoadLevel_InitCustomElements(level);
7045 // initialize element properties for level editor etc.
7046 InitElementPropertiesEngine(level->game_version);
7047 InitElementPropertiesGfxElement();
7050 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7054 // map elements that have changed in newer versions
7055 for (y = 0; y < level->fieldy; y++)
7056 for (x = 0; x < level->fieldx; x++)
7057 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7058 level->game_version);
7060 // clear unused playfield data (nicer if level gets resized in editor)
7061 for (x = 0; x < MAX_LEV_FIELDX; x++)
7062 for (y = 0; y < MAX_LEV_FIELDY; y++)
7063 if (x >= level->fieldx || y >= level->fieldy)
7064 level->field[x][y] = EL_EMPTY;
7066 // copy elements to runtime playfield array
7067 for (x = 0; x < MAX_LEV_FIELDX; x++)
7068 for (y = 0; y < MAX_LEV_FIELDY; y++)
7069 Tile[x][y] = level->field[x][y];
7071 // initialize level size variables for faster access
7072 lev_fieldx = level->fieldx;
7073 lev_fieldy = level->fieldy;
7075 // determine border element for this level
7076 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7077 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7082 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7084 struct LevelFileInfo *level_file_info = &level->file_info;
7086 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7087 CopyNativeLevel_RND_to_Native(level);
7090 static void LoadLevelTemplate_LoadAndInit(void)
7092 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7094 LoadLevel_InitVersion(&level_template);
7095 LoadLevel_InitElements(&level_template);
7096 LoadLevel_InitSettings(&level_template);
7098 ActivateLevelTemplate();
7101 void LoadLevelTemplate(int nr)
7103 if (!fileExists(getGlobalLevelTemplateFilename()))
7105 Warn("no level template found for this level");
7110 setLevelFileInfo(&level_template.file_info, nr);
7112 LoadLevelTemplate_LoadAndInit();
7115 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7117 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7119 LoadLevelTemplate_LoadAndInit();
7122 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7124 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7126 if (level.use_custom_template)
7128 if (network_level != NULL)
7129 LoadNetworkLevelTemplate(network_level);
7131 LoadLevelTemplate(-1);
7134 LoadLevel_InitVersion(&level);
7135 LoadLevel_InitElements(&level);
7136 LoadLevel_InitPlayfield(&level);
7137 LoadLevel_InitSettings(&level);
7139 LoadLevel_InitNativeEngines(&level);
7142 void LoadLevel(int nr)
7144 SetLevelSetInfo(leveldir_current->identifier, nr);
7146 setLevelFileInfo(&level.file_info, nr);
7148 LoadLevel_LoadAndInit(NULL);
7151 void LoadLevelInfoOnly(int nr)
7153 setLevelFileInfo(&level.file_info, nr);
7155 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7158 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7160 SetLevelSetInfo(network_level->leveldir_identifier,
7161 network_level->file_info.nr);
7163 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7165 LoadLevel_LoadAndInit(network_level);
7168 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7172 chunk_size += putFileVersion(file, level->file_version);
7173 chunk_size += putFileVersion(file, level->game_version);
7178 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7182 chunk_size += putFile16BitBE(file, level->creation_date.year);
7183 chunk_size += putFile8Bit(file, level->creation_date.month);
7184 chunk_size += putFile8Bit(file, level->creation_date.day);
7189 #if ENABLE_HISTORIC_CHUNKS
7190 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7194 putFile8Bit(file, level->fieldx);
7195 putFile8Bit(file, level->fieldy);
7197 putFile16BitBE(file, level->time);
7198 putFile16BitBE(file, level->gems_needed);
7200 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7201 putFile8Bit(file, level->name[i]);
7203 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7204 putFile8Bit(file, level->score[i]);
7206 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7207 for (y = 0; y < 3; y++)
7208 for (x = 0; x < 3; x++)
7209 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7210 level->yamyam_content[i].e[x][y]));
7211 putFile8Bit(file, level->amoeba_speed);
7212 putFile8Bit(file, level->time_magic_wall);
7213 putFile8Bit(file, level->time_wheel);
7214 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7215 level->amoeba_content));
7216 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7217 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7218 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7219 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7221 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7223 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7224 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7225 putFile32BitBE(file, level->can_move_into_acid_bits);
7226 putFile8Bit(file, level->dont_collide_with_bits);
7228 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7229 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7231 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7232 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7233 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7235 putFile8Bit(file, level->game_engine_type);
7237 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7241 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7246 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7247 chunk_size += putFile8Bit(file, level->name[i]);
7252 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7257 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7258 chunk_size += putFile8Bit(file, level->author[i]);
7263 #if ENABLE_HISTORIC_CHUNKS
7264 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7269 for (y = 0; y < level->fieldy; y++)
7270 for (x = 0; x < level->fieldx; x++)
7271 if (level->encoding_16bit_field)
7272 chunk_size += putFile16BitBE(file, level->field[x][y]);
7274 chunk_size += putFile8Bit(file, level->field[x][y]);
7280 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7285 for (y = 0; y < level->fieldy; y++)
7286 for (x = 0; x < level->fieldx; x++)
7287 chunk_size += putFile16BitBE(file, level->field[x][y]);
7292 #if ENABLE_HISTORIC_CHUNKS
7293 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7297 putFile8Bit(file, EL_YAMYAM);
7298 putFile8Bit(file, level->num_yamyam_contents);
7299 putFile8Bit(file, 0);
7300 putFile8Bit(file, 0);
7302 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7303 for (y = 0; y < 3; y++)
7304 for (x = 0; x < 3; x++)
7305 if (level->encoding_16bit_field)
7306 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7308 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7312 #if ENABLE_HISTORIC_CHUNKS
7313 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7316 int num_contents, content_xsize, content_ysize;
7317 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7319 if (element == EL_YAMYAM)
7321 num_contents = level->num_yamyam_contents;
7325 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7326 for (y = 0; y < 3; y++)
7327 for (x = 0; x < 3; x++)
7328 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7330 else if (element == EL_BD_AMOEBA)
7336 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7337 for (y = 0; y < 3; y++)
7338 for (x = 0; x < 3; x++)
7339 content_array[i][x][y] = EL_EMPTY;
7340 content_array[0][0][0] = level->amoeba_content;
7344 // chunk header already written -- write empty chunk data
7345 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7347 Warn("cannot save content for element '%d'", element);
7352 putFile16BitBE(file, element);
7353 putFile8Bit(file, num_contents);
7354 putFile8Bit(file, content_xsize);
7355 putFile8Bit(file, content_ysize);
7357 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7359 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7360 for (y = 0; y < 3; y++)
7361 for (x = 0; x < 3; x++)
7362 putFile16BitBE(file, content_array[i][x][y]);
7366 #if ENABLE_HISTORIC_CHUNKS
7367 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7369 int envelope_nr = element - EL_ENVELOPE_1;
7370 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7374 chunk_size += putFile16BitBE(file, element);
7375 chunk_size += putFile16BitBE(file, envelope_len);
7376 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7377 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7379 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7380 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7382 for (i = 0; i < envelope_len; i++)
7383 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7389 #if ENABLE_HISTORIC_CHUNKS
7390 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7391 int num_changed_custom_elements)
7395 putFile16BitBE(file, num_changed_custom_elements);
7397 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7399 int element = EL_CUSTOM_START + i;
7401 struct ElementInfo *ei = &element_info[element];
7403 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7405 if (check < num_changed_custom_elements)
7407 putFile16BitBE(file, element);
7408 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7415 if (check != num_changed_custom_elements) // should not happen
7416 Warn("inconsistent number of custom element properties");
7420 #if ENABLE_HISTORIC_CHUNKS
7421 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7422 int num_changed_custom_elements)
7426 putFile16BitBE(file, num_changed_custom_elements);
7428 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7430 int element = EL_CUSTOM_START + i;
7432 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7434 if (check < num_changed_custom_elements)
7436 putFile16BitBE(file, element);
7437 putFile16BitBE(file, element_info[element].change->target_element);
7444 if (check != num_changed_custom_elements) // should not happen
7445 Warn("inconsistent number of custom target elements");
7449 #if ENABLE_HISTORIC_CHUNKS
7450 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7451 int num_changed_custom_elements)
7453 int i, j, x, y, check = 0;
7455 putFile16BitBE(file, num_changed_custom_elements);
7457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7459 int element = EL_CUSTOM_START + i;
7460 struct ElementInfo *ei = &element_info[element];
7462 if (ei->modified_settings)
7464 if (check < num_changed_custom_elements)
7466 putFile16BitBE(file, element);
7468 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7469 putFile8Bit(file, ei->description[j]);
7471 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7473 // some free bytes for future properties and padding
7474 WriteUnusedBytesToFile(file, 7);
7476 putFile8Bit(file, ei->use_gfx_element);
7477 putFile16BitBE(file, ei->gfx_element_initial);
7479 putFile8Bit(file, ei->collect_score_initial);
7480 putFile8Bit(file, ei->collect_count_initial);
7482 putFile16BitBE(file, ei->push_delay_fixed);
7483 putFile16BitBE(file, ei->push_delay_random);
7484 putFile16BitBE(file, ei->move_delay_fixed);
7485 putFile16BitBE(file, ei->move_delay_random);
7487 putFile16BitBE(file, ei->move_pattern);
7488 putFile8Bit(file, ei->move_direction_initial);
7489 putFile8Bit(file, ei->move_stepsize);
7491 for (y = 0; y < 3; y++)
7492 for (x = 0; x < 3; x++)
7493 putFile16BitBE(file, ei->content.e[x][y]);
7495 putFile32BitBE(file, ei->change->events);
7497 putFile16BitBE(file, ei->change->target_element);
7499 putFile16BitBE(file, ei->change->delay_fixed);
7500 putFile16BitBE(file, ei->change->delay_random);
7501 putFile16BitBE(file, ei->change->delay_frames);
7503 putFile16BitBE(file, ei->change->initial_trigger_element);
7505 putFile8Bit(file, ei->change->explode);
7506 putFile8Bit(file, ei->change->use_target_content);
7507 putFile8Bit(file, ei->change->only_if_complete);
7508 putFile8Bit(file, ei->change->use_random_replace);
7510 putFile8Bit(file, ei->change->random_percentage);
7511 putFile8Bit(file, ei->change->replace_when);
7513 for (y = 0; y < 3; y++)
7514 for (x = 0; x < 3; x++)
7515 putFile16BitBE(file, ei->change->content.e[x][y]);
7517 putFile8Bit(file, ei->slippery_type);
7519 // some free bytes for future properties and padding
7520 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7527 if (check != num_changed_custom_elements) // should not happen
7528 Warn("inconsistent number of custom element properties");
7532 #if ENABLE_HISTORIC_CHUNKS
7533 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7535 struct ElementInfo *ei = &element_info[element];
7538 // ---------- custom element base property values (96 bytes) ----------------
7540 putFile16BitBE(file, element);
7542 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7543 putFile8Bit(file, ei->description[i]);
7545 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7547 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7549 putFile8Bit(file, ei->num_change_pages);
7551 putFile16BitBE(file, ei->ce_value_fixed_initial);
7552 putFile16BitBE(file, ei->ce_value_random_initial);
7553 putFile8Bit(file, ei->use_last_ce_value);
7555 putFile8Bit(file, ei->use_gfx_element);
7556 putFile16BitBE(file, ei->gfx_element_initial);
7558 putFile8Bit(file, ei->collect_score_initial);
7559 putFile8Bit(file, ei->collect_count_initial);
7561 putFile8Bit(file, ei->drop_delay_fixed);
7562 putFile8Bit(file, ei->push_delay_fixed);
7563 putFile8Bit(file, ei->drop_delay_random);
7564 putFile8Bit(file, ei->push_delay_random);
7565 putFile16BitBE(file, ei->move_delay_fixed);
7566 putFile16BitBE(file, ei->move_delay_random);
7568 // bits 0 - 15 of "move_pattern" ...
7569 putFile16BitBE(file, ei->move_pattern & 0xffff);
7570 putFile8Bit(file, ei->move_direction_initial);
7571 putFile8Bit(file, ei->move_stepsize);
7573 putFile8Bit(file, ei->slippery_type);
7575 for (y = 0; y < 3; y++)
7576 for (x = 0; x < 3; x++)
7577 putFile16BitBE(file, ei->content.e[x][y]);
7579 putFile16BitBE(file, ei->move_enter_element);
7580 putFile16BitBE(file, ei->move_leave_element);
7581 putFile8Bit(file, ei->move_leave_type);
7583 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7584 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7586 putFile8Bit(file, ei->access_direction);
7588 putFile8Bit(file, ei->explosion_delay);
7589 putFile8Bit(file, ei->ignition_delay);
7590 putFile8Bit(file, ei->explosion_type);
7592 // some free bytes for future custom property values and padding
7593 WriteUnusedBytesToFile(file, 1);
7595 // ---------- change page property values (48 bytes) ------------------------
7597 for (i = 0; i < ei->num_change_pages; i++)
7599 struct ElementChangeInfo *change = &ei->change_page[i];
7600 unsigned int event_bits;
7602 // bits 0 - 31 of "has_event[]" ...
7604 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7605 if (change->has_event[j])
7606 event_bits |= (1u << j);
7607 putFile32BitBE(file, event_bits);
7609 putFile16BitBE(file, change->target_element);
7611 putFile16BitBE(file, change->delay_fixed);
7612 putFile16BitBE(file, change->delay_random);
7613 putFile16BitBE(file, change->delay_frames);
7615 putFile16BitBE(file, change->initial_trigger_element);
7617 putFile8Bit(file, change->explode);
7618 putFile8Bit(file, change->use_target_content);
7619 putFile8Bit(file, change->only_if_complete);
7620 putFile8Bit(file, change->use_random_replace);
7622 putFile8Bit(file, change->random_percentage);
7623 putFile8Bit(file, change->replace_when);
7625 for (y = 0; y < 3; y++)
7626 for (x = 0; x < 3; x++)
7627 putFile16BitBE(file, change->target_content.e[x][y]);
7629 putFile8Bit(file, change->can_change);
7631 putFile8Bit(file, change->trigger_side);
7633 putFile8Bit(file, change->trigger_player);
7634 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7635 log_2(change->trigger_page)));
7637 putFile8Bit(file, change->has_action);
7638 putFile8Bit(file, change->action_type);
7639 putFile8Bit(file, change->action_mode);
7640 putFile16BitBE(file, change->action_arg);
7642 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7644 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7645 if (change->has_event[j])
7646 event_bits |= (1u << (j - 32));
7647 putFile8Bit(file, event_bits);
7652 #if ENABLE_HISTORIC_CHUNKS
7653 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7655 struct ElementInfo *ei = &element_info[element];
7656 struct ElementGroupInfo *group = ei->group;
7659 putFile16BitBE(file, element);
7661 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7662 putFile8Bit(file, ei->description[i]);
7664 putFile8Bit(file, group->num_elements);
7666 putFile8Bit(file, ei->use_gfx_element);
7667 putFile16BitBE(file, ei->gfx_element_initial);
7669 putFile8Bit(file, group->choice_mode);
7671 // some free bytes for future values and padding
7672 WriteUnusedBytesToFile(file, 3);
7674 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7675 putFile16BitBE(file, group->element[i]);
7679 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7680 boolean write_element)
7682 int save_type = entry->save_type;
7683 int data_type = entry->data_type;
7684 int conf_type = entry->conf_type;
7685 int byte_mask = conf_type & CONF_MASK_BYTES;
7686 int element = entry->element;
7687 int default_value = entry->default_value;
7689 boolean modified = FALSE;
7691 if (byte_mask != CONF_MASK_MULTI_BYTES)
7693 void *value_ptr = entry->value;
7694 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7697 // check if any settings have been modified before saving them
7698 if (value != default_value)
7701 // do not save if explicitly told or if unmodified default settings
7702 if ((save_type == SAVE_CONF_NEVER) ||
7703 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7707 num_bytes += putFile16BitBE(file, element);
7709 num_bytes += putFile8Bit(file, conf_type);
7710 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7711 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7712 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7715 else if (data_type == TYPE_STRING)
7717 char *default_string = entry->default_string;
7718 char *string = (char *)(entry->value);
7719 int string_length = strlen(string);
7722 // check if any settings have been modified before saving them
7723 if (!strEqual(string, default_string))
7726 // do not save if explicitly told or if unmodified default settings
7727 if ((save_type == SAVE_CONF_NEVER) ||
7728 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7732 num_bytes += putFile16BitBE(file, element);
7734 num_bytes += putFile8Bit(file, conf_type);
7735 num_bytes += putFile16BitBE(file, string_length);
7737 for (i = 0; i < string_length; i++)
7738 num_bytes += putFile8Bit(file, string[i]);
7740 else if (data_type == TYPE_ELEMENT_LIST)
7742 int *element_array = (int *)(entry->value);
7743 int num_elements = *(int *)(entry->num_entities);
7746 // check if any settings have been modified before saving them
7747 for (i = 0; i < num_elements; i++)
7748 if (element_array[i] != default_value)
7751 // do not save if explicitly told or if unmodified default settings
7752 if ((save_type == SAVE_CONF_NEVER) ||
7753 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7757 num_bytes += putFile16BitBE(file, element);
7759 num_bytes += putFile8Bit(file, conf_type);
7760 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7762 for (i = 0; i < num_elements; i++)
7763 num_bytes += putFile16BitBE(file, element_array[i]);
7765 else if (data_type == TYPE_CONTENT_LIST)
7767 struct Content *content = (struct Content *)(entry->value);
7768 int num_contents = *(int *)(entry->num_entities);
7771 // check if any settings have been modified before saving them
7772 for (i = 0; i < num_contents; i++)
7773 for (y = 0; y < 3; y++)
7774 for (x = 0; x < 3; x++)
7775 if (content[i].e[x][y] != default_value)
7778 // do not save if explicitly told or if unmodified default settings
7779 if ((save_type == SAVE_CONF_NEVER) ||
7780 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7784 num_bytes += putFile16BitBE(file, element);
7786 num_bytes += putFile8Bit(file, conf_type);
7787 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7789 for (i = 0; i < num_contents; i++)
7790 for (y = 0; y < 3; y++)
7791 for (x = 0; x < 3; x++)
7792 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7798 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7803 li = *level; // copy level data into temporary buffer
7805 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7806 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7811 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7816 li = *level; // copy level data into temporary buffer
7818 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7819 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7824 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7826 int envelope_nr = element - EL_ENVELOPE_1;
7830 chunk_size += putFile16BitBE(file, element);
7832 // copy envelope data into temporary buffer
7833 xx_envelope = level->envelope[envelope_nr];
7835 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7836 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7841 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7843 struct ElementInfo *ei = &element_info[element];
7847 chunk_size += putFile16BitBE(file, element);
7849 xx_ei = *ei; // copy element data into temporary buffer
7851 // set default description string for this specific element
7852 strcpy(xx_default_description, getDefaultElementDescription(ei));
7854 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7855 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7857 for (i = 0; i < ei->num_change_pages; i++)
7859 struct ElementChangeInfo *change = &ei->change_page[i];
7861 xx_current_change_page = i;
7863 xx_change = *change; // copy change data into temporary buffer
7866 setEventBitsFromEventFlags(change);
7868 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7869 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7876 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7878 struct ElementInfo *ei = &element_info[element];
7879 struct ElementGroupInfo *group = ei->group;
7883 chunk_size += putFile16BitBE(file, element);
7885 xx_ei = *ei; // copy element data into temporary buffer
7886 xx_group = *group; // copy group data into temporary buffer
7888 // set default description string for this specific element
7889 strcpy(xx_default_description, getDefaultElementDescription(ei));
7891 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7892 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7897 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7899 struct ElementInfo *ei = &element_info[element];
7903 chunk_size += putFile16BitBE(file, element);
7905 xx_ei = *ei; // copy element data into temporary buffer
7907 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7908 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7913 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7914 boolean save_as_template)
7920 if (!(file = fopen(filename, MODE_WRITE)))
7922 Warn("cannot save level file '%s'", filename);
7927 level->file_version = FILE_VERSION_ACTUAL;
7928 level->game_version = GAME_VERSION_ACTUAL;
7930 level->creation_date = getCurrentDate();
7932 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7933 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7935 chunk_size = SaveLevel_VERS(NULL, level);
7936 putFileChunkBE(file, "VERS", chunk_size);
7937 SaveLevel_VERS(file, level);
7939 chunk_size = SaveLevel_DATE(NULL, level);
7940 putFileChunkBE(file, "DATE", chunk_size);
7941 SaveLevel_DATE(file, level);
7943 chunk_size = SaveLevel_NAME(NULL, level);
7944 putFileChunkBE(file, "NAME", chunk_size);
7945 SaveLevel_NAME(file, level);
7947 chunk_size = SaveLevel_AUTH(NULL, level);
7948 putFileChunkBE(file, "AUTH", chunk_size);
7949 SaveLevel_AUTH(file, level);
7951 chunk_size = SaveLevel_INFO(NULL, level);
7952 putFileChunkBE(file, "INFO", chunk_size);
7953 SaveLevel_INFO(file, level);
7955 chunk_size = SaveLevel_BODY(NULL, level);
7956 putFileChunkBE(file, "BODY", chunk_size);
7957 SaveLevel_BODY(file, level);
7959 chunk_size = SaveLevel_ELEM(NULL, level);
7960 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7962 putFileChunkBE(file, "ELEM", chunk_size);
7963 SaveLevel_ELEM(file, level);
7966 for (i = 0; i < NUM_ENVELOPES; i++)
7968 int element = EL_ENVELOPE_1 + i;
7970 chunk_size = SaveLevel_NOTE(NULL, level, element);
7971 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7973 putFileChunkBE(file, "NOTE", chunk_size);
7974 SaveLevel_NOTE(file, level, element);
7978 // if not using template level, check for non-default custom/group elements
7979 if (!level->use_custom_template || save_as_template)
7981 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7983 int element = EL_CUSTOM_START + i;
7985 chunk_size = SaveLevel_CUSX(NULL, level, element);
7986 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7988 putFileChunkBE(file, "CUSX", chunk_size);
7989 SaveLevel_CUSX(file, level, element);
7993 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7995 int element = EL_GROUP_START + i;
7997 chunk_size = SaveLevel_GRPX(NULL, level, element);
7998 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8000 putFileChunkBE(file, "GRPX", chunk_size);
8001 SaveLevel_GRPX(file, level, element);
8005 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8007 int element = GET_EMPTY_ELEMENT(i);
8009 chunk_size = SaveLevel_EMPX(NULL, level, element);
8010 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8012 putFileChunkBE(file, "EMPX", chunk_size);
8013 SaveLevel_EMPX(file, level, element);
8020 SetFilePermissions(filename, PERMS_PRIVATE);
8023 void SaveLevel(int nr)
8025 char *filename = getDefaultLevelFilename(nr);
8027 SaveLevelFromFilename(&level, filename, FALSE);
8030 void SaveLevelTemplate(void)
8032 char *filename = getLocalLevelTemplateFilename();
8034 SaveLevelFromFilename(&level, filename, TRUE);
8037 boolean SaveLevelChecked(int nr)
8039 char *filename = getDefaultLevelFilename(nr);
8040 boolean new_level = !fileExists(filename);
8041 boolean level_saved = FALSE;
8043 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8048 Request("Level saved!", REQ_CONFIRM);
8056 void DumpLevel(struct LevelInfo *level)
8058 if (level->no_level_file || level->no_valid_file)
8060 Warn("cannot dump -- no valid level file found");
8066 Print("Level xxx (file version %08d, game version %08d)\n",
8067 level->file_version, level->game_version);
8070 Print("Level author: '%s'\n", level->author);
8071 Print("Level title: '%s'\n", level->name);
8073 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8075 Print("Level time: %d seconds\n", level->time);
8076 Print("Gems needed: %d\n", level->gems_needed);
8078 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8079 Print("Time for wheel: %d seconds\n", level->time_wheel);
8080 Print("Time for light: %d seconds\n", level->time_light);
8081 Print("Time for timegate: %d seconds\n", level->time_timegate);
8083 Print("Amoeba speed: %d\n", level->amoeba_speed);
8086 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8087 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8088 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8089 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8090 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8091 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8097 for (i = 0; i < NUM_ENVELOPES; i++)
8099 char *text = level->envelope[i].text;
8100 int text_len = strlen(text);
8101 boolean has_text = FALSE;
8103 for (j = 0; j < text_len; j++)
8104 if (text[j] != ' ' && text[j] != '\n')
8110 Print("Envelope %d:\n'%s'\n", i + 1, text);
8118 void DumpLevels(void)
8120 static LevelDirTree *dumplevel_leveldir = NULL;
8122 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8123 global.dumplevel_leveldir);
8125 if (dumplevel_leveldir == NULL)
8126 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8128 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8129 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8130 Fail("no such level number: %d", global.dumplevel_level_nr);
8132 leveldir_current = dumplevel_leveldir;
8134 LoadLevel(global.dumplevel_level_nr);
8141 // ============================================================================
8142 // tape file functions
8143 // ============================================================================
8145 static void setTapeInfoToDefaults(void)
8149 // always start with reliable default values (empty tape)
8152 // default values (also for pre-1.2 tapes) with only the first player
8153 tape.player_participates[0] = TRUE;
8154 for (i = 1; i < MAX_PLAYERS; i++)
8155 tape.player_participates[i] = FALSE;
8157 // at least one (default: the first) player participates in every tape
8158 tape.num_participating_players = 1;
8160 tape.property_bits = TAPE_PROPERTY_NONE;
8162 tape.level_nr = level_nr;
8164 tape.changed = FALSE;
8165 tape.solved = FALSE;
8167 tape.recording = FALSE;
8168 tape.playing = FALSE;
8169 tape.pausing = FALSE;
8171 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8172 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8174 tape.no_info_chunk = TRUE;
8175 tape.no_valid_file = FALSE;
8178 static int getTapePosSize(struct TapeInfo *tape)
8180 int tape_pos_size = 0;
8182 if (tape->use_key_actions)
8183 tape_pos_size += tape->num_participating_players;
8185 if (tape->use_mouse_actions)
8186 tape_pos_size += 3; // x and y position and mouse button mask
8188 tape_pos_size += 1; // tape action delay value
8190 return tape_pos_size;
8193 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8195 tape->use_key_actions = FALSE;
8196 tape->use_mouse_actions = FALSE;
8198 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8199 tape->use_key_actions = TRUE;
8201 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8202 tape->use_mouse_actions = TRUE;
8205 static int getTapeActionValue(struct TapeInfo *tape)
8207 return (tape->use_key_actions &&
8208 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8209 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8210 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8211 TAPE_ACTIONS_DEFAULT);
8214 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8216 tape->file_version = getFileVersion(file);
8217 tape->game_version = getFileVersion(file);
8222 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8226 tape->random_seed = getFile32BitBE(file);
8227 tape->date = getFile32BitBE(file);
8228 tape->length = getFile32BitBE(file);
8230 // read header fields that are new since version 1.2
8231 if (tape->file_version >= FILE_VERSION_1_2)
8233 byte store_participating_players = getFile8Bit(file);
8236 // since version 1.2, tapes store which players participate in the tape
8237 tape->num_participating_players = 0;
8238 for (i = 0; i < MAX_PLAYERS; i++)
8240 tape->player_participates[i] = FALSE;
8242 if (store_participating_players & (1 << i))
8244 tape->player_participates[i] = TRUE;
8245 tape->num_participating_players++;
8249 setTapeActionFlags(tape, getFile8Bit(file));
8251 tape->property_bits = getFile8Bit(file);
8252 tape->solved = getFile8Bit(file);
8254 engine_version = getFileVersion(file);
8255 if (engine_version > 0)
8256 tape->engine_version = engine_version;
8258 tape->engine_version = tape->game_version;
8264 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8266 tape->scr_fieldx = getFile8Bit(file);
8267 tape->scr_fieldy = getFile8Bit(file);
8272 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8274 char *level_identifier = NULL;
8275 int level_identifier_size;
8278 tape->no_info_chunk = FALSE;
8280 level_identifier_size = getFile16BitBE(file);
8282 level_identifier = checked_malloc(level_identifier_size);
8284 for (i = 0; i < level_identifier_size; i++)
8285 level_identifier[i] = getFile8Bit(file);
8287 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8288 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8290 checked_free(level_identifier);
8292 tape->level_nr = getFile16BitBE(file);
8294 chunk_size = 2 + level_identifier_size + 2;
8299 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8302 int tape_pos_size = getTapePosSize(tape);
8303 int chunk_size_expected = tape_pos_size * tape->length;
8305 if (chunk_size_expected != chunk_size)
8307 ReadUnusedBytesFromFile(file, chunk_size);
8308 return chunk_size_expected;
8311 for (i = 0; i < tape->length; i++)
8313 if (i >= MAX_TAPE_LEN)
8315 Warn("tape truncated -- size exceeds maximum tape size %d",
8318 // tape too large; read and ignore remaining tape data from this chunk
8319 for (;i < tape->length; i++)
8320 ReadUnusedBytesFromFile(file, tape_pos_size);
8325 if (tape->use_key_actions)
8327 for (j = 0; j < MAX_PLAYERS; j++)
8329 tape->pos[i].action[j] = MV_NONE;
8331 if (tape->player_participates[j])
8332 tape->pos[i].action[j] = getFile8Bit(file);
8336 if (tape->use_mouse_actions)
8338 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8339 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8340 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8343 tape->pos[i].delay = getFile8Bit(file);
8345 if (tape->file_version == FILE_VERSION_1_0)
8347 // eliminate possible diagonal moves in old tapes
8348 // this is only for backward compatibility
8350 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8351 byte action = tape->pos[i].action[0];
8352 int k, num_moves = 0;
8354 for (k = 0; k < 4; k++)
8356 if (action & joy_dir[k])
8358 tape->pos[i + num_moves].action[0] = joy_dir[k];
8360 tape->pos[i + num_moves].delay = 0;
8369 tape->length += num_moves;
8372 else if (tape->file_version < FILE_VERSION_2_0)
8374 // convert pre-2.0 tapes to new tape format
8376 if (tape->pos[i].delay > 1)
8379 tape->pos[i + 1] = tape->pos[i];
8380 tape->pos[i + 1].delay = 1;
8383 for (j = 0; j < MAX_PLAYERS; j++)
8384 tape->pos[i].action[j] = MV_NONE;
8385 tape->pos[i].delay--;
8392 if (checkEndOfFile(file))
8396 if (i != tape->length)
8397 chunk_size = tape_pos_size * i;
8402 static void LoadTape_SokobanSolution(char *filename)
8405 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8407 if (!(file = openFile(filename, MODE_READ)))
8409 tape.no_valid_file = TRUE;
8414 while (!checkEndOfFile(file))
8416 unsigned char c = getByteFromFile(file);
8418 if (checkEndOfFile(file))
8425 tape.pos[tape.length].action[0] = MV_UP;
8426 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8432 tape.pos[tape.length].action[0] = MV_DOWN;
8433 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8439 tape.pos[tape.length].action[0] = MV_LEFT;
8440 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8446 tape.pos[tape.length].action[0] = MV_RIGHT;
8447 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8455 // ignore white-space characters
8459 tape.no_valid_file = TRUE;
8461 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8469 if (tape.no_valid_file)
8472 tape.length_frames = GetTapeLengthFrames();
8473 tape.length_seconds = GetTapeLengthSeconds();
8476 void LoadTapeFromFilename(char *filename)
8478 char cookie[MAX_LINE_LEN];
8479 char chunk_name[CHUNK_ID_LEN + 1];
8483 // always start with reliable default values
8484 setTapeInfoToDefaults();
8486 if (strSuffix(filename, ".sln"))
8488 LoadTape_SokobanSolution(filename);
8493 if (!(file = openFile(filename, MODE_READ)))
8495 tape.no_valid_file = TRUE;
8500 getFileChunkBE(file, chunk_name, NULL);
8501 if (strEqual(chunk_name, "RND1"))
8503 getFile32BitBE(file); // not used
8505 getFileChunkBE(file, chunk_name, NULL);
8506 if (!strEqual(chunk_name, "TAPE"))
8508 tape.no_valid_file = TRUE;
8510 Warn("unknown format of tape file '%s'", filename);
8517 else // check for pre-2.0 file format with cookie string
8519 strcpy(cookie, chunk_name);
8520 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8522 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8523 cookie[strlen(cookie) - 1] = '\0';
8525 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8527 tape.no_valid_file = TRUE;
8529 Warn("unknown format of tape file '%s'", filename);
8536 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8538 tape.no_valid_file = TRUE;
8540 Warn("unsupported version of tape file '%s'", filename);
8547 // pre-2.0 tape files have no game version, so use file version here
8548 tape.game_version = tape.file_version;
8551 if (tape.file_version < FILE_VERSION_1_2)
8553 // tape files from versions before 1.2.0 without chunk structure
8554 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8555 LoadTape_BODY(file, 2 * tape.length, &tape);
8563 int (*loader)(File *, int, struct TapeInfo *);
8567 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8568 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8569 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8570 { "INFO", -1, LoadTape_INFO },
8571 { "BODY", -1, LoadTape_BODY },
8575 while (getFileChunkBE(file, chunk_name, &chunk_size))
8579 while (chunk_info[i].name != NULL &&
8580 !strEqual(chunk_name, chunk_info[i].name))
8583 if (chunk_info[i].name == NULL)
8585 Warn("unknown chunk '%s' in tape file '%s'",
8586 chunk_name, filename);
8588 ReadUnusedBytesFromFile(file, chunk_size);
8590 else if (chunk_info[i].size != -1 &&
8591 chunk_info[i].size != chunk_size)
8593 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8594 chunk_size, chunk_name, filename);
8596 ReadUnusedBytesFromFile(file, chunk_size);
8600 // call function to load this tape chunk
8601 int chunk_size_expected =
8602 (chunk_info[i].loader)(file, chunk_size, &tape);
8604 // the size of some chunks cannot be checked before reading other
8605 // chunks first (like "HEAD" and "BODY") that contain some header
8606 // information, so check them here
8607 if (chunk_size_expected != chunk_size)
8609 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8610 chunk_size, chunk_name, filename);
8618 tape.length_frames = GetTapeLengthFrames();
8619 tape.length_seconds = GetTapeLengthSeconds();
8622 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8624 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8626 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8627 tape.engine_version);
8631 void LoadTape(int nr)
8633 char *filename = getTapeFilename(nr);
8635 LoadTapeFromFilename(filename);
8638 void LoadSolutionTape(int nr)
8640 char *filename = getSolutionTapeFilename(nr);
8642 LoadTapeFromFilename(filename);
8644 if (TAPE_IS_EMPTY(tape))
8646 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8647 level.native_bd_level->replay != NULL)
8648 CopyNativeTape_BD_to_RND(&level);
8649 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8650 level.native_sp_level->demo.is_available)
8651 CopyNativeTape_SP_to_RND(&level);
8655 void LoadScoreTape(char *score_tape_basename, int nr)
8657 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8659 LoadTapeFromFilename(filename);
8662 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8664 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8666 LoadTapeFromFilename(filename);
8669 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8671 // chunk required for team mode tapes with non-default screen size
8672 return (tape->num_participating_players > 1 &&
8673 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8674 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8677 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8679 putFileVersion(file, tape->file_version);
8680 putFileVersion(file, tape->game_version);
8683 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8686 byte store_participating_players = 0;
8688 // set bits for participating players for compact storage
8689 for (i = 0; i < MAX_PLAYERS; i++)
8690 if (tape->player_participates[i])
8691 store_participating_players |= (1 << i);
8693 putFile32BitBE(file, tape->random_seed);
8694 putFile32BitBE(file, tape->date);
8695 putFile32BitBE(file, tape->length);
8697 putFile8Bit(file, store_participating_players);
8699 putFile8Bit(file, getTapeActionValue(tape));
8701 putFile8Bit(file, tape->property_bits);
8702 putFile8Bit(file, tape->solved);
8704 putFileVersion(file, tape->engine_version);
8707 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8709 putFile8Bit(file, tape->scr_fieldx);
8710 putFile8Bit(file, tape->scr_fieldy);
8713 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8715 int level_identifier_size = strlen(tape->level_identifier) + 1;
8718 putFile16BitBE(file, level_identifier_size);
8720 for (i = 0; i < level_identifier_size; i++)
8721 putFile8Bit(file, tape->level_identifier[i]);
8723 putFile16BitBE(file, tape->level_nr);
8726 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8730 for (i = 0; i < tape->length; i++)
8732 if (tape->use_key_actions)
8734 for (j = 0; j < MAX_PLAYERS; j++)
8735 if (tape->player_participates[j])
8736 putFile8Bit(file, tape->pos[i].action[j]);
8739 if (tape->use_mouse_actions)
8741 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8742 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8743 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8746 putFile8Bit(file, tape->pos[i].delay);
8750 void SaveTapeToFilename(char *filename)
8754 int info_chunk_size;
8755 int body_chunk_size;
8757 if (!(file = fopen(filename, MODE_WRITE)))
8759 Warn("cannot save level recording file '%s'", filename);
8764 tape_pos_size = getTapePosSize(&tape);
8766 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8767 body_chunk_size = tape_pos_size * tape.length;
8769 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8770 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8772 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8773 SaveTape_VERS(file, &tape);
8775 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8776 SaveTape_HEAD(file, &tape);
8778 if (checkSaveTape_SCRN(&tape))
8780 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8781 SaveTape_SCRN(file, &tape);
8784 putFileChunkBE(file, "INFO", info_chunk_size);
8785 SaveTape_INFO(file, &tape);
8787 putFileChunkBE(file, "BODY", body_chunk_size);
8788 SaveTape_BODY(file, &tape);
8792 SetFilePermissions(filename, PERMS_PRIVATE);
8795 static void SaveTapeExt(char *filename)
8799 tape.file_version = FILE_VERSION_ACTUAL;
8800 tape.game_version = GAME_VERSION_ACTUAL;
8802 tape.num_participating_players = 0;
8804 // count number of participating players
8805 for (i = 0; i < MAX_PLAYERS; i++)
8806 if (tape.player_participates[i])
8807 tape.num_participating_players++;
8809 SaveTapeToFilename(filename);
8811 tape.changed = FALSE;
8814 void SaveTape(int nr)
8816 char *filename = getTapeFilename(nr);
8818 InitTapeDirectory(leveldir_current->subdir);
8820 SaveTapeExt(filename);
8823 void SaveScoreTape(int nr)
8825 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8827 // used instead of "leveldir_current->subdir" (for network games)
8828 InitScoreTapeDirectory(levelset.identifier, nr);
8830 SaveTapeExt(filename);
8833 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8834 unsigned int req_state_added)
8836 char *filename = getTapeFilename(nr);
8837 boolean new_tape = !fileExists(filename);
8838 boolean tape_saved = FALSE;
8840 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8845 Request(msg_saved, REQ_CONFIRM | req_state_added);
8853 boolean SaveTapeChecked(int nr)
8855 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8858 boolean SaveTapeChecked_LevelSolved(int nr)
8860 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8861 "Level solved! Tape saved!", REQ_STAY_OPEN);
8864 void DumpTape(struct TapeInfo *tape)
8866 int tape_frame_counter;
8869 if (tape->no_valid_file)
8871 Warn("cannot dump -- no valid tape file found");
8878 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8879 tape->level_nr, tape->file_version, tape->game_version);
8880 Print(" (effective engine version %08d)\n",
8881 tape->engine_version);
8882 Print("Level series identifier: '%s'\n", tape->level_identifier);
8884 Print("Solution tape: %s\n",
8885 tape->solved ? "yes" :
8886 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8888 Print("Special tape properties: ");
8889 if (tape->property_bits == TAPE_PROPERTY_NONE)
8891 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8892 Print("[em_random_bug]");
8893 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8894 Print("[game_speed]");
8895 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8897 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8898 Print("[single_step]");
8899 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8900 Print("[snapshot]");
8901 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8902 Print("[replayed]");
8903 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8904 Print("[tas_keys]");
8905 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8906 Print("[small_graphics]");
8909 int year2 = tape->date / 10000;
8910 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8911 int month_index_raw = (tape->date / 100) % 100;
8912 int month_index = month_index_raw % 12; // prevent invalid index
8913 int month = month_index + 1;
8914 int day = tape->date % 100;
8916 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8920 tape_frame_counter = 0;
8922 for (i = 0; i < tape->length; i++)
8924 if (i >= MAX_TAPE_LEN)
8929 for (j = 0; j < MAX_PLAYERS; j++)
8931 if (tape->player_participates[j])
8933 int action = tape->pos[i].action[j];
8935 Print("%d:%02x ", j, action);
8936 Print("[%c%c%c%c|%c%c] - ",
8937 (action & JOY_LEFT ? '<' : ' '),
8938 (action & JOY_RIGHT ? '>' : ' '),
8939 (action & JOY_UP ? '^' : ' '),
8940 (action & JOY_DOWN ? 'v' : ' '),
8941 (action & JOY_BUTTON_1 ? '1' : ' '),
8942 (action & JOY_BUTTON_2 ? '2' : ' '));
8946 Print("(%03d) ", tape->pos[i].delay);
8947 Print("[%05d]\n", tape_frame_counter);
8949 tape_frame_counter += tape->pos[i].delay;
8955 void DumpTapes(void)
8957 static LevelDirTree *dumptape_leveldir = NULL;
8959 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8960 global.dumptape_leveldir);
8962 if (dumptape_leveldir == NULL)
8963 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8965 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8966 global.dumptape_level_nr > dumptape_leveldir->last_level)
8967 Fail("no such level number: %d", global.dumptape_level_nr);
8969 leveldir_current = dumptape_leveldir;
8971 if (options.mytapes)
8972 LoadTape(global.dumptape_level_nr);
8974 LoadSolutionTape(global.dumptape_level_nr);
8982 // ============================================================================
8983 // score file functions
8984 // ============================================================================
8986 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8990 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8992 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8993 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8994 scores->entry[i].score = 0;
8995 scores->entry[i].time = 0;
8997 scores->entry[i].id = -1;
8998 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8999 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9000 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9001 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9002 strcpy(scores->entry[i].country_code, "??");
9005 scores->num_entries = 0;
9006 scores->last_added = -1;
9007 scores->last_added_local = -1;
9009 scores->updated = FALSE;
9010 scores->uploaded = FALSE;
9011 scores->tape_downloaded = FALSE;
9012 scores->force_last_added = FALSE;
9014 // The following values are intentionally not reset here:
9018 // - continue_playing
9019 // - continue_on_return
9022 static void setScoreInfoToDefaults(void)
9024 setScoreInfoToDefaultsExt(&scores);
9027 static void setServerScoreInfoToDefaults(void)
9029 setScoreInfoToDefaultsExt(&server_scores);
9032 static void LoadScore_OLD(int nr)
9035 char *filename = getScoreFilename(nr);
9036 char cookie[MAX_LINE_LEN];
9037 char line[MAX_LINE_LEN];
9041 if (!(file = fopen(filename, MODE_READ)))
9044 // check file identifier
9045 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9047 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9048 cookie[strlen(cookie) - 1] = '\0';
9050 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9052 Warn("unknown format of score file '%s'", filename);
9059 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9061 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9062 Warn("fscanf() failed; %s", strerror(errno));
9064 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9067 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9068 line[strlen(line) - 1] = '\0';
9070 for (line_ptr = line; *line_ptr; line_ptr++)
9072 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9074 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9075 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9084 static void ConvertScore_OLD(void)
9086 // only convert score to time for levels that rate playing time over score
9087 if (!level.rate_time_over_score)
9090 // convert old score to playing time for score-less levels (like Supaplex)
9091 int time_final_max = 999;
9094 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9096 int score = scores.entry[i].score;
9098 if (score > 0 && score < time_final_max)
9099 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9103 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9105 scores->file_version = getFileVersion(file);
9106 scores->game_version = getFileVersion(file);
9111 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9113 char *level_identifier = NULL;
9114 int level_identifier_size;
9117 level_identifier_size = getFile16BitBE(file);
9119 level_identifier = checked_malloc(level_identifier_size);
9121 for (i = 0; i < level_identifier_size; i++)
9122 level_identifier[i] = getFile8Bit(file);
9124 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9125 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9127 checked_free(level_identifier);
9129 scores->level_nr = getFile16BitBE(file);
9130 scores->num_entries = getFile16BitBE(file);
9132 chunk_size = 2 + level_identifier_size + 2 + 2;
9137 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9141 for (i = 0; i < scores->num_entries; i++)
9143 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9144 scores->entry[i].name[j] = getFile8Bit(file);
9146 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9149 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9154 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9158 for (i = 0; i < scores->num_entries; i++)
9159 scores->entry[i].score = getFile16BitBE(file);
9161 chunk_size = scores->num_entries * 2;
9166 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9170 for (i = 0; i < scores->num_entries; i++)
9171 scores->entry[i].score = getFile32BitBE(file);
9173 chunk_size = scores->num_entries * 4;
9178 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9182 for (i = 0; i < scores->num_entries; i++)
9183 scores->entry[i].time = getFile32BitBE(file);
9185 chunk_size = scores->num_entries * 4;
9190 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9194 for (i = 0; i < scores->num_entries; i++)
9196 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9197 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9199 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9202 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9207 void LoadScore(int nr)
9209 char *filename = getScoreFilename(nr);
9210 char cookie[MAX_LINE_LEN];
9211 char chunk_name[CHUNK_ID_LEN + 1];
9213 boolean old_score_file_format = FALSE;
9216 // always start with reliable default values
9217 setScoreInfoToDefaults();
9219 if (!(file = openFile(filename, MODE_READ)))
9222 getFileChunkBE(file, chunk_name, NULL);
9223 if (strEqual(chunk_name, "RND1"))
9225 getFile32BitBE(file); // not used
9227 getFileChunkBE(file, chunk_name, NULL);
9228 if (!strEqual(chunk_name, "SCOR"))
9230 Warn("unknown format of score file '%s'", filename);
9237 else // check for old file format with cookie string
9239 strcpy(cookie, chunk_name);
9240 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9242 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9243 cookie[strlen(cookie) - 1] = '\0';
9245 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9247 Warn("unknown format of score file '%s'", filename);
9254 old_score_file_format = TRUE;
9257 if (old_score_file_format)
9259 // score files from versions before 4.2.4.0 without chunk structure
9262 // convert score to time, if possible (mainly for Supaplex levels)
9271 int (*loader)(File *, int, struct ScoreInfo *);
9275 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9276 { "INFO", -1, LoadScore_INFO },
9277 { "NAME", -1, LoadScore_NAME },
9278 { "SCOR", -1, LoadScore_SCOR },
9279 { "SC4R", -1, LoadScore_SC4R },
9280 { "TIME", -1, LoadScore_TIME },
9281 { "TAPE", -1, LoadScore_TAPE },
9286 while (getFileChunkBE(file, chunk_name, &chunk_size))
9290 while (chunk_info[i].name != NULL &&
9291 !strEqual(chunk_name, chunk_info[i].name))
9294 if (chunk_info[i].name == NULL)
9296 Warn("unknown chunk '%s' in score file '%s'",
9297 chunk_name, filename);
9299 ReadUnusedBytesFromFile(file, chunk_size);
9301 else if (chunk_info[i].size != -1 &&
9302 chunk_info[i].size != chunk_size)
9304 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9305 chunk_size, chunk_name, filename);
9307 ReadUnusedBytesFromFile(file, chunk_size);
9311 // call function to load this score chunk
9312 int chunk_size_expected =
9313 (chunk_info[i].loader)(file, chunk_size, &scores);
9315 // the size of some chunks cannot be checked before reading other
9316 // chunks first (like "HEAD" and "BODY") that contain some header
9317 // information, so check them here
9318 if (chunk_size_expected != chunk_size)
9320 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9321 chunk_size, chunk_name, filename);
9330 #if ENABLE_HISTORIC_CHUNKS
9331 void SaveScore_OLD(int nr)
9334 char *filename = getScoreFilename(nr);
9337 // used instead of "leveldir_current->subdir" (for network games)
9338 InitScoreDirectory(levelset.identifier);
9340 if (!(file = fopen(filename, MODE_WRITE)))
9342 Warn("cannot save score for level %d", nr);
9347 fprintf(file, "%s\n\n", SCORE_COOKIE);
9349 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9350 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9354 SetFilePermissions(filename, PERMS_PRIVATE);
9358 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9360 putFileVersion(file, scores->file_version);
9361 putFileVersion(file, scores->game_version);
9364 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9366 int level_identifier_size = strlen(scores->level_identifier) + 1;
9369 putFile16BitBE(file, level_identifier_size);
9371 for (i = 0; i < level_identifier_size; i++)
9372 putFile8Bit(file, scores->level_identifier[i]);
9374 putFile16BitBE(file, scores->level_nr);
9375 putFile16BitBE(file, scores->num_entries);
9378 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9382 for (i = 0; i < scores->num_entries; i++)
9384 int name_size = strlen(scores->entry[i].name);
9386 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9387 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9391 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9395 for (i = 0; i < scores->num_entries; i++)
9396 putFile16BitBE(file, scores->entry[i].score);
9399 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9403 for (i = 0; i < scores->num_entries; i++)
9404 putFile32BitBE(file, scores->entry[i].score);
9407 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9411 for (i = 0; i < scores->num_entries; i++)
9412 putFile32BitBE(file, scores->entry[i].time);
9415 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9419 for (i = 0; i < scores->num_entries; i++)
9421 int size = strlen(scores->entry[i].tape_basename);
9423 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9424 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9428 static void SaveScoreToFilename(char *filename)
9431 int info_chunk_size;
9432 int name_chunk_size;
9433 int scor_chunk_size;
9434 int sc4r_chunk_size;
9435 int time_chunk_size;
9436 int tape_chunk_size;
9437 boolean has_large_score_values;
9440 if (!(file = fopen(filename, MODE_WRITE)))
9442 Warn("cannot save score file '%s'", filename);
9447 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9448 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9449 scor_chunk_size = scores.num_entries * 2;
9450 sc4r_chunk_size = scores.num_entries * 4;
9451 time_chunk_size = scores.num_entries * 4;
9452 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9454 has_large_score_values = FALSE;
9455 for (i = 0; i < scores.num_entries; i++)
9456 if (scores.entry[i].score > 0xffff)
9457 has_large_score_values = TRUE;
9459 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9460 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9462 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9463 SaveScore_VERS(file, &scores);
9465 putFileChunkBE(file, "INFO", info_chunk_size);
9466 SaveScore_INFO(file, &scores);
9468 putFileChunkBE(file, "NAME", name_chunk_size);
9469 SaveScore_NAME(file, &scores);
9471 if (has_large_score_values)
9473 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9474 SaveScore_SC4R(file, &scores);
9478 putFileChunkBE(file, "SCOR", scor_chunk_size);
9479 SaveScore_SCOR(file, &scores);
9482 putFileChunkBE(file, "TIME", time_chunk_size);
9483 SaveScore_TIME(file, &scores);
9485 putFileChunkBE(file, "TAPE", tape_chunk_size);
9486 SaveScore_TAPE(file, &scores);
9490 SetFilePermissions(filename, PERMS_PRIVATE);
9493 void SaveScore(int nr)
9495 char *filename = getScoreFilename(nr);
9498 // used instead of "leveldir_current->subdir" (for network games)
9499 InitScoreDirectory(levelset.identifier);
9501 scores.file_version = FILE_VERSION_ACTUAL;
9502 scores.game_version = GAME_VERSION_ACTUAL;
9504 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9505 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9506 scores.level_nr = level_nr;
9508 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9509 if (scores.entry[i].score == 0 &&
9510 scores.entry[i].time == 0 &&
9511 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9514 scores.num_entries = i;
9516 if (scores.num_entries == 0)
9519 SaveScoreToFilename(filename);
9522 static void LoadServerScoreFromCache(int nr)
9524 struct ScoreEntry score_entry;
9533 { &score_entry.score, FALSE, 0 },
9534 { &score_entry.time, FALSE, 0 },
9535 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9536 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9537 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9538 { &score_entry.id, FALSE, 0 },
9539 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9540 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9541 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9542 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9546 char *filename = getScoreCacheFilename(nr);
9547 SetupFileHash *score_hash = loadSetupFileHash(filename);
9550 server_scores.num_entries = 0;
9552 if (score_hash == NULL)
9555 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9557 score_entry = server_scores.entry[i];
9559 for (j = 0; score_mapping[j].value != NULL; j++)
9563 sprintf(token, "%02d.%d", i, j);
9565 char *value = getHashEntry(score_hash, token);
9570 if (score_mapping[j].is_string)
9572 char *score_value = (char *)score_mapping[j].value;
9573 int value_size = score_mapping[j].string_size;
9575 strncpy(score_value, value, value_size);
9576 score_value[value_size] = '\0';
9580 int *score_value = (int *)score_mapping[j].value;
9582 *score_value = atoi(value);
9585 server_scores.num_entries = i + 1;
9588 server_scores.entry[i] = score_entry;
9591 freeSetupFileHash(score_hash);
9594 void LoadServerScore(int nr, boolean download_score)
9596 if (!setup.use_api_server)
9599 // always start with reliable default values
9600 setServerScoreInfoToDefaults();
9602 // 1st step: load server scores from cache file (which may not exist)
9603 // (this should prevent reading it while the thread is writing to it)
9604 LoadServerScoreFromCache(nr);
9606 if (download_score && runtime.use_api_server)
9608 // 2nd step: download server scores from score server to cache file
9609 // (as thread, as it might time out if the server is not reachable)
9610 ApiGetScoreAsThread(nr);
9614 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9616 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9618 // if score tape not uploaded, ask for uploading missing tapes later
9619 if (!setup.has_remaining_tapes)
9620 setup.ask_for_remaining_tapes = TRUE;
9622 setup.provide_uploading_tapes = TRUE;
9623 setup.has_remaining_tapes = TRUE;
9625 SaveSetup_ServerSetup();
9628 void SaveServerScore(int nr, boolean tape_saved)
9630 if (!runtime.use_api_server)
9632 PrepareScoreTapesForUpload(leveldir_current->subdir);
9637 ApiAddScoreAsThread(nr, tape_saved, NULL);
9640 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9641 char *score_tape_filename)
9643 if (!runtime.use_api_server)
9646 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9649 void LoadLocalAndServerScore(int nr, boolean download_score)
9651 int last_added_local = scores.last_added_local;
9652 boolean force_last_added = scores.force_last_added;
9654 // needed if only showing server scores
9655 setScoreInfoToDefaults();
9657 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9660 // restore last added local score entry (before merging server scores)
9661 scores.last_added = scores.last_added_local = last_added_local;
9663 if (setup.use_api_server &&
9664 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9666 // load server scores from cache file and trigger update from server
9667 LoadServerScore(nr, download_score);
9669 // merge local scores with scores from server
9673 if (force_last_added)
9674 scores.force_last_added = force_last_added;
9678 // ============================================================================
9679 // setup file functions
9680 // ============================================================================
9682 #define TOKEN_STR_PLAYER_PREFIX "player_"
9685 static struct TokenInfo global_setup_tokens[] =
9689 &setup.player_name, "player_name"
9693 &setup.multiple_users, "multiple_users"
9697 &setup.sound, "sound"
9701 &setup.sound_loops, "repeating_sound_loops"
9705 &setup.sound_music, "background_music"
9709 &setup.sound_simple, "simple_sound_effects"
9713 &setup.toons, "toons"
9717 &setup.global_animations, "global_animations"
9721 &setup.scroll_delay, "scroll_delay"
9725 &setup.forced_scroll_delay, "forced_scroll_delay"
9729 &setup.scroll_delay_value, "scroll_delay_value"
9733 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9737 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9741 &setup.fade_screens, "fade_screens"
9745 &setup.autorecord, "automatic_tape_recording"
9749 &setup.autorecord_after_replay, "autorecord_after_replay"
9753 &setup.auto_pause_on_start, "auto_pause_on_start"
9757 &setup.show_titlescreen, "show_titlescreen"
9761 &setup.quick_doors, "quick_doors"
9765 &setup.team_mode, "team_mode"
9769 &setup.handicap, "handicap"
9773 &setup.skip_levels, "skip_levels"
9777 &setup.increment_levels, "increment_levels"
9781 &setup.auto_play_next_level, "auto_play_next_level"
9785 &setup.count_score_after_game, "count_score_after_game"
9789 &setup.show_scores_after_game, "show_scores_after_game"
9793 &setup.time_limit, "time_limit"
9797 &setup.fullscreen, "fullscreen"
9801 &setup.window_scaling_percent, "window_scaling_percent"
9805 &setup.window_scaling_quality, "window_scaling_quality"
9809 &setup.screen_rendering_mode, "screen_rendering_mode"
9813 &setup.vsync_mode, "vsync_mode"
9817 &setup.ask_on_escape, "ask_on_escape"
9821 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9825 &setup.ask_on_game_over, "ask_on_game_over"
9829 &setup.ask_on_quit_game, "ask_on_quit_game"
9833 &setup.ask_on_quit_program, "ask_on_quit_program"
9837 &setup.quick_switch, "quick_player_switch"
9841 &setup.input_on_focus, "input_on_focus"
9845 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9849 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9853 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9857 &setup.game_speed_extended, "game_speed_extended"
9861 &setup.game_frame_delay, "game_frame_delay"
9865 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9869 &setup.bd_skip_hatching, "bd_skip_hatching"
9873 &setup.bd_scroll_delay, "bd_scroll_delay"
9877 &setup.bd_smooth_movements, "bd_smooth_movements"
9881 &setup.sp_show_border_elements, "sp_show_border_elements"
9885 &setup.small_game_graphics, "small_game_graphics"
9889 &setup.show_load_save_buttons, "show_load_save_buttons"
9893 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9897 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9901 &setup.graphics_set, "graphics_set"
9905 &setup.sounds_set, "sounds_set"
9909 &setup.music_set, "music_set"
9913 &setup.override_level_graphics, "override_level_graphics"
9917 &setup.override_level_sounds, "override_level_sounds"
9921 &setup.override_level_music, "override_level_music"
9925 &setup.volume_simple, "volume_simple"
9929 &setup.volume_loops, "volume_loops"
9933 &setup.volume_music, "volume_music"
9937 &setup.network_mode, "network_mode"
9941 &setup.network_player_nr, "network_player"
9945 &setup.network_server_hostname, "network_server_hostname"
9949 &setup.touch.control_type, "touch.control_type"
9953 &setup.touch.move_distance, "touch.move_distance"
9957 &setup.touch.drop_distance, "touch.drop_distance"
9961 &setup.touch.transparency, "touch.transparency"
9965 &setup.touch.draw_outlined, "touch.draw_outlined"
9969 &setup.touch.draw_pressed, "touch.draw_pressed"
9973 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9977 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9981 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9985 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9989 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9993 static struct TokenInfo auto_setup_tokens[] =
9997 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10001 static struct TokenInfo server_setup_tokens[] =
10005 &setup.player_uuid, "player_uuid"
10009 &setup.player_version, "player_version"
10013 &setup.use_api_server, TEST_PREFIX "use_api_server"
10017 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10021 &setup.api_server_password, TEST_PREFIX "api_server_password"
10025 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10029 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10033 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10037 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10041 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10045 static struct TokenInfo editor_setup_tokens[] =
10049 &setup.editor.el_classic, "editor.el_classic"
10053 &setup.editor.el_custom, "editor.el_custom"
10057 &setup.editor.el_user_defined, "editor.el_user_defined"
10061 &setup.editor.el_dynamic, "editor.el_dynamic"
10065 &setup.editor.el_headlines, "editor.el_headlines"
10069 &setup.editor.show_element_token, "editor.show_element_token"
10073 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10077 static struct TokenInfo editor_cascade_setup_tokens[] =
10081 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10085 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10089 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10093 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10097 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10101 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10105 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10109 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10113 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10117 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10121 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10125 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10129 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10133 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10137 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10141 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10145 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10149 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10153 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10157 static struct TokenInfo shortcut_setup_tokens[] =
10161 &setup.shortcut.save_game, "shortcut.save_game"
10165 &setup.shortcut.load_game, "shortcut.load_game"
10169 &setup.shortcut.restart_game, "shortcut.restart_game"
10173 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10177 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10181 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10185 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10189 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10193 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10197 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10201 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10205 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10209 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10213 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10217 &setup.shortcut.tape_record, "shortcut.tape_record"
10221 &setup.shortcut.tape_play, "shortcut.tape_play"
10225 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10229 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10233 &setup.shortcut.sound_music, "shortcut.sound_music"
10237 &setup.shortcut.snap_left, "shortcut.snap_left"
10241 &setup.shortcut.snap_right, "shortcut.snap_right"
10245 &setup.shortcut.snap_up, "shortcut.snap_up"
10249 &setup.shortcut.snap_down, "shortcut.snap_down"
10253 static struct SetupInputInfo setup_input;
10254 static struct TokenInfo player_setup_tokens[] =
10258 &setup_input.use_joystick, ".use_joystick"
10262 &setup_input.joy.device_name, ".joy.device_name"
10266 &setup_input.joy.xleft, ".joy.xleft"
10270 &setup_input.joy.xmiddle, ".joy.xmiddle"
10274 &setup_input.joy.xright, ".joy.xright"
10278 &setup_input.joy.yupper, ".joy.yupper"
10282 &setup_input.joy.ymiddle, ".joy.ymiddle"
10286 &setup_input.joy.ylower, ".joy.ylower"
10290 &setup_input.joy.snap, ".joy.snap_field"
10294 &setup_input.joy.drop, ".joy.place_bomb"
10298 &setup_input.key.left, ".key.move_left"
10302 &setup_input.key.right, ".key.move_right"
10306 &setup_input.key.up, ".key.move_up"
10310 &setup_input.key.down, ".key.move_down"
10314 &setup_input.key.snap, ".key.snap_field"
10318 &setup_input.key.drop, ".key.place_bomb"
10322 static struct TokenInfo system_setup_tokens[] =
10326 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10330 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10334 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10338 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10342 static struct TokenInfo internal_setup_tokens[] =
10346 &setup.internal.program_title, "program_title"
10350 &setup.internal.program_version, "program_version"
10354 &setup.internal.program_author, "program_author"
10358 &setup.internal.program_email, "program_email"
10362 &setup.internal.program_website, "program_website"
10366 &setup.internal.program_copyright, "program_copyright"
10370 &setup.internal.program_company, "program_company"
10374 &setup.internal.program_icon_file, "program_icon_file"
10378 &setup.internal.default_graphics_set, "default_graphics_set"
10382 &setup.internal.default_sounds_set, "default_sounds_set"
10386 &setup.internal.default_music_set, "default_music_set"
10390 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10394 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10398 &setup.internal.fallback_music_file, "fallback_music_file"
10402 &setup.internal.default_level_series, "default_level_series"
10406 &setup.internal.default_window_width, "default_window_width"
10410 &setup.internal.default_window_height, "default_window_height"
10414 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10418 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10422 &setup.internal.create_user_levelset, "create_user_levelset"
10426 &setup.internal.info_screens_from_main, "info_screens_from_main"
10430 &setup.internal.menu_game, "menu_game"
10434 &setup.internal.menu_engines, "menu_engines"
10438 &setup.internal.menu_editor, "menu_editor"
10442 &setup.internal.menu_graphics, "menu_graphics"
10446 &setup.internal.menu_sound, "menu_sound"
10450 &setup.internal.menu_artwork, "menu_artwork"
10454 &setup.internal.menu_input, "menu_input"
10458 &setup.internal.menu_touch, "menu_touch"
10462 &setup.internal.menu_shortcuts, "menu_shortcuts"
10466 &setup.internal.menu_exit, "menu_exit"
10470 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10474 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10478 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10482 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10486 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10490 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10494 &setup.internal.info_title, "info_title"
10498 &setup.internal.info_elements, "info_elements"
10502 &setup.internal.info_music, "info_music"
10506 &setup.internal.info_credits, "info_credits"
10510 &setup.internal.info_program, "info_program"
10514 &setup.internal.info_version, "info_version"
10518 &setup.internal.info_levelset, "info_levelset"
10522 &setup.internal.info_exit, "info_exit"
10526 static struct TokenInfo debug_setup_tokens[] =
10530 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10534 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10538 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10542 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10546 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10550 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10554 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10558 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10562 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10566 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10570 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10574 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10578 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10582 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10586 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10590 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10594 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10598 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10602 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10606 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10610 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10613 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10617 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10621 &setup.debug.xsn_mode, "debug.xsn_mode"
10625 &setup.debug.xsn_percent, "debug.xsn_percent"
10629 static struct TokenInfo options_setup_tokens[] =
10633 &setup.options.verbose, "options.verbose"
10637 &setup.options.debug, "options.debug"
10641 &setup.options.debug_mode, "options.debug_mode"
10645 static void setSetupInfoToDefaults(struct SetupInfo *si)
10649 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10651 si->multiple_users = TRUE;
10654 si->sound_loops = TRUE;
10655 si->sound_music = TRUE;
10656 si->sound_simple = TRUE;
10658 si->global_animations = TRUE;
10659 si->scroll_delay = TRUE;
10660 si->forced_scroll_delay = FALSE;
10661 si->scroll_delay_value = STD_SCROLL_DELAY;
10662 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10663 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10664 si->fade_screens = TRUE;
10665 si->autorecord = TRUE;
10666 si->autorecord_after_replay = TRUE;
10667 si->auto_pause_on_start = FALSE;
10668 si->show_titlescreen = TRUE;
10669 si->quick_doors = FALSE;
10670 si->team_mode = FALSE;
10671 si->handicap = TRUE;
10672 si->skip_levels = TRUE;
10673 si->increment_levels = TRUE;
10674 si->auto_play_next_level = TRUE;
10675 si->count_score_after_game = TRUE;
10676 si->show_scores_after_game = TRUE;
10677 si->time_limit = TRUE;
10678 si->fullscreen = FALSE;
10679 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10680 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10681 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10682 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10683 si->ask_on_escape = TRUE;
10684 si->ask_on_escape_editor = TRUE;
10685 si->ask_on_game_over = TRUE;
10686 si->ask_on_quit_game = TRUE;
10687 si->ask_on_quit_program = TRUE;
10688 si->quick_switch = FALSE;
10689 si->input_on_focus = FALSE;
10690 si->prefer_aga_graphics = TRUE;
10691 si->prefer_lowpass_sounds = FALSE;
10692 si->prefer_extra_panel_items = TRUE;
10693 si->game_speed_extended = FALSE;
10694 si->game_frame_delay = GAME_FRAME_DELAY;
10695 si->bd_skip_uncovering = FALSE;
10696 si->bd_skip_hatching = FALSE;
10697 si->bd_scroll_delay = TRUE;
10698 si->bd_smooth_movements = AUTO;
10699 si->sp_show_border_elements = FALSE;
10700 si->small_game_graphics = FALSE;
10701 si->show_load_save_buttons = FALSE;
10702 si->show_undo_redo_buttons = FALSE;
10703 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10705 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10706 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10707 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10709 si->override_level_graphics = FALSE;
10710 si->override_level_sounds = FALSE;
10711 si->override_level_music = FALSE;
10713 si->volume_simple = 100; // percent
10714 si->volume_loops = 100; // percent
10715 si->volume_music = 100; // percent
10717 si->network_mode = FALSE;
10718 si->network_player_nr = 0; // first player
10719 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10721 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10722 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10723 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10724 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10725 si->touch.draw_outlined = TRUE;
10726 si->touch.draw_pressed = TRUE;
10728 for (i = 0; i < 2; i++)
10730 char *default_grid_button[6][2] =
10736 { "111222", " vv " },
10737 { "111222", " vv " }
10739 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10740 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10741 int min_xsize = MIN(6, grid_xsize);
10742 int min_ysize = MIN(6, grid_ysize);
10743 int startx = grid_xsize - min_xsize;
10744 int starty = grid_ysize - min_ysize;
10747 // virtual buttons grid can only be set to defaults if video is initialized
10748 // (this will be repeated if virtual buttons are not loaded from setup file)
10749 if (video.initialized)
10751 si->touch.grid_xsize[i] = grid_xsize;
10752 si->touch.grid_ysize[i] = grid_ysize;
10756 si->touch.grid_xsize[i] = -1;
10757 si->touch.grid_ysize[i] = -1;
10760 for (x = 0; x < MAX_GRID_XSIZE; x++)
10761 for (y = 0; y < MAX_GRID_YSIZE; y++)
10762 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10764 for (x = 0; x < min_xsize; x++)
10765 for (y = 0; y < min_ysize; y++)
10766 si->touch.grid_button[i][x][starty + y] =
10767 default_grid_button[y][0][x];
10769 for (x = 0; x < min_xsize; x++)
10770 for (y = 0; y < min_ysize; y++)
10771 si->touch.grid_button[i][startx + x][starty + y] =
10772 default_grid_button[y][1][x];
10775 si->touch.grid_initialized = video.initialized;
10777 si->touch.overlay_buttons = FALSE;
10779 si->editor.el_boulderdash = TRUE;
10780 si->editor.el_boulderdash_native = TRUE;
10781 si->editor.el_emerald_mine = TRUE;
10782 si->editor.el_emerald_mine_club = TRUE;
10783 si->editor.el_more = TRUE;
10784 si->editor.el_sokoban = TRUE;
10785 si->editor.el_supaplex = TRUE;
10786 si->editor.el_diamond_caves = TRUE;
10787 si->editor.el_dx_boulderdash = TRUE;
10789 si->editor.el_mirror_magic = TRUE;
10790 si->editor.el_deflektor = TRUE;
10792 si->editor.el_chars = TRUE;
10793 si->editor.el_steel_chars = TRUE;
10795 si->editor.el_classic = TRUE;
10796 si->editor.el_custom = TRUE;
10798 si->editor.el_user_defined = FALSE;
10799 si->editor.el_dynamic = TRUE;
10801 si->editor.el_headlines = TRUE;
10803 si->editor.show_element_token = FALSE;
10805 si->editor.show_read_only_warning = TRUE;
10807 si->editor.use_template_for_new_levels = TRUE;
10809 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10810 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10811 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10812 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10813 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10815 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10816 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10817 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10818 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10819 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10821 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10822 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10823 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10824 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10825 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10826 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10828 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10829 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10830 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10832 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10833 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10834 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10835 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10837 for (i = 0; i < MAX_PLAYERS; i++)
10839 si->input[i].use_joystick = FALSE;
10840 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10841 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10842 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10843 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10844 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10845 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10846 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10847 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10848 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10849 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10850 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10851 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10852 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10853 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10854 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10857 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10858 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10859 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10860 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10862 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10863 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10864 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10865 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10866 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10867 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10868 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10870 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10872 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10873 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10874 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10876 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10877 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10878 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10880 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10881 si->internal.choose_from_top_leveldir = FALSE;
10882 si->internal.show_scaling_in_title = TRUE;
10883 si->internal.create_user_levelset = TRUE;
10884 si->internal.info_screens_from_main = FALSE;
10886 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10887 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10889 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10890 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10891 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10892 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10893 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10894 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10895 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10896 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10897 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10898 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10900 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10901 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10902 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10903 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10904 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10905 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10906 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10907 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10908 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10909 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10911 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10912 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10914 si->debug.show_frames_per_second = FALSE;
10916 si->debug.xsn_mode = AUTO;
10917 si->debug.xsn_percent = 0;
10919 si->options.verbose = FALSE;
10920 si->options.debug = FALSE;
10921 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10923 #if defined(PLATFORM_ANDROID)
10924 si->fullscreen = TRUE;
10925 si->touch.overlay_buttons = TRUE;
10928 setHideSetupEntry(&setup.debug.xsn_mode);
10931 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10933 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10936 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10938 si->player_uuid = NULL; // (will be set later)
10939 si->player_version = 1; // (will be set later)
10941 si->use_api_server = TRUE;
10942 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10943 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10944 si->ask_for_uploading_tapes = TRUE;
10945 si->ask_for_remaining_tapes = FALSE;
10946 si->provide_uploading_tapes = TRUE;
10947 si->ask_for_using_api_server = TRUE;
10948 si->has_remaining_tapes = FALSE;
10951 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10953 si->editor_cascade.el_bd = TRUE;
10954 si->editor_cascade.el_bd_native = TRUE;
10955 si->editor_cascade.el_em = TRUE;
10956 si->editor_cascade.el_emc = TRUE;
10957 si->editor_cascade.el_rnd = TRUE;
10958 si->editor_cascade.el_sb = TRUE;
10959 si->editor_cascade.el_sp = TRUE;
10960 si->editor_cascade.el_dc = TRUE;
10961 si->editor_cascade.el_dx = TRUE;
10963 si->editor_cascade.el_mm = TRUE;
10964 si->editor_cascade.el_df = TRUE;
10966 si->editor_cascade.el_chars = FALSE;
10967 si->editor_cascade.el_steel_chars = FALSE;
10968 si->editor_cascade.el_ce = FALSE;
10969 si->editor_cascade.el_ge = FALSE;
10970 si->editor_cascade.el_es = FALSE;
10971 si->editor_cascade.el_ref = FALSE;
10972 si->editor_cascade.el_user = FALSE;
10973 si->editor_cascade.el_dynamic = FALSE;
10976 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10978 static char *getHideSetupToken(void *setup_value)
10980 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10982 if (setup_value != NULL)
10983 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10985 return hide_setup_token;
10988 void setHideSetupEntry(void *setup_value)
10990 char *hide_setup_token = getHideSetupToken(setup_value);
10992 if (hide_setup_hash == NULL)
10993 hide_setup_hash = newSetupFileHash();
10995 if (setup_value != NULL)
10996 setHashEntry(hide_setup_hash, hide_setup_token, "");
10999 void removeHideSetupEntry(void *setup_value)
11001 char *hide_setup_token = getHideSetupToken(setup_value);
11003 if (setup_value != NULL)
11004 removeHashEntry(hide_setup_hash, hide_setup_token);
11007 boolean hideSetupEntry(void *setup_value)
11009 char *hide_setup_token = getHideSetupToken(setup_value);
11011 return (setup_value != NULL &&
11012 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11015 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11016 struct TokenInfo *token_info,
11017 int token_nr, char *token_text)
11019 char *token_hide_text = getStringCat2(token_text, ".hide");
11020 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11022 // set the value of this setup option in the setup option structure
11023 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11025 // check if this setup option should be hidden in the setup menu
11026 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11027 setHideSetupEntry(token_info[token_nr].value);
11029 free(token_hide_text);
11032 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11033 struct TokenInfo *token_info,
11036 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11037 token_info[token_nr].text);
11040 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11044 if (!setup_file_hash)
11047 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11048 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11050 setup.touch.grid_initialized = TRUE;
11051 for (i = 0; i < 2; i++)
11053 int grid_xsize = setup.touch.grid_xsize[i];
11054 int grid_ysize = setup.touch.grid_ysize[i];
11057 // if virtual buttons are not loaded from setup file, repeat initializing
11058 // virtual buttons grid with default values later when video is initialized
11059 if (grid_xsize == -1 ||
11062 setup.touch.grid_initialized = FALSE;
11067 for (y = 0; y < grid_ysize; y++)
11069 char token_string[MAX_LINE_LEN];
11071 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11073 char *value_string = getHashEntry(setup_file_hash, token_string);
11075 if (value_string == NULL)
11078 for (x = 0; x < grid_xsize; x++)
11080 char c = value_string[x];
11082 setup.touch.grid_button[i][x][y] =
11083 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11088 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11089 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11091 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11092 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11094 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11098 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11100 setup_input = setup.input[pnr];
11101 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11103 char full_token[100];
11105 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11106 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11109 setup.input[pnr] = setup_input;
11112 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11113 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11115 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11116 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11118 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11119 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11121 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11122 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11124 setHideRelatedSetupEntries();
11127 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11131 if (!setup_file_hash)
11134 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11135 setSetupInfo(auto_setup_tokens, i,
11136 getHashEntry(setup_file_hash,
11137 auto_setup_tokens[i].text));
11140 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11144 if (!setup_file_hash)
11147 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11148 setSetupInfo(server_setup_tokens, i,
11149 getHashEntry(setup_file_hash,
11150 server_setup_tokens[i].text));
11153 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11157 if (!setup_file_hash)
11160 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11161 setSetupInfo(editor_cascade_setup_tokens, i,
11162 getHashEntry(setup_file_hash,
11163 editor_cascade_setup_tokens[i].text));
11166 void LoadUserNames(void)
11168 int last_user_nr = user.nr;
11171 if (global.user_names != NULL)
11173 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11174 checked_free(global.user_names[i]);
11176 checked_free(global.user_names);
11179 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11181 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11185 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11187 if (setup_file_hash)
11189 char *player_name = getHashEntry(setup_file_hash, "player_name");
11191 global.user_names[i] = getFixedUserName(player_name);
11193 freeSetupFileHash(setup_file_hash);
11196 if (global.user_names[i] == NULL)
11197 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11200 user.nr = last_user_nr;
11203 void LoadSetupFromFilename(char *filename)
11205 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11207 if (setup_file_hash)
11209 decodeSetupFileHash_Default(setup_file_hash);
11211 freeSetupFileHash(setup_file_hash);
11215 Debug("setup", "using default setup values");
11219 static void LoadSetup_SpecialPostProcessing(void)
11221 char *player_name_new;
11223 // needed to work around problems with fixed length strings
11224 player_name_new = getFixedUserName(setup.player_name);
11225 free(setup.player_name);
11226 setup.player_name = player_name_new;
11228 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11229 if (setup.scroll_delay == FALSE)
11231 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11232 setup.scroll_delay = TRUE; // now always "on"
11235 // make sure that scroll delay value stays inside valid range
11236 setup.scroll_delay_value =
11237 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11240 void LoadSetup_Default(void)
11244 // always start with reliable default values
11245 setSetupInfoToDefaults(&setup);
11247 // try to load setup values from default setup file
11248 filename = getDefaultSetupFilename();
11250 if (fileExists(filename))
11251 LoadSetupFromFilename(filename);
11253 // try to load setup values from platform setup file
11254 filename = getPlatformSetupFilename();
11256 if (fileExists(filename))
11257 LoadSetupFromFilename(filename);
11259 // try to load setup values from user setup file
11260 filename = getSetupFilename();
11262 LoadSetupFromFilename(filename);
11264 LoadSetup_SpecialPostProcessing();
11267 void LoadSetup_AutoSetup(void)
11269 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11270 SetupFileHash *setup_file_hash = NULL;
11272 // always start with reliable default values
11273 setSetupInfoToDefaults_AutoSetup(&setup);
11275 setup_file_hash = loadSetupFileHash(filename);
11277 if (setup_file_hash)
11279 decodeSetupFileHash_AutoSetup(setup_file_hash);
11281 freeSetupFileHash(setup_file_hash);
11287 void LoadSetup_ServerSetup(void)
11289 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11290 SetupFileHash *setup_file_hash = NULL;
11292 // always start with reliable default values
11293 setSetupInfoToDefaults_ServerSetup(&setup);
11295 setup_file_hash = loadSetupFileHash(filename);
11297 if (setup_file_hash)
11299 decodeSetupFileHash_ServerSetup(setup_file_hash);
11301 freeSetupFileHash(setup_file_hash);
11306 if (setup.player_uuid == NULL)
11308 // player UUID does not yet exist in setup file
11309 setup.player_uuid = getStringCopy(getUUID());
11310 setup.player_version = 2;
11312 SaveSetup_ServerSetup();
11316 void LoadSetup_EditorCascade(void)
11318 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11319 SetupFileHash *setup_file_hash = NULL;
11321 // always start with reliable default values
11322 setSetupInfoToDefaults_EditorCascade(&setup);
11324 setup_file_hash = loadSetupFileHash(filename);
11326 if (setup_file_hash)
11328 decodeSetupFileHash_EditorCascade(setup_file_hash);
11330 freeSetupFileHash(setup_file_hash);
11336 void LoadSetup(void)
11338 LoadSetup_Default();
11339 LoadSetup_AutoSetup();
11340 LoadSetup_ServerSetup();
11341 LoadSetup_EditorCascade();
11344 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11345 char *mapping_line)
11347 char mapping_guid[MAX_LINE_LEN];
11348 char *mapping_start, *mapping_end;
11350 // get GUID from game controller mapping line: copy complete line
11351 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11352 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11354 // get GUID from game controller mapping line: cut after GUID part
11355 mapping_start = strchr(mapping_guid, ',');
11356 if (mapping_start != NULL)
11357 *mapping_start = '\0';
11359 // cut newline from game controller mapping line
11360 mapping_end = strchr(mapping_line, '\n');
11361 if (mapping_end != NULL)
11362 *mapping_end = '\0';
11364 // add mapping entry to game controller mappings hash
11365 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11368 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11373 if (!(file = fopen(filename, MODE_READ)))
11375 Warn("cannot read game controller mappings file '%s'", filename);
11380 while (!feof(file))
11382 char line[MAX_LINE_LEN];
11384 if (!fgets(line, MAX_LINE_LEN, file))
11387 addGameControllerMappingToHash(mappings_hash, line);
11393 void SaveSetup_Default(void)
11395 char *filename = getSetupFilename();
11399 InitUserDataDirectory();
11401 if (!(file = fopen(filename, MODE_WRITE)))
11403 Warn("cannot write setup file '%s'", filename);
11408 fprintFileHeader(file, SETUP_FILENAME);
11410 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11412 // just to make things nicer :)
11413 if (global_setup_tokens[i].value == &setup.multiple_users ||
11414 global_setup_tokens[i].value == &setup.sound ||
11415 global_setup_tokens[i].value == &setup.graphics_set ||
11416 global_setup_tokens[i].value == &setup.volume_simple ||
11417 global_setup_tokens[i].value == &setup.network_mode ||
11418 global_setup_tokens[i].value == &setup.touch.control_type ||
11419 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11420 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11421 fprintf(file, "\n");
11423 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11426 for (i = 0; i < 2; i++)
11428 int grid_xsize = setup.touch.grid_xsize[i];
11429 int grid_ysize = setup.touch.grid_ysize[i];
11432 fprintf(file, "\n");
11434 for (y = 0; y < grid_ysize; y++)
11436 char token_string[MAX_LINE_LEN];
11437 char value_string[MAX_LINE_LEN];
11439 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11441 for (x = 0; x < grid_xsize; x++)
11443 char c = setup.touch.grid_button[i][x][y];
11445 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11448 value_string[grid_xsize] = '\0';
11450 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11454 fprintf(file, "\n");
11455 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11456 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11458 fprintf(file, "\n");
11459 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11460 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11462 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11466 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11467 fprintf(file, "\n");
11469 setup_input = setup.input[pnr];
11470 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11471 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11474 fprintf(file, "\n");
11475 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11476 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11478 // (internal setup values not saved to user setup file)
11480 fprintf(file, "\n");
11481 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11482 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11483 setup.debug.xsn_mode != AUTO)
11484 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11486 fprintf(file, "\n");
11487 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11488 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11492 SetFilePermissions(filename, PERMS_PRIVATE);
11495 void SaveSetup_AutoSetup(void)
11497 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11501 InitUserDataDirectory();
11503 if (!(file = fopen(filename, MODE_WRITE)))
11505 Warn("cannot write auto setup file '%s'", filename);
11512 fprintFileHeader(file, AUTOSETUP_FILENAME);
11514 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11515 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11519 SetFilePermissions(filename, PERMS_PRIVATE);
11524 void SaveSetup_ServerSetup(void)
11526 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11530 InitUserDataDirectory();
11532 if (!(file = fopen(filename, MODE_WRITE)))
11534 Warn("cannot write server setup file '%s'", filename);
11541 fprintFileHeader(file, SERVERSETUP_FILENAME);
11543 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11545 // just to make things nicer :)
11546 if (server_setup_tokens[i].value == &setup.use_api_server)
11547 fprintf(file, "\n");
11549 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11554 SetFilePermissions(filename, PERMS_PRIVATE);
11559 void SaveSetup_EditorCascade(void)
11561 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11565 InitUserDataDirectory();
11567 if (!(file = fopen(filename, MODE_WRITE)))
11569 Warn("cannot write editor cascade state file '%s'", filename);
11576 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11578 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11579 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11583 SetFilePermissions(filename, PERMS_PRIVATE);
11588 void SaveSetup(void)
11590 SaveSetup_Default();
11591 SaveSetup_AutoSetup();
11592 SaveSetup_ServerSetup();
11593 SaveSetup_EditorCascade();
11596 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11601 if (!(file = fopen(filename, MODE_WRITE)))
11603 Warn("cannot write game controller mappings file '%s'", filename);
11608 BEGIN_HASH_ITERATION(mappings_hash, itr)
11610 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11612 END_HASH_ITERATION(mappings_hash, itr)
11617 void SaveSetup_AddGameControllerMapping(char *mapping)
11619 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11620 SetupFileHash *mappings_hash = newSetupFileHash();
11622 InitUserDataDirectory();
11624 // load existing personal game controller mappings
11625 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11627 // add new mapping to personal game controller mappings
11628 addGameControllerMappingToHash(mappings_hash, mapping);
11630 // save updated personal game controller mappings
11631 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11633 freeSetupFileHash(mappings_hash);
11637 void LoadCustomElementDescriptions(void)
11639 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11640 SetupFileHash *setup_file_hash;
11643 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11645 if (element_info[i].custom_description != NULL)
11647 free(element_info[i].custom_description);
11648 element_info[i].custom_description = NULL;
11652 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11655 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11657 char *token = getStringCat2(element_info[i].token_name, ".name");
11658 char *value = getHashEntry(setup_file_hash, token);
11661 element_info[i].custom_description = getStringCopy(value);
11666 freeSetupFileHash(setup_file_hash);
11669 static int getElementFromToken(char *token)
11671 char *value = getHashEntry(element_token_hash, token);
11674 return atoi(value);
11676 Warn("unknown element token '%s'", token);
11678 return EL_UNDEFINED;
11681 void FreeGlobalAnimEventInfo(void)
11683 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11685 if (gaei->event_list == NULL)
11690 for (i = 0; i < gaei->num_event_lists; i++)
11692 checked_free(gaei->event_list[i]->event_value);
11693 checked_free(gaei->event_list[i]);
11696 checked_free(gaei->event_list);
11698 gaei->event_list = NULL;
11699 gaei->num_event_lists = 0;
11702 static int AddGlobalAnimEventList(void)
11704 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11705 int list_pos = gaei->num_event_lists++;
11707 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11708 sizeof(struct GlobalAnimEventListInfo *));
11710 gaei->event_list[list_pos] =
11711 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11713 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11715 gaeli->event_value = NULL;
11716 gaeli->num_event_values = 0;
11721 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11723 // do not add empty global animation events
11724 if (event_value == ANIM_EVENT_NONE)
11727 // if list position is undefined, create new list
11728 if (list_pos == ANIM_EVENT_UNDEFINED)
11729 list_pos = AddGlobalAnimEventList();
11731 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11732 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11733 int value_pos = gaeli->num_event_values++;
11735 gaeli->event_value = checked_realloc(gaeli->event_value,
11736 gaeli->num_event_values * sizeof(int *));
11738 gaeli->event_value[value_pos] = event_value;
11743 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11745 if (list_pos == ANIM_EVENT_UNDEFINED)
11746 return ANIM_EVENT_NONE;
11748 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11749 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11751 return gaeli->event_value[value_pos];
11754 int GetGlobalAnimEventValueCount(int list_pos)
11756 if (list_pos == ANIM_EVENT_UNDEFINED)
11759 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11760 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11762 return gaeli->num_event_values;
11765 // This function checks if a string <s> of the format "string1, string2, ..."
11766 // exactly contains a string <s_contained>.
11768 static boolean string_has_parameter(char *s, char *s_contained)
11772 if (s == NULL || s_contained == NULL)
11775 if (strlen(s_contained) > strlen(s))
11778 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11780 char next_char = s[strlen(s_contained)];
11782 // check if next character is delimiter or whitespace
11783 if (next_char == ',' || next_char == '\0' ||
11784 next_char == ' ' || next_char == '\t')
11788 // check if string contains another parameter string after a comma
11789 substring = strchr(s, ',');
11790 if (substring == NULL) // string does not contain a comma
11793 // advance string pointer to next character after the comma
11796 // skip potential whitespaces after the comma
11797 while (*substring == ' ' || *substring == '\t')
11800 return string_has_parameter(substring, s_contained);
11803 static int get_anim_parameter_value_ce(char *s)
11806 char *pattern_1 = "ce_change:custom_";
11807 char *pattern_2 = ".page_";
11808 int pattern_1_len = strlen(pattern_1);
11809 char *matching_char = strstr(s_ptr, pattern_1);
11810 int result = ANIM_EVENT_NONE;
11812 if (matching_char == NULL)
11813 return ANIM_EVENT_NONE;
11815 result = ANIM_EVENT_CE_CHANGE;
11817 s_ptr = matching_char + pattern_1_len;
11819 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11820 if (*s_ptr >= '0' && *s_ptr <= '9')
11822 int gic_ce_nr = (*s_ptr++ - '0');
11824 if (*s_ptr >= '0' && *s_ptr <= '9')
11826 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11828 if (*s_ptr >= '0' && *s_ptr <= '9')
11829 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11832 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11833 return ANIM_EVENT_NONE;
11835 // custom element stored as 0 to 255
11838 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11842 // invalid custom element number specified
11844 return ANIM_EVENT_NONE;
11847 // check for change page number ("page_X" or "page_XX") (optional)
11848 if (strPrefix(s_ptr, pattern_2))
11850 s_ptr += strlen(pattern_2);
11852 if (*s_ptr >= '0' && *s_ptr <= '9')
11854 int gic_page_nr = (*s_ptr++ - '0');
11856 if (*s_ptr >= '0' && *s_ptr <= '9')
11857 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11859 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11860 return ANIM_EVENT_NONE;
11862 // change page stored as 1 to 32 (0 means "all change pages")
11864 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11868 // invalid animation part number specified
11870 return ANIM_EVENT_NONE;
11874 // discard result if next character is neither delimiter nor whitespace
11875 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11876 *s_ptr == ' ' || *s_ptr == '\t'))
11877 return ANIM_EVENT_NONE;
11882 static int get_anim_parameter_value(char *s)
11884 int event_value[] =
11892 char *pattern_1[] =
11900 char *pattern_2 = ".part_";
11901 char *matching_char = NULL;
11903 int pattern_1_len = 0;
11904 int result = ANIM_EVENT_NONE;
11907 result = get_anim_parameter_value_ce(s);
11909 if (result != ANIM_EVENT_NONE)
11912 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11914 matching_char = strstr(s_ptr, pattern_1[i]);
11915 pattern_1_len = strlen(pattern_1[i]);
11916 result = event_value[i];
11918 if (matching_char != NULL)
11922 if (matching_char == NULL)
11923 return ANIM_EVENT_NONE;
11925 s_ptr = matching_char + pattern_1_len;
11927 // check for main animation number ("anim_X" or "anim_XX")
11928 if (*s_ptr >= '0' && *s_ptr <= '9')
11930 int gic_anim_nr = (*s_ptr++ - '0');
11932 if (*s_ptr >= '0' && *s_ptr <= '9')
11933 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11935 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11936 return ANIM_EVENT_NONE;
11938 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11942 // invalid main animation number specified
11944 return ANIM_EVENT_NONE;
11947 // check for animation part number ("part_X" or "part_XX") (optional)
11948 if (strPrefix(s_ptr, pattern_2))
11950 s_ptr += strlen(pattern_2);
11952 if (*s_ptr >= '0' && *s_ptr <= '9')
11954 int gic_part_nr = (*s_ptr++ - '0');
11956 if (*s_ptr >= '0' && *s_ptr <= '9')
11957 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11959 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11960 return ANIM_EVENT_NONE;
11962 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11966 // invalid animation part number specified
11968 return ANIM_EVENT_NONE;
11972 // discard result if next character is neither delimiter nor whitespace
11973 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11974 *s_ptr == ' ' || *s_ptr == '\t'))
11975 return ANIM_EVENT_NONE;
11980 static int get_anim_parameter_values(char *s)
11982 int list_pos = ANIM_EVENT_UNDEFINED;
11983 int event_value = ANIM_EVENT_DEFAULT;
11985 if (string_has_parameter(s, "any"))
11986 event_value |= ANIM_EVENT_ANY;
11988 if (string_has_parameter(s, "click:self") ||
11989 string_has_parameter(s, "click") ||
11990 string_has_parameter(s, "self"))
11991 event_value |= ANIM_EVENT_SELF;
11993 if (string_has_parameter(s, "unclick:any"))
11994 event_value |= ANIM_EVENT_UNCLICK_ANY;
11996 // if animation event found, add it to global animation event list
11997 if (event_value != ANIM_EVENT_NONE)
11998 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12002 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12003 event_value = get_anim_parameter_value(s);
12005 // if animation event found, add it to global animation event list
12006 if (event_value != ANIM_EVENT_NONE)
12007 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12009 // continue with next part of the string, starting with next comma
12010 s = strchr(s + 1, ',');
12016 static int get_anim_action_parameter_value(char *token)
12018 // check most common default case first to massively speed things up
12019 if (strEqual(token, ARG_UNDEFINED))
12020 return ANIM_EVENT_ACTION_NONE;
12022 int result = getImageIDFromToken(token);
12026 char *gfx_token = getStringCat2("gfx.", token);
12028 result = getImageIDFromToken(gfx_token);
12030 checked_free(gfx_token);
12035 Key key = getKeyFromX11KeyName(token);
12037 if (key != KSYM_UNDEFINED)
12038 result = -(int)key;
12045 result = get_hash_from_string(token); // unsigned int => int
12046 result = ABS(result); // may be negative now
12047 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12049 setHashEntry(anim_url_hash, int2str(result, 0), token);
12054 result = ANIM_EVENT_ACTION_NONE;
12059 int get_parameter_value(char *value_raw, char *suffix, int type)
12061 char *value = getStringToLower(value_raw);
12062 int result = 0; // probably a save default value
12064 if (strEqual(suffix, ".direction"))
12066 result = (strEqual(value, "left") ? MV_LEFT :
12067 strEqual(value, "right") ? MV_RIGHT :
12068 strEqual(value, "up") ? MV_UP :
12069 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12071 else if (strEqual(suffix, ".position"))
12073 result = (strEqual(value, "left") ? POS_LEFT :
12074 strEqual(value, "right") ? POS_RIGHT :
12075 strEqual(value, "top") ? POS_TOP :
12076 strEqual(value, "upper") ? POS_UPPER :
12077 strEqual(value, "middle") ? POS_MIDDLE :
12078 strEqual(value, "lower") ? POS_LOWER :
12079 strEqual(value, "bottom") ? POS_BOTTOM :
12080 strEqual(value, "any") ? POS_ANY :
12081 strEqual(value, "ce") ? POS_CE :
12082 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12083 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12085 else if (strEqual(suffix, ".align"))
12087 result = (strEqual(value, "left") ? ALIGN_LEFT :
12088 strEqual(value, "right") ? ALIGN_RIGHT :
12089 strEqual(value, "center") ? ALIGN_CENTER :
12090 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12092 else if (strEqual(suffix, ".valign"))
12094 result = (strEqual(value, "top") ? VALIGN_TOP :
12095 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12096 strEqual(value, "middle") ? VALIGN_MIDDLE :
12097 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12099 else if (strEqual(suffix, ".anim_mode"))
12101 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12102 string_has_parameter(value, "loop") ? ANIM_LOOP :
12103 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12104 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12105 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12106 string_has_parameter(value, "random") ? ANIM_RANDOM :
12107 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12108 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12109 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12110 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12111 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12112 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12113 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12114 string_has_parameter(value, "all") ? ANIM_ALL :
12115 string_has_parameter(value, "tiled") ? ANIM_TILED :
12116 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12119 if (string_has_parameter(value, "once"))
12120 result |= ANIM_ONCE;
12122 if (string_has_parameter(value, "reverse"))
12123 result |= ANIM_REVERSE;
12125 if (string_has_parameter(value, "opaque_player"))
12126 result |= ANIM_OPAQUE_PLAYER;
12128 if (string_has_parameter(value, "static_panel"))
12129 result |= ANIM_STATIC_PANEL;
12131 else if (strEqual(suffix, ".init_event") ||
12132 strEqual(suffix, ".anim_event"))
12134 result = get_anim_parameter_values(value);
12136 else if (strEqual(suffix, ".init_delay_action") ||
12137 strEqual(suffix, ".anim_delay_action") ||
12138 strEqual(suffix, ".post_delay_action") ||
12139 strEqual(suffix, ".init_event_action") ||
12140 strEqual(suffix, ".anim_event_action"))
12142 result = get_anim_action_parameter_value(value_raw);
12144 else if (strEqual(suffix, ".class"))
12146 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12147 get_hash_from_string(value));
12149 else if (strEqual(suffix, ".style"))
12151 result = STYLE_DEFAULT;
12153 if (string_has_parameter(value, "accurate_borders"))
12154 result |= STYLE_ACCURATE_BORDERS;
12156 if (string_has_parameter(value, "inner_corners"))
12157 result |= STYLE_INNER_CORNERS;
12159 if (string_has_parameter(value, "reverse"))
12160 result |= STYLE_REVERSE;
12162 if (string_has_parameter(value, "leftmost_position"))
12163 result |= STYLE_LEFTMOST_POSITION;
12165 if (string_has_parameter(value, "block_clicks"))
12166 result |= STYLE_BLOCK;
12168 if (string_has_parameter(value, "passthrough_clicks"))
12169 result |= STYLE_PASSTHROUGH;
12171 if (string_has_parameter(value, "multiple_actions"))
12172 result |= STYLE_MULTIPLE_ACTIONS;
12174 if (string_has_parameter(value, "consume_ce_event"))
12175 result |= STYLE_CONSUME_CE_EVENT;
12177 else if (strEqual(suffix, ".fade_mode"))
12179 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12180 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12181 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12182 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12183 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12184 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12185 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12186 FADE_MODE_DEFAULT);
12188 else if (strEqual(suffix, ".auto_delay_unit"))
12190 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12191 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12192 AUTO_DELAY_UNIT_DEFAULT);
12194 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12196 result = gfx.get_font_from_token_function(value);
12198 else // generic parameter of type integer or boolean
12200 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12201 type == TYPE_INTEGER ? get_integer_from_string(value) :
12202 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12203 ARG_UNDEFINED_VALUE);
12211 static int get_token_parameter_value(char *token, char *value_raw)
12215 if (token == NULL || value_raw == NULL)
12216 return ARG_UNDEFINED_VALUE;
12218 suffix = strrchr(token, '.');
12219 if (suffix == NULL)
12222 if (strEqual(suffix, ".element"))
12223 return getElementFromToken(value_raw);
12225 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12226 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12229 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12230 boolean ignore_defaults)
12234 for (i = 0; image_config_vars[i].token != NULL; i++)
12236 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12238 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12239 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12243 *image_config_vars[i].value =
12244 get_token_parameter_value(image_config_vars[i].token, value);
12248 void InitMenuDesignSettings_Static(void)
12250 // always start with reliable default values from static default config
12251 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12254 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12258 // the following initializes hierarchical values from static configuration
12260 // special case: initialize "ARG_DEFAULT" values in static default config
12261 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12262 titlescreen_initial_first_default.fade_mode =
12263 title_initial_first_default.fade_mode;
12264 titlescreen_initial_first_default.fade_delay =
12265 title_initial_first_default.fade_delay;
12266 titlescreen_initial_first_default.post_delay =
12267 title_initial_first_default.post_delay;
12268 titlescreen_initial_first_default.auto_delay =
12269 title_initial_first_default.auto_delay;
12270 titlescreen_initial_first_default.auto_delay_unit =
12271 title_initial_first_default.auto_delay_unit;
12272 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12273 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12274 titlescreen_first_default.post_delay = title_first_default.post_delay;
12275 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12276 titlescreen_first_default.auto_delay_unit =
12277 title_first_default.auto_delay_unit;
12278 titlemessage_initial_first_default.fade_mode =
12279 title_initial_first_default.fade_mode;
12280 titlemessage_initial_first_default.fade_delay =
12281 title_initial_first_default.fade_delay;
12282 titlemessage_initial_first_default.post_delay =
12283 title_initial_first_default.post_delay;
12284 titlemessage_initial_first_default.auto_delay =
12285 title_initial_first_default.auto_delay;
12286 titlemessage_initial_first_default.auto_delay_unit =
12287 title_initial_first_default.auto_delay_unit;
12288 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12289 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12290 titlemessage_first_default.post_delay = title_first_default.post_delay;
12291 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12292 titlemessage_first_default.auto_delay_unit =
12293 title_first_default.auto_delay_unit;
12295 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12296 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12297 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12298 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12299 titlescreen_initial_default.auto_delay_unit =
12300 title_initial_default.auto_delay_unit;
12301 titlescreen_default.fade_mode = title_default.fade_mode;
12302 titlescreen_default.fade_delay = title_default.fade_delay;
12303 titlescreen_default.post_delay = title_default.post_delay;
12304 titlescreen_default.auto_delay = title_default.auto_delay;
12305 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12306 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12307 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12308 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12309 titlemessage_initial_default.auto_delay_unit =
12310 title_initial_default.auto_delay_unit;
12311 titlemessage_default.fade_mode = title_default.fade_mode;
12312 titlemessage_default.fade_delay = title_default.fade_delay;
12313 titlemessage_default.post_delay = title_default.post_delay;
12314 titlemessage_default.auto_delay = title_default.auto_delay;
12315 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12317 // special case: initialize "ARG_DEFAULT" values in static default config
12318 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12319 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12321 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12322 titlescreen_first[i] = titlescreen_first_default;
12323 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12324 titlemessage_first[i] = titlemessage_first_default;
12326 titlescreen_initial[i] = titlescreen_initial_default;
12327 titlescreen[i] = titlescreen_default;
12328 titlemessage_initial[i] = titlemessage_initial_default;
12329 titlemessage[i] = titlemessage_default;
12332 // special case: initialize "ARG_DEFAULT" values in static default config
12333 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12334 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12336 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12339 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12340 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12341 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12344 // special case: initialize "ARG_DEFAULT" values in static default config
12345 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12346 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12348 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12349 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12350 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12352 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12355 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12359 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12363 struct XY *dst, *src;
12365 game_buttons_xy[] =
12367 { &game.button.save, &game.button.stop },
12368 { &game.button.pause2, &game.button.pause },
12369 { &game.button.load, &game.button.play },
12370 { &game.button.undo, &game.button.stop },
12371 { &game.button.redo, &game.button.play },
12377 // special case: initialize later added SETUP list size from LEVELS value
12378 if (menu.list_size[GAME_MODE_SETUP] == -1)
12379 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12381 // set default position for snapshot buttons to stop/pause/play buttons
12382 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12383 if ((*game_buttons_xy[i].dst).x == -1 &&
12384 (*game_buttons_xy[i].dst).y == -1)
12385 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12387 // --------------------------------------------------------------------------
12388 // dynamic viewports (including playfield margins, borders and alignments)
12389 // --------------------------------------------------------------------------
12391 // dynamic viewports currently only supported for landscape mode
12392 int display_width = MAX(video.display_width, video.display_height);
12393 int display_height = MIN(video.display_width, video.display_height);
12395 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12397 struct RectWithBorder *vp_window = &viewport.window[i];
12398 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12399 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12400 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12401 boolean dynamic_window_width = (vp_window->min_width != -1);
12402 boolean dynamic_window_height = (vp_window->min_height != -1);
12403 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12404 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12406 // adjust window size if min/max width/height is specified
12408 if (vp_window->min_width != -1)
12410 int window_width = display_width;
12412 // when using static window height, use aspect ratio of display
12413 if (vp_window->min_height == -1)
12414 window_width = vp_window->height * display_width / display_height;
12416 vp_window->width = MAX(vp_window->min_width, window_width);
12419 if (vp_window->min_height != -1)
12421 int window_height = display_height;
12423 // when using static window width, use aspect ratio of display
12424 if (vp_window->min_width == -1)
12425 window_height = vp_window->width * display_height / display_width;
12427 vp_window->height = MAX(vp_window->min_height, window_height);
12430 if (vp_window->max_width != -1)
12431 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12433 if (vp_window->max_height != -1)
12434 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12436 int playfield_width = vp_window->width;
12437 int playfield_height = vp_window->height;
12439 // adjust playfield size and position according to specified margins
12441 playfield_width -= vp_playfield->margin_left;
12442 playfield_width -= vp_playfield->margin_right;
12444 playfield_height -= vp_playfield->margin_top;
12445 playfield_height -= vp_playfield->margin_bottom;
12447 // adjust playfield size if min/max width/height is specified
12449 if (vp_playfield->min_width != -1)
12450 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12452 if (vp_playfield->min_height != -1)
12453 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12455 if (vp_playfield->max_width != -1)
12456 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12458 if (vp_playfield->max_height != -1)
12459 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12461 // adjust playfield position according to specified alignment
12463 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12464 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12465 else if (vp_playfield->align == ALIGN_CENTER)
12466 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12467 else if (vp_playfield->align == ALIGN_RIGHT)
12468 vp_playfield->x += playfield_width - vp_playfield->width;
12470 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12471 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12472 else if (vp_playfield->valign == VALIGN_MIDDLE)
12473 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12474 else if (vp_playfield->valign == VALIGN_BOTTOM)
12475 vp_playfield->y += playfield_height - vp_playfield->height;
12477 vp_playfield->x += vp_playfield->margin_left;
12478 vp_playfield->y += vp_playfield->margin_top;
12480 // adjust individual playfield borders if only default border is specified
12482 if (vp_playfield->border_left == -1)
12483 vp_playfield->border_left = vp_playfield->border_size;
12484 if (vp_playfield->border_right == -1)
12485 vp_playfield->border_right = vp_playfield->border_size;
12486 if (vp_playfield->border_top == -1)
12487 vp_playfield->border_top = vp_playfield->border_size;
12488 if (vp_playfield->border_bottom == -1)
12489 vp_playfield->border_bottom = vp_playfield->border_size;
12491 // set dynamic playfield borders if borders are specified as undefined
12492 // (but only if window size was dynamic and playfield size was static)
12494 if (dynamic_window_width && !dynamic_playfield_width)
12496 if (vp_playfield->border_left == -1)
12498 vp_playfield->border_left = (vp_playfield->x -
12499 vp_playfield->margin_left);
12500 vp_playfield->x -= vp_playfield->border_left;
12501 vp_playfield->width += vp_playfield->border_left;
12504 if (vp_playfield->border_right == -1)
12506 vp_playfield->border_right = (vp_window->width -
12508 vp_playfield->width -
12509 vp_playfield->margin_right);
12510 vp_playfield->width += vp_playfield->border_right;
12514 if (dynamic_window_height && !dynamic_playfield_height)
12516 if (vp_playfield->border_top == -1)
12518 vp_playfield->border_top = (vp_playfield->y -
12519 vp_playfield->margin_top);
12520 vp_playfield->y -= vp_playfield->border_top;
12521 vp_playfield->height += vp_playfield->border_top;
12524 if (vp_playfield->border_bottom == -1)
12526 vp_playfield->border_bottom = (vp_window->height -
12528 vp_playfield->height -
12529 vp_playfield->margin_bottom);
12530 vp_playfield->height += vp_playfield->border_bottom;
12534 // adjust playfield size to be a multiple of a defined alignment tile size
12536 int align_size = vp_playfield->align_size;
12537 int playfield_xtiles = vp_playfield->width / align_size;
12538 int playfield_ytiles = vp_playfield->height / align_size;
12539 int playfield_width_corrected = playfield_xtiles * align_size;
12540 int playfield_height_corrected = playfield_ytiles * align_size;
12541 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12542 i == GFX_SPECIAL_ARG_EDITOR);
12544 if (is_playfield_mode &&
12545 dynamic_playfield_width &&
12546 vp_playfield->width != playfield_width_corrected)
12548 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12550 vp_playfield->width = playfield_width_corrected;
12552 if (vp_playfield->align == ALIGN_LEFT)
12554 vp_playfield->border_left += playfield_xdiff;
12556 else if (vp_playfield->align == ALIGN_RIGHT)
12558 vp_playfield->border_right += playfield_xdiff;
12560 else if (vp_playfield->align == ALIGN_CENTER)
12562 int border_left_diff = playfield_xdiff / 2;
12563 int border_right_diff = playfield_xdiff - border_left_diff;
12565 vp_playfield->border_left += border_left_diff;
12566 vp_playfield->border_right += border_right_diff;
12570 if (is_playfield_mode &&
12571 dynamic_playfield_height &&
12572 vp_playfield->height != playfield_height_corrected)
12574 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12576 vp_playfield->height = playfield_height_corrected;
12578 if (vp_playfield->valign == VALIGN_TOP)
12580 vp_playfield->border_top += playfield_ydiff;
12582 else if (vp_playfield->align == VALIGN_BOTTOM)
12584 vp_playfield->border_right += playfield_ydiff;
12586 else if (vp_playfield->align == VALIGN_MIDDLE)
12588 int border_top_diff = playfield_ydiff / 2;
12589 int border_bottom_diff = playfield_ydiff - border_top_diff;
12591 vp_playfield->border_top += border_top_diff;
12592 vp_playfield->border_bottom += border_bottom_diff;
12596 // adjust door positions according to specified alignment
12598 for (j = 0; j < 2; j++)
12600 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12602 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12603 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12604 else if (vp_door->align == ALIGN_CENTER)
12605 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12606 else if (vp_door->align == ALIGN_RIGHT)
12607 vp_door->x += vp_window->width - vp_door->width;
12609 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12610 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12611 else if (vp_door->valign == VALIGN_MIDDLE)
12612 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12613 else if (vp_door->valign == VALIGN_BOTTOM)
12614 vp_door->y += vp_window->height - vp_door->height;
12619 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12623 struct XYTileSize *dst, *src;
12626 editor_buttons_xy[] =
12629 &editor.button.element_left, &editor.palette.element_left,
12630 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12633 &editor.button.element_middle, &editor.palette.element_middle,
12634 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12637 &editor.button.element_right, &editor.palette.element_right,
12638 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12645 // set default position for element buttons to element graphics
12646 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12648 if ((*editor_buttons_xy[i].dst).x == -1 &&
12649 (*editor_buttons_xy[i].dst).y == -1)
12651 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12653 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12655 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12659 // adjust editor palette rows and columns if specified to be dynamic
12661 if (editor.palette.cols == -1)
12663 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12664 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12665 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12667 editor.palette.cols = (vp_width - sc_width) / bt_width;
12669 if (editor.palette.x == -1)
12671 int palette_width = editor.palette.cols * bt_width + sc_width;
12673 editor.palette.x = (vp_width - palette_width) / 2;
12677 if (editor.palette.rows == -1)
12679 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12680 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12681 int tx_height = getFontHeight(FONT_TEXT_2);
12683 editor.palette.rows = (vp_height - tx_height) / bt_height;
12685 if (editor.palette.y == -1)
12687 int palette_height = editor.palette.rows * bt_height + tx_height;
12689 editor.palette.y = (vp_height - palette_height) / 2;
12694 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12695 boolean initialize)
12697 // special case: check if network and preview player positions are redefined,
12698 // to compare this later against the main menu level preview being redefined
12699 struct TokenIntPtrInfo menu_config_players[] =
12701 { "main.network_players.x", &menu.main.network_players.redefined },
12702 { "main.network_players.y", &menu.main.network_players.redefined },
12703 { "main.preview_players.x", &menu.main.preview_players.redefined },
12704 { "main.preview_players.y", &menu.main.preview_players.redefined },
12705 { "preview.x", &preview.redefined },
12706 { "preview.y", &preview.redefined }
12712 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12713 *menu_config_players[i].value = FALSE;
12717 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12718 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12719 *menu_config_players[i].value = TRUE;
12723 static void InitMenuDesignSettings_PreviewPlayers(void)
12725 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12728 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12730 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12733 static void LoadMenuDesignSettingsFromFilename(char *filename)
12735 static struct TitleFadingInfo tfi;
12736 static struct TitleMessageInfo tmi;
12737 static struct TokenInfo title_tokens[] =
12739 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12740 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12741 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12742 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12743 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12747 static struct TokenInfo titlemessage_tokens[] =
12749 { TYPE_INTEGER, &tmi.x, ".x" },
12750 { TYPE_INTEGER, &tmi.y, ".y" },
12751 { TYPE_INTEGER, &tmi.width, ".width" },
12752 { TYPE_INTEGER, &tmi.height, ".height" },
12753 { TYPE_INTEGER, &tmi.chars, ".chars" },
12754 { TYPE_INTEGER, &tmi.lines, ".lines" },
12755 { TYPE_INTEGER, &tmi.align, ".align" },
12756 { TYPE_INTEGER, &tmi.valign, ".valign" },
12757 { TYPE_INTEGER, &tmi.font, ".font" },
12758 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12759 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12760 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12761 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12762 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12763 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12764 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12765 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12766 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12772 struct TitleFadingInfo *info;
12777 // initialize first titles from "enter screen" definitions, if defined
12778 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12779 { &title_first_default, "menu.enter_screen.TITLE" },
12781 // initialize title screens from "next screen" definitions, if defined
12782 { &title_initial_default, "menu.next_screen.TITLE" },
12783 { &title_default, "menu.next_screen.TITLE" },
12789 struct TitleMessageInfo *array;
12792 titlemessage_arrays[] =
12794 // initialize first titles from "enter screen" definitions, if defined
12795 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12796 { titlescreen_first, "menu.enter_screen.TITLE" },
12797 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12798 { titlemessage_first, "menu.enter_screen.TITLE" },
12800 // initialize titles from "next screen" definitions, if defined
12801 { titlescreen_initial, "menu.next_screen.TITLE" },
12802 { titlescreen, "menu.next_screen.TITLE" },
12803 { titlemessage_initial, "menu.next_screen.TITLE" },
12804 { titlemessage, "menu.next_screen.TITLE" },
12806 // overwrite titles with title definitions, if defined
12807 { titlescreen_initial_first, "[title_initial]" },
12808 { titlescreen_first, "[title]" },
12809 { titlemessage_initial_first, "[title_initial]" },
12810 { titlemessage_first, "[title]" },
12812 { titlescreen_initial, "[title_initial]" },
12813 { titlescreen, "[title]" },
12814 { titlemessage_initial, "[title_initial]" },
12815 { titlemessage, "[title]" },
12817 // overwrite titles with title screen/message definitions, if defined
12818 { titlescreen_initial_first, "[titlescreen_initial]" },
12819 { titlescreen_first, "[titlescreen]" },
12820 { titlemessage_initial_first, "[titlemessage_initial]" },
12821 { titlemessage_first, "[titlemessage]" },
12823 { titlescreen_initial, "[titlescreen_initial]" },
12824 { titlescreen, "[titlescreen]" },
12825 { titlemessage_initial, "[titlemessage_initial]" },
12826 { titlemessage, "[titlemessage]" },
12830 SetupFileHash *setup_file_hash;
12833 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12836 // the following initializes hierarchical values from dynamic configuration
12838 // special case: initialize with default values that may be overwritten
12839 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12840 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12842 struct TokenIntPtrInfo menu_config[] =
12844 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12845 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12846 { "menu.list_size", &menu.list_size[i] }
12849 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12851 char *token = menu_config[j].token;
12852 char *value = getHashEntry(setup_file_hash, token);
12855 *menu_config[j].value = get_integer_from_string(value);
12859 // special case: initialize with default values that may be overwritten
12860 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12861 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12863 struct TokenIntPtrInfo menu_config[] =
12865 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12866 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12867 { "menu.list_size.INFO", &menu.list_size_info[i] },
12868 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12869 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12872 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12874 char *token = menu_config[j].token;
12875 char *value = getHashEntry(setup_file_hash, token);
12878 *menu_config[j].value = get_integer_from_string(value);
12882 // special case: initialize with default values that may be overwritten
12883 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12884 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12886 struct TokenIntPtrInfo menu_config[] =
12888 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12889 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12892 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12894 char *token = menu_config[j].token;
12895 char *value = getHashEntry(setup_file_hash, token);
12898 *menu_config[j].value = get_integer_from_string(value);
12902 // special case: initialize with default values that may be overwritten
12903 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12904 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12906 struct TokenIntPtrInfo menu_config[] =
12908 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12909 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12910 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12911 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12912 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12913 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12914 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12915 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12916 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12917 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12920 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12922 char *token = menu_config[j].token;
12923 char *value = getHashEntry(setup_file_hash, token);
12926 *menu_config[j].value = get_integer_from_string(value);
12930 // special case: initialize with default values that may be overwritten
12931 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12932 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12934 struct TokenIntPtrInfo menu_config[] =
12936 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12937 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12938 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12939 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12940 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12941 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12942 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12943 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12944 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12947 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12949 char *token = menu_config[j].token;
12950 char *value = getHashEntry(setup_file_hash, token);
12953 *menu_config[j].value = get_token_parameter_value(token, value);
12957 // special case: initialize with default values that may be overwritten
12958 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12959 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12963 char *token_prefix;
12964 struct RectWithBorder *struct_ptr;
12968 { "viewport.window", &viewport.window[i] },
12969 { "viewport.playfield", &viewport.playfield[i] },
12970 { "viewport.door_1", &viewport.door_1[i] },
12971 { "viewport.door_2", &viewport.door_2[i] }
12974 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12976 struct TokenIntPtrInfo vp_config[] =
12978 { ".x", &vp_struct[j].struct_ptr->x },
12979 { ".y", &vp_struct[j].struct_ptr->y },
12980 { ".width", &vp_struct[j].struct_ptr->width },
12981 { ".height", &vp_struct[j].struct_ptr->height },
12982 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12983 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12984 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12985 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12986 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12987 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12988 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12989 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12990 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12991 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12992 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12993 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12994 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12995 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12996 { ".align", &vp_struct[j].struct_ptr->align },
12997 { ".valign", &vp_struct[j].struct_ptr->valign }
13000 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13002 char *token = getStringCat2(vp_struct[j].token_prefix,
13003 vp_config[k].token);
13004 char *value = getHashEntry(setup_file_hash, token);
13007 *vp_config[k].value = get_token_parameter_value(token, value);
13014 // special case: initialize with default values that may be overwritten
13015 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13016 for (i = 0; title_info[i].info != NULL; i++)
13018 struct TitleFadingInfo *info = title_info[i].info;
13019 char *base_token = title_info[i].text;
13021 for (j = 0; title_tokens[j].type != -1; j++)
13023 char *token = getStringCat2(base_token, title_tokens[j].text);
13024 char *value = getHashEntry(setup_file_hash, token);
13028 int parameter_value = get_token_parameter_value(token, value);
13032 *(int *)title_tokens[j].value = (int)parameter_value;
13041 // special case: initialize with default values that may be overwritten
13042 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13043 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13045 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13046 char *base_token = titlemessage_arrays[i].text;
13048 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13050 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13051 char *value = getHashEntry(setup_file_hash, token);
13055 int parameter_value = get_token_parameter_value(token, value);
13057 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13061 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13062 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13064 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13074 // read (and overwrite with) values that may be specified in config file
13075 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13077 // special case: check if network and preview player positions are redefined
13078 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13080 freeSetupFileHash(setup_file_hash);
13083 void LoadMenuDesignSettings(void)
13085 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13087 InitMenuDesignSettings_Static();
13088 InitMenuDesignSettings_SpecialPreProcessing();
13089 InitMenuDesignSettings_PreviewPlayers();
13091 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13093 // first look for special settings configured in level series config
13094 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13096 if (fileExists(filename_base))
13097 LoadMenuDesignSettingsFromFilename(filename_base);
13100 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13102 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13103 LoadMenuDesignSettingsFromFilename(filename_local);
13105 InitMenuDesignSettings_SpecialPostProcessing();
13108 void LoadMenuDesignSettings_AfterGraphics(void)
13110 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13113 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13114 boolean ignore_defaults)
13118 for (i = 0; sound_config_vars[i].token != NULL; i++)
13120 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13122 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13123 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13127 *sound_config_vars[i].value =
13128 get_token_parameter_value(sound_config_vars[i].token, value);
13132 void InitSoundSettings_Static(void)
13134 // always start with reliable default values from static default config
13135 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13138 static void LoadSoundSettingsFromFilename(char *filename)
13140 SetupFileHash *setup_file_hash;
13142 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13145 // read (and overwrite with) values that may be specified in config file
13146 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13148 freeSetupFileHash(setup_file_hash);
13151 void LoadSoundSettings(void)
13153 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13155 InitSoundSettings_Static();
13157 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13159 // first look for special settings configured in level series config
13160 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13162 if (fileExists(filename_base))
13163 LoadSoundSettingsFromFilename(filename_base);
13166 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13168 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13169 LoadSoundSettingsFromFilename(filename_local);
13172 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13174 char *filename = getEditorSetupFilename();
13175 SetupFileList *setup_file_list, *list;
13176 SetupFileHash *element_hash;
13177 int num_unknown_tokens = 0;
13180 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13183 element_hash = newSetupFileHash();
13185 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13186 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13188 // determined size may be larger than needed (due to unknown elements)
13190 for (list = setup_file_list; list != NULL; list = list->next)
13193 // add space for up to 3 more elements for padding that may be needed
13194 *num_elements += 3;
13196 // free memory for old list of elements, if needed
13197 checked_free(*elements);
13199 // allocate memory for new list of elements
13200 *elements = checked_malloc(*num_elements * sizeof(int));
13203 for (list = setup_file_list; list != NULL; list = list->next)
13205 char *value = getHashEntry(element_hash, list->token);
13207 if (value == NULL) // try to find obsolete token mapping
13209 char *mapped_token = get_mapped_token(list->token);
13211 if (mapped_token != NULL)
13213 value = getHashEntry(element_hash, mapped_token);
13215 free(mapped_token);
13221 (*elements)[(*num_elements)++] = atoi(value);
13225 if (num_unknown_tokens == 0)
13228 Warn("unknown token(s) found in config file:");
13229 Warn("- config file: '%s'", filename);
13231 num_unknown_tokens++;
13234 Warn("- token: '%s'", list->token);
13238 if (num_unknown_tokens > 0)
13241 while (*num_elements % 4) // pad with empty elements, if needed
13242 (*elements)[(*num_elements)++] = EL_EMPTY;
13244 freeSetupFileList(setup_file_list);
13245 freeSetupFileHash(element_hash);
13248 for (i = 0; i < *num_elements; i++)
13249 Debug("editor", "element '%s' [%d]\n",
13250 element_info[(*elements)[i]].token_name, (*elements)[i]);
13254 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13257 SetupFileHash *setup_file_hash = NULL;
13258 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13259 char *filename_music, *filename_prefix, *filename_info;
13265 token_to_value_ptr[] =
13267 { "title_header", &tmp_music_file_info.title_header },
13268 { "artist_header", &tmp_music_file_info.artist_header },
13269 { "album_header", &tmp_music_file_info.album_header },
13270 { "year_header", &tmp_music_file_info.year_header },
13271 { "played_header", &tmp_music_file_info.played_header },
13273 { "title", &tmp_music_file_info.title },
13274 { "artist", &tmp_music_file_info.artist },
13275 { "album", &tmp_music_file_info.album },
13276 { "year", &tmp_music_file_info.year },
13277 { "played", &tmp_music_file_info.played },
13283 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13284 getCustomMusicFilename(basename));
13286 if (filename_music == NULL)
13289 // ---------- try to replace file extension ----------
13291 filename_prefix = getStringCopy(filename_music);
13292 if (strrchr(filename_prefix, '.') != NULL)
13293 *strrchr(filename_prefix, '.') = '\0';
13294 filename_info = getStringCat2(filename_prefix, ".txt");
13296 if (fileExists(filename_info))
13297 setup_file_hash = loadSetupFileHash(filename_info);
13299 free(filename_prefix);
13300 free(filename_info);
13302 if (setup_file_hash == NULL)
13304 // ---------- try to add file extension ----------
13306 filename_prefix = getStringCopy(filename_music);
13307 filename_info = getStringCat2(filename_prefix, ".txt");
13309 if (fileExists(filename_info))
13310 setup_file_hash = loadSetupFileHash(filename_info);
13312 free(filename_prefix);
13313 free(filename_info);
13316 if (setup_file_hash == NULL)
13319 // ---------- music file info found ----------
13321 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13323 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13325 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13327 *token_to_value_ptr[i].value_ptr =
13328 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13331 tmp_music_file_info.basename = getStringCopy(basename);
13332 tmp_music_file_info.music = music;
13333 tmp_music_file_info.is_sound = is_sound;
13335 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13336 *new_music_file_info = tmp_music_file_info;
13338 return new_music_file_info;
13341 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13343 return get_music_file_info_ext(basename, music, FALSE);
13346 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13348 return get_music_file_info_ext(basename, sound, TRUE);
13351 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13352 char *basename, boolean is_sound)
13354 for (; list != NULL; list = list->next)
13355 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13361 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13363 return music_info_listed_ext(list, basename, FALSE);
13366 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13368 return music_info_listed_ext(list, basename, TRUE);
13371 void LoadMusicInfo(void)
13373 int num_music_noconf = getMusicListSize_NoConf();
13374 int num_music = getMusicListSize();
13375 int num_sounds = getSoundListSize();
13376 struct FileInfo *music, *sound;
13377 struct MusicFileInfo *next, **new;
13381 while (music_file_info != NULL)
13383 next = music_file_info->next;
13385 checked_free(music_file_info->basename);
13387 checked_free(music_file_info->title_header);
13388 checked_free(music_file_info->artist_header);
13389 checked_free(music_file_info->album_header);
13390 checked_free(music_file_info->year_header);
13391 checked_free(music_file_info->played_header);
13393 checked_free(music_file_info->title);
13394 checked_free(music_file_info->artist);
13395 checked_free(music_file_info->album);
13396 checked_free(music_file_info->year);
13397 checked_free(music_file_info->played);
13399 free(music_file_info);
13401 music_file_info = next;
13404 new = &music_file_info;
13406 // get (configured or unconfigured) music file info for all levels
13407 for (i = leveldir_current->first_level;
13408 i <= leveldir_current->last_level; i++)
13412 if (levelset.music[i] != MUS_UNDEFINED)
13414 // get music file info for configured level music
13415 music_nr = levelset.music[i];
13417 else if (num_music_noconf > 0)
13419 // get music file info for unconfigured level music
13420 int level_pos = i - leveldir_current->first_level;
13422 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13429 char *basename = getMusicInfoEntryFilename(music_nr);
13431 if (basename == NULL)
13434 if (!music_info_listed(music_file_info, basename))
13436 *new = get_music_file_info(basename, music_nr);
13439 new = &(*new)->next;
13443 // get music file info for all remaining configured music files
13444 for (i = 0; i < num_music; i++)
13446 music = getMusicListEntry(i);
13448 if (music->filename == NULL)
13451 if (strEqual(music->filename, UNDEFINED_FILENAME))
13454 // a configured file may be not recognized as music
13455 if (!FileIsMusic(music->filename))
13458 if (!music_info_listed(music_file_info, music->filename))
13460 *new = get_music_file_info(music->filename, i);
13463 new = &(*new)->next;
13467 // get sound file info for all configured sound files
13468 for (i = 0; i < num_sounds; i++)
13470 sound = getSoundListEntry(i);
13472 if (sound->filename == NULL)
13475 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13478 // a configured file may be not recognized as sound
13479 if (!FileIsSound(sound->filename))
13482 if (!sound_info_listed(music_file_info, sound->filename))
13484 *new = get_sound_file_info(sound->filename, i);
13486 new = &(*new)->next;
13490 // add pointers to previous list nodes
13492 struct MusicFileInfo *node = music_file_info;
13494 while (node != NULL)
13497 node->next->prev = node;
13503 static void add_helpanim_entry(int element, int action, int direction,
13504 int delay, int *num_list_entries)
13506 struct HelpAnimInfo *new_list_entry;
13507 (*num_list_entries)++;
13510 checked_realloc(helpanim_info,
13511 *num_list_entries * sizeof(struct HelpAnimInfo));
13512 new_list_entry = &helpanim_info[*num_list_entries - 1];
13514 new_list_entry->element = element;
13515 new_list_entry->action = action;
13516 new_list_entry->direction = direction;
13517 new_list_entry->delay = delay;
13520 static void print_unknown_token(char *filename, char *token, int token_nr)
13525 Warn("unknown token(s) found in config file:");
13526 Warn("- config file: '%s'", filename);
13529 Warn("- token: '%s'", token);
13532 static void print_unknown_token_end(int token_nr)
13538 void LoadHelpAnimInfo(void)
13540 char *filename = getHelpAnimFilename();
13541 SetupFileList *setup_file_list = NULL, *list;
13542 SetupFileHash *element_hash, *action_hash, *direction_hash;
13543 int num_list_entries = 0;
13544 int num_unknown_tokens = 0;
13547 if (fileExists(filename))
13548 setup_file_list = loadSetupFileList(filename);
13550 if (setup_file_list == NULL)
13552 // use reliable default values from static configuration
13553 SetupFileList *insert_ptr;
13555 insert_ptr = setup_file_list =
13556 newSetupFileList(helpanim_config[0].token,
13557 helpanim_config[0].value);
13559 for (i = 1; helpanim_config[i].token; i++)
13560 insert_ptr = addListEntry(insert_ptr,
13561 helpanim_config[i].token,
13562 helpanim_config[i].value);
13565 element_hash = newSetupFileHash();
13566 action_hash = newSetupFileHash();
13567 direction_hash = newSetupFileHash();
13569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13570 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13572 for (i = 0; i < NUM_ACTIONS; i++)
13573 setHashEntry(action_hash, element_action_info[i].suffix,
13574 i_to_a(element_action_info[i].value));
13576 // do not store direction index (bit) here, but direction value!
13577 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13578 setHashEntry(direction_hash, element_direction_info[i].suffix,
13579 i_to_a(1 << element_direction_info[i].value));
13581 for (list = setup_file_list; list != NULL; list = list->next)
13583 char *element_token, *action_token, *direction_token;
13584 char *element_value, *action_value, *direction_value;
13585 int delay = atoi(list->value);
13587 if (strEqual(list->token, "end"))
13589 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13594 /* first try to break element into element/action/direction parts;
13595 if this does not work, also accept combined "element[.act][.dir]"
13596 elements (like "dynamite.active"), which are unique elements */
13598 if (strchr(list->token, '.') == NULL) // token contains no '.'
13600 element_value = getHashEntry(element_hash, list->token);
13601 if (element_value != NULL) // element found
13602 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13603 &num_list_entries);
13606 // no further suffixes found -- this is not an element
13607 print_unknown_token(filename, list->token, num_unknown_tokens++);
13613 // token has format "<prefix>.<something>"
13615 action_token = strchr(list->token, '.'); // suffix may be action ...
13616 direction_token = action_token; // ... or direction
13618 element_token = getStringCopy(list->token);
13619 *strchr(element_token, '.') = '\0';
13621 element_value = getHashEntry(element_hash, element_token);
13623 if (element_value == NULL) // this is no element
13625 element_value = getHashEntry(element_hash, list->token);
13626 if (element_value != NULL) // combined element found
13627 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13628 &num_list_entries);
13630 print_unknown_token(filename, list->token, num_unknown_tokens++);
13632 free(element_token);
13637 action_value = getHashEntry(action_hash, action_token);
13639 if (action_value != NULL) // action found
13641 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13642 &num_list_entries);
13644 free(element_token);
13649 direction_value = getHashEntry(direction_hash, direction_token);
13651 if (direction_value != NULL) // direction found
13653 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13654 &num_list_entries);
13656 free(element_token);
13661 if (strchr(action_token + 1, '.') == NULL)
13663 // no further suffixes found -- this is not an action nor direction
13665 element_value = getHashEntry(element_hash, list->token);
13666 if (element_value != NULL) // combined element found
13667 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13668 &num_list_entries);
13670 print_unknown_token(filename, list->token, num_unknown_tokens++);
13672 free(element_token);
13677 // token has format "<prefix>.<suffix>.<something>"
13679 direction_token = strchr(action_token + 1, '.');
13681 action_token = getStringCopy(action_token);
13682 *strchr(action_token + 1, '.') = '\0';
13684 action_value = getHashEntry(action_hash, action_token);
13686 if (action_value == NULL) // this is no action
13688 element_value = getHashEntry(element_hash, list->token);
13689 if (element_value != NULL) // combined element found
13690 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13691 &num_list_entries);
13693 print_unknown_token(filename, list->token, num_unknown_tokens++);
13695 free(element_token);
13696 free(action_token);
13701 direction_value = getHashEntry(direction_hash, direction_token);
13703 if (direction_value != NULL) // direction found
13705 add_helpanim_entry(atoi(element_value), atoi(action_value),
13706 atoi(direction_value), delay, &num_list_entries);
13708 free(element_token);
13709 free(action_token);
13714 // this is no direction
13716 element_value = getHashEntry(element_hash, list->token);
13717 if (element_value != NULL) // combined element found
13718 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13719 &num_list_entries);
13721 print_unknown_token(filename, list->token, num_unknown_tokens++);
13723 free(element_token);
13724 free(action_token);
13727 print_unknown_token_end(num_unknown_tokens);
13729 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13730 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13732 freeSetupFileList(setup_file_list);
13733 freeSetupFileHash(element_hash);
13734 freeSetupFileHash(action_hash);
13735 freeSetupFileHash(direction_hash);
13738 for (i = 0; i < num_list_entries; i++)
13739 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13740 EL_NAME(helpanim_info[i].element),
13741 helpanim_info[i].element,
13742 helpanim_info[i].action,
13743 helpanim_info[i].direction,
13744 helpanim_info[i].delay);
13748 void LoadHelpTextInfo(void)
13750 char *filename = getHelpTextFilename();
13753 if (helptext_info != NULL)
13755 freeSetupFileHash(helptext_info);
13756 helptext_info = NULL;
13759 if (fileExists(filename))
13760 helptext_info = loadSetupFileHash(filename);
13762 if (helptext_info == NULL)
13764 // use reliable default values from static configuration
13765 helptext_info = newSetupFileHash();
13767 for (i = 0; helptext_config[i].token; i++)
13768 setHashEntry(helptext_info,
13769 helptext_config[i].token,
13770 helptext_config[i].value);
13774 BEGIN_HASH_ITERATION(helptext_info, itr)
13776 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13777 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13779 END_HASH_ITERATION(hash, itr)
13784 // ----------------------------------------------------------------------------
13786 // ----------------------------------------------------------------------------
13788 #define MAX_NUM_CONVERT_LEVELS 1000
13790 void ConvertLevels(void)
13792 static LevelDirTree *convert_leveldir = NULL;
13793 static int convert_level_nr = -1;
13794 static int num_levels_handled = 0;
13795 static int num_levels_converted = 0;
13796 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13799 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13800 global.convert_leveldir);
13802 if (convert_leveldir == NULL)
13803 Fail("no such level identifier: '%s'", global.convert_leveldir);
13805 leveldir_current = convert_leveldir;
13807 if (global.convert_level_nr != -1)
13809 convert_leveldir->first_level = global.convert_level_nr;
13810 convert_leveldir->last_level = global.convert_level_nr;
13813 convert_level_nr = convert_leveldir->first_level;
13815 PrintLine("=", 79);
13816 Print("Converting levels\n");
13817 PrintLine("-", 79);
13818 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13819 Print("Level series name: '%s'\n", convert_leveldir->name);
13820 Print("Level series author: '%s'\n", convert_leveldir->author);
13821 Print("Number of levels: %d\n", convert_leveldir->levels);
13822 PrintLine("=", 79);
13825 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13826 levels_failed[i] = FALSE;
13828 while (convert_level_nr <= convert_leveldir->last_level)
13830 char *level_filename;
13833 level_nr = convert_level_nr++;
13835 Print("Level %03d: ", level_nr);
13837 LoadLevel(level_nr);
13838 if (level.no_level_file || level.no_valid_file)
13840 Print("(no level)\n");
13844 Print("converting level ... ");
13847 // special case: conversion of some EMC levels as requested by ACME
13848 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13851 level_filename = getDefaultLevelFilename(level_nr);
13852 new_level = !fileExists(level_filename);
13856 SaveLevel(level_nr);
13858 num_levels_converted++;
13860 Print("converted.\n");
13864 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13865 levels_failed[level_nr] = TRUE;
13867 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13870 num_levels_handled++;
13874 PrintLine("=", 79);
13875 Print("Number of levels handled: %d\n", num_levels_handled);
13876 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13877 (num_levels_handled ?
13878 num_levels_converted * 100 / num_levels_handled : 0));
13879 PrintLine("-", 79);
13880 Print("Summary (for automatic parsing by scripts):\n");
13881 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13882 convert_leveldir->identifier, num_levels_converted,
13883 num_levels_handled,
13884 (num_levels_handled ?
13885 num_levels_converted * 100 / num_levels_handled : 0));
13887 if (num_levels_handled != num_levels_converted)
13889 Print(", FAILED:");
13890 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13891 if (levels_failed[i])
13896 PrintLine("=", 79);
13898 CloseAllAndExit(0);
13902 // ----------------------------------------------------------------------------
13903 // create and save images for use in level sketches (raw BMP format)
13904 // ----------------------------------------------------------------------------
13906 void CreateLevelSketchImages(void)
13912 InitElementPropertiesGfxElement();
13914 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13915 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13917 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13919 int element = getMappedElement(i);
13920 char basename1[16];
13921 char basename2[16];
13925 sprintf(basename1, "%04d.bmp", i);
13926 sprintf(basename2, "%04ds.bmp", i);
13928 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13929 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13931 DrawSizedElement(0, 0, element, TILESIZE);
13932 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13934 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13935 Fail("cannot save level sketch image file '%s'", filename1);
13937 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13938 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13940 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13941 Fail("cannot save level sketch image file '%s'", filename2);
13946 // create corresponding SQL statements (for normal and small images)
13949 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13950 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13953 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13954 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13956 // optional: create content for forum level sketch demonstration post
13958 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13961 FreeBitmap(bitmap1);
13962 FreeBitmap(bitmap2);
13965 fprintf(stderr, "\n");
13967 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13969 CloseAllAndExit(0);
13973 // ----------------------------------------------------------------------------
13974 // create and save images for element collecting animations (raw BMP format)
13975 // ----------------------------------------------------------------------------
13977 static boolean createCollectImage(int element)
13979 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13982 void CreateCollectElementImages(void)
13986 int anim_frames = num_steps - 1;
13987 int tile_size = TILESIZE;
13988 int anim_width = tile_size * anim_frames;
13989 int anim_height = tile_size;
13990 int num_collect_images = 0;
13991 int pos_collect_images = 0;
13993 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13994 if (createCollectImage(i))
13995 num_collect_images++;
13997 Info("Creating %d element collecting animation images ...",
13998 num_collect_images);
14000 int dst_width = anim_width * 2;
14001 int dst_height = anim_height * num_collect_images / 2;
14002 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14003 char *basename_bmp = "RocksCollect.bmp";
14004 char *basename_png = "RocksCollect.png";
14005 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14006 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14007 int len_filename_bmp = strlen(filename_bmp);
14008 int len_filename_png = strlen(filename_png);
14009 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14010 char cmd_convert[max_command_len];
14012 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14016 // force using RGBA surface for destination bitmap
14017 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14018 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14020 dst_bitmap->surface =
14021 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14023 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14025 if (!createCollectImage(i))
14028 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14029 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14030 int graphic = el2img(i);
14031 char *token_name = element_info[i].token_name;
14032 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14033 Bitmap *src_bitmap;
14036 Info("- creating collecting image for '%s' ...", token_name);
14038 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14040 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14041 tile_size, tile_size, 0, 0);
14043 // force using RGBA surface for temporary bitmap (using transparent black)
14044 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14045 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14047 tmp_bitmap->surface =
14048 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14050 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14052 for (j = 0; j < anim_frames; j++)
14054 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14055 int frame_size = frame_size_final * num_steps;
14056 int offset = (tile_size - frame_size_final) / 2;
14057 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14059 while (frame_size > frame_size_final)
14063 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14065 FreeBitmap(frame_bitmap);
14067 frame_bitmap = half_bitmap;
14070 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14071 frame_size_final, frame_size_final,
14072 dst_x + j * tile_size + offset, dst_y + offset);
14074 FreeBitmap(frame_bitmap);
14077 tmp_bitmap->surface_masked = NULL;
14079 FreeBitmap(tmp_bitmap);
14081 pos_collect_images++;
14084 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14085 Fail("cannot save element collecting image file '%s'", filename_bmp);
14087 FreeBitmap(dst_bitmap);
14089 Info("Converting image file from BMP to PNG ...");
14091 if (system(cmd_convert) != 0)
14092 Fail("converting image file failed");
14094 unlink(filename_bmp);
14098 CloseAllAndExit(0);
14102 // ----------------------------------------------------------------------------
14103 // create and save images for custom and group elements (raw BMP format)
14104 // ----------------------------------------------------------------------------
14106 void CreateCustomElementImages(char *directory)
14108 char *src_basename = "RocksCE-template.ilbm";
14109 char *dst_basename = "RocksCE.bmp";
14110 char *src_filename = getPath2(directory, src_basename);
14111 char *dst_filename = getPath2(directory, dst_basename);
14112 Bitmap *src_bitmap;
14114 int yoffset_ce = 0;
14115 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14118 InitVideoDefaults();
14120 ReCreateBitmap(&backbuffer, video.width, video.height);
14122 src_bitmap = LoadImage(src_filename);
14124 bitmap = CreateBitmap(TILEX * 16 * 2,
14125 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14135 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14136 TILEX * x, TILEY * y + yoffset_ce);
14138 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14140 TILEX * x + TILEX * 16,
14141 TILEY * y + yoffset_ce);
14143 for (j = 2; j >= 0; j--)
14147 BlitBitmap(src_bitmap, bitmap,
14148 TILEX + c * 7, 0, 6, 10,
14149 TILEX * x + 6 + j * 7,
14150 TILEY * y + 11 + yoffset_ce);
14152 BlitBitmap(src_bitmap, bitmap,
14153 TILEX + c * 8, TILEY, 6, 10,
14154 TILEX * 16 + TILEX * x + 6 + j * 8,
14155 TILEY * y + 10 + yoffset_ce);
14161 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14168 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14169 TILEX * x, TILEY * y + yoffset_ge);
14171 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14173 TILEX * x + TILEX * 16,
14174 TILEY * y + yoffset_ge);
14176 for (j = 1; j >= 0; j--)
14180 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14181 TILEX * x + 6 + j * 10,
14182 TILEY * y + 11 + yoffset_ge);
14184 BlitBitmap(src_bitmap, bitmap,
14185 TILEX + c * 8, TILEY + 12, 6, 10,
14186 TILEX * 16 + TILEX * x + 10 + j * 8,
14187 TILEY * y + 10 + yoffset_ge);
14193 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14194 Fail("cannot save CE graphics file '%s'", dst_filename);
14196 FreeBitmap(bitmap);
14198 CloseAllAndExit(0);