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
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
927 &li.rotate_mm_ball_content, TRUE
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.explode_mm_ball, FALSE
936 EL_MM_STEEL_BLOCK, -1,
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.mm_time_block, 75
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_ELEM_BONUS], 10
946 // ---------- unused values -------------------------------------------------
949 EL_UNKNOWN, SAVE_CONF_NEVER,
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.score[SC_UNKNOWN_15], 10
961 static struct LevelFileConfigInfo chunk_config_NOTE[] =
965 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
966 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
970 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
971 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
977 &xx_envelope.autowrap, FALSE
981 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
982 &xx_envelope.centered, FALSE
987 TYPE_STRING, CONF_VALUE_BYTES(1),
988 &xx_envelope.text, -1, NULL,
989 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
990 &xx_default_string_empty[0]
1000 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1004 TYPE_STRING, CONF_VALUE_BYTES(1),
1005 &xx_ei.description[0], -1,
1006 &yy_ei.description[0],
1007 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1008 &xx_default_description[0]
1013 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1014 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1015 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1017 #if ENABLE_RESERVED_CODE
1018 // (reserved for later use)
1021 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1022 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1023 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1029 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1030 &xx_ei.use_gfx_element, FALSE,
1031 &yy_ei.use_gfx_element
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1036 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1037 &yy_ei.gfx_element_initial
1042 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1043 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1044 &yy_ei.access_direction
1049 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1050 &xx_ei.collect_score_initial, 10,
1051 &yy_ei.collect_score_initial
1055 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1056 &xx_ei.collect_count_initial, 1,
1057 &yy_ei.collect_count_initial
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1063 &xx_ei.ce_value_fixed_initial, 0,
1064 &yy_ei.ce_value_fixed_initial
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1069 &xx_ei.ce_value_random_initial, 0,
1070 &yy_ei.ce_value_random_initial
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1075 &xx_ei.use_last_ce_value, FALSE,
1076 &yy_ei.use_last_ce_value
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1082 &xx_ei.push_delay_fixed, 8,
1083 &yy_ei.push_delay_fixed
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1088 &xx_ei.push_delay_random, 8,
1089 &yy_ei.push_delay_random
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1094 &xx_ei.drop_delay_fixed, 0,
1095 &yy_ei.drop_delay_fixed
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1100 &xx_ei.drop_delay_random, 0,
1101 &yy_ei.drop_delay_random
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1106 &xx_ei.move_delay_fixed, 0,
1107 &yy_ei.move_delay_fixed
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1112 &xx_ei.move_delay_random, 0,
1113 &yy_ei.move_delay_random
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1118 &xx_ei.step_delay_fixed, 0,
1119 &yy_ei.step_delay_fixed
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1124 &xx_ei.step_delay_random, 0,
1125 &yy_ei.step_delay_random
1130 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1131 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1136 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1137 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1138 &yy_ei.move_direction_initial
1142 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1143 &xx_ei.move_stepsize, TILEX / 8,
1144 &yy_ei.move_stepsize
1149 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1150 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1151 &yy_ei.move_enter_element
1155 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1156 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1157 &yy_ei.move_leave_element
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1162 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1163 &yy_ei.move_leave_type
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1169 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1170 &yy_ei.slippery_type
1175 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1176 &xx_ei.explosion_type, EXPLODES_3X3,
1177 &yy_ei.explosion_type
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1182 &xx_ei.explosion_delay, 16,
1183 &yy_ei.explosion_delay
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1188 &xx_ei.ignition_delay, 8,
1189 &yy_ei.ignition_delay
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1195 &xx_ei.content, EL_EMPTY_SPACE,
1197 &xx_num_contents, 1, 1
1200 // ---------- "num_change_pages" must be the last entry ---------------------
1203 -1, SAVE_CONF_ALWAYS,
1204 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1205 &xx_ei.num_change_pages, 1,
1206 &yy_ei.num_change_pages
1217 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1219 // ---------- "current_change_page" must be the first entry -----------------
1222 -1, SAVE_CONF_ALWAYS,
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1224 &xx_current_change_page, -1
1227 // ---------- (the remaining entries can be in any order) -------------------
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1232 &xx_change.can_change, FALSE
1237 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1238 &xx_event_bits[0], 0
1242 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1243 &xx_event_bits[1], 0
1248 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1249 &xx_change.trigger_player, CH_PLAYER_ANY
1253 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1254 &xx_change.trigger_side, CH_SIDE_ANY
1258 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1259 &xx_change.trigger_page, CH_PAGE_ANY
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1265 &xx_change.target_element, EL_EMPTY_SPACE
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1271 &xx_change.delay_fixed, 0
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1276 &xx_change.delay_random, 0
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1281 &xx_change.delay_frames, FRAMES_PER_SECOND
1286 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1287 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1293 &xx_change.explode, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1298 &xx_change.use_target_content, FALSE
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1303 &xx_change.only_if_complete, FALSE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1308 &xx_change.use_random_replace, FALSE
1312 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1313 &xx_change.random_percentage, 100
1317 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1318 &xx_change.replace_when, CP_WHEN_EMPTY
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1324 &xx_change.has_action, FALSE
1328 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1329 &xx_change.action_type, CA_NO_ACTION
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1334 &xx_change.action_mode, CA_MODE_UNDEFINED
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1339 &xx_change.action_arg, CA_ARG_UNDEFINED
1344 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1345 &xx_change.action_element, EL_EMPTY_SPACE
1350 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1351 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1352 &xx_num_contents, 1, 1
1362 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1366 TYPE_STRING, CONF_VALUE_BYTES(1),
1367 &xx_ei.description[0], -1, NULL,
1368 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1369 &xx_default_description[0]
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1375 &xx_ei.use_gfx_element, FALSE
1379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1380 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1386 &xx_group.choice_mode, ANIM_RANDOM
1391 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1392 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1393 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1403 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1408 &xx_ei.use_gfx_element, FALSE
1412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1413 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1423 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1428 &li.block_snap_field, TRUE
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1433 &li.continuous_snapping, TRUE
1437 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1438 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1443 &li.use_start_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1448 &li.start_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1453 &li.use_artwork_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1458 &li.artwork_element[0], EL_PLAYER_1
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1463 &li.use_explosion_element[0], FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1468 &li.explosion_element[0], EL_PLAYER_1
1483 filetype_id_list[] =
1485 { LEVEL_FILE_TYPE_RND, "RND" },
1486 { LEVEL_FILE_TYPE_BD, "BD" },
1487 { LEVEL_FILE_TYPE_EM, "EM" },
1488 { LEVEL_FILE_TYPE_SP, "SP" },
1489 { LEVEL_FILE_TYPE_DX, "DX" },
1490 { LEVEL_FILE_TYPE_SB, "SB" },
1491 { LEVEL_FILE_TYPE_DC, "DC" },
1492 { LEVEL_FILE_TYPE_MM, "MM" },
1493 { LEVEL_FILE_TYPE_MM, "DF" },
1498 // ============================================================================
1499 // level file functions
1500 // ============================================================================
1502 static boolean check_special_flags(char *flag)
1504 if (strEqual(options.special_flags, flag) ||
1505 strEqual(leveldir_current->special_flags, flag))
1511 static struct DateInfo getCurrentDate(void)
1513 time_t epoch_seconds = time(NULL);
1514 struct tm *now = localtime(&epoch_seconds);
1515 struct DateInfo date;
1517 date.year = now->tm_year + 1900;
1518 date.month = now->tm_mon + 1;
1519 date.day = now->tm_mday;
1521 date.src = DATE_SRC_CLOCK;
1526 static void resetEventFlags(struct ElementChangeInfo *change)
1530 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1531 change->has_event[i] = FALSE;
1534 static void resetEventBits(void)
1538 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1539 xx_event_bits[i] = 0;
1542 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1546 /* important: only change event flag if corresponding event bit is set
1547 (this is because all xx_event_bits[] values are loaded separately,
1548 and all xx_event_bits[] values are set back to zero before loading
1549 another value xx_event_bits[x] (each value representing 32 flags)) */
1551 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1552 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1553 change->has_event[i] = TRUE;
1556 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1560 /* in contrast to the above function setEventFlagsFromEventBits(), it
1561 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1562 depending on the corresponding change->has_event[i] values here, as
1563 all xx_event_bits[] values are reset in resetEventBits() before */
1565 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1566 if (change->has_event[i])
1567 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1570 static char *getDefaultElementDescription(struct ElementInfo *ei)
1572 static char description[MAX_ELEMENT_NAME_LEN + 1];
1573 char *default_description = (ei->custom_description != NULL ?
1574 ei->custom_description :
1575 ei->editor_description);
1578 // always start with reliable default values
1579 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1580 description[i] = '\0';
1582 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1583 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1585 return &description[0];
1588 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1590 char *default_description = getDefaultElementDescription(ei);
1593 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1594 ei->description[i] = default_description[i];
1597 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1601 for (i = 0; conf[i].data_type != -1; i++)
1603 int default_value = conf[i].default_value;
1604 int data_type = conf[i].data_type;
1605 int conf_type = conf[i].conf_type;
1606 int byte_mask = conf_type & CONF_MASK_BYTES;
1608 if (byte_mask == CONF_MASK_MULTI_BYTES)
1610 int default_num_entities = conf[i].default_num_entities;
1611 int max_num_entities = conf[i].max_num_entities;
1613 *(int *)(conf[i].num_entities) = default_num_entities;
1615 if (data_type == TYPE_STRING)
1617 char *default_string = conf[i].default_string;
1618 char *string = (char *)(conf[i].value);
1620 strncpy(string, default_string, max_num_entities);
1622 else if (data_type == TYPE_ELEMENT_LIST)
1624 int *element_array = (int *)(conf[i].value);
1627 for (j = 0; j < max_num_entities; j++)
1628 element_array[j] = default_value;
1630 else if (data_type == TYPE_CONTENT_LIST)
1632 struct Content *content = (struct Content *)(conf[i].value);
1635 for (c = 0; c < max_num_entities; c++)
1636 for (y = 0; y < 3; y++)
1637 for (x = 0; x < 3; x++)
1638 content[c].e[x][y] = default_value;
1641 else // constant size configuration data (1, 2 or 4 bytes)
1643 if (data_type == TYPE_BOOLEAN)
1644 *(boolean *)(conf[i].value) = default_value;
1646 *(int *) (conf[i].value) = default_value;
1651 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1655 for (i = 0; conf[i].data_type != -1; i++)
1657 int data_type = conf[i].data_type;
1658 int conf_type = conf[i].conf_type;
1659 int byte_mask = conf_type & CONF_MASK_BYTES;
1661 if (byte_mask == CONF_MASK_MULTI_BYTES)
1663 int max_num_entities = conf[i].max_num_entities;
1665 if (data_type == TYPE_STRING)
1667 char *string = (char *)(conf[i].value);
1668 char *string_copy = (char *)(conf[i].value_copy);
1670 strncpy(string_copy, string, max_num_entities);
1672 else if (data_type == TYPE_ELEMENT_LIST)
1674 int *element_array = (int *)(conf[i].value);
1675 int *element_array_copy = (int *)(conf[i].value_copy);
1678 for (j = 0; j < max_num_entities; j++)
1679 element_array_copy[j] = element_array[j];
1681 else if (data_type == TYPE_CONTENT_LIST)
1683 struct Content *content = (struct Content *)(conf[i].value);
1684 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1687 for (c = 0; c < max_num_entities; c++)
1688 for (y = 0; y < 3; y++)
1689 for (x = 0; x < 3; x++)
1690 content_copy[c].e[x][y] = content[c].e[x][y];
1693 else // constant size configuration data (1, 2 or 4 bytes)
1695 if (data_type == TYPE_BOOLEAN)
1696 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1698 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1703 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1707 xx_ei = *ei_from; // copy element data into temporary buffer
1708 yy_ei = *ei_to; // copy element data into temporary buffer
1710 copyConfigFromConfigList(chunk_config_CUSX_base);
1715 // ---------- reinitialize and copy change pages ----------
1717 ei_to->num_change_pages = ei_from->num_change_pages;
1718 ei_to->current_change_page = ei_from->current_change_page;
1720 setElementChangePages(ei_to, ei_to->num_change_pages);
1722 for (i = 0; i < ei_to->num_change_pages; i++)
1723 ei_to->change_page[i] = ei_from->change_page[i];
1725 // ---------- copy group element info ----------
1726 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1727 *ei_to->group = *ei_from->group;
1729 // mark this custom element as modified
1730 ei_to->modified_settings = TRUE;
1733 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1735 int change_page_size = sizeof(struct ElementChangeInfo);
1737 ei->num_change_pages = MAX(1, change_pages);
1740 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1742 if (ei->current_change_page >= ei->num_change_pages)
1743 ei->current_change_page = ei->num_change_pages - 1;
1745 ei->change = &ei->change_page[ei->current_change_page];
1748 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1750 xx_change = *change; // copy change data into temporary buffer
1752 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1754 *change = xx_change;
1756 resetEventFlags(change);
1758 change->direct_action = 0;
1759 change->other_action = 0;
1761 change->pre_change_function = NULL;
1762 change->change_function = NULL;
1763 change->post_change_function = NULL;
1766 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1770 li = *level; // copy level data into temporary buffer
1771 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1772 *level = li; // copy temporary buffer back to level data
1774 setLevelInfoToDefaults_BD();
1775 setLevelInfoToDefaults_EM();
1776 setLevelInfoToDefaults_SP();
1777 setLevelInfoToDefaults_MM();
1779 level->native_bd_level = &native_bd_level;
1780 level->native_em_level = &native_em_level;
1781 level->native_sp_level = &native_sp_level;
1782 level->native_mm_level = &native_mm_level;
1784 level->file_version = FILE_VERSION_ACTUAL;
1785 level->game_version = GAME_VERSION_ACTUAL;
1787 level->creation_date = getCurrentDate();
1789 level->encoding_16bit_field = TRUE;
1790 level->encoding_16bit_yamyam = TRUE;
1791 level->encoding_16bit_amoeba = TRUE;
1793 // clear level name and level author string buffers
1794 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1795 level->name[i] = '\0';
1796 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1797 level->author[i] = '\0';
1799 // set level name and level author to default values
1800 strcpy(level->name, NAMELESS_LEVEL_NAME);
1801 strcpy(level->author, ANONYMOUS_NAME);
1803 // set level playfield to playable default level with player and exit
1804 for (x = 0; x < MAX_LEV_FIELDX; x++)
1805 for (y = 0; y < MAX_LEV_FIELDY; y++)
1806 level->field[x][y] = EL_SAND;
1808 level->field[0][0] = EL_PLAYER_1;
1809 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1811 BorderElement = EL_STEELWALL;
1813 // detect custom elements when loading them
1814 level->file_has_custom_elements = FALSE;
1816 // set all bug compatibility flags to "false" => do not emulate this bug
1817 level->use_action_after_change_bug = FALSE;
1819 if (leveldir_current)
1821 // try to determine better author name than 'anonymous'
1822 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1824 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1829 switch (LEVELCLASS(leveldir_current))
1831 case LEVELCLASS_TUTORIAL:
1832 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1835 case LEVELCLASS_CONTRIB:
1836 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1837 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1840 case LEVELCLASS_PRIVATE:
1841 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1842 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1846 // keep default value
1853 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1855 static boolean clipboard_elements_initialized = FALSE;
1858 InitElementPropertiesStatic();
1860 li = *level; // copy level data into temporary buffer
1861 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1862 *level = li; // copy temporary buffer back to level data
1864 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1867 struct ElementInfo *ei = &element_info[element];
1869 if (element == EL_MM_GRAY_BALL)
1871 struct LevelInfo_MM *level_mm = level->native_mm_level;
1874 for (j = 0; j < level->num_mm_ball_contents; j++)
1875 level->mm_ball_content[j] =
1876 map_element_MM_to_RND(level_mm->ball_content[j]);
1879 // never initialize clipboard elements after the very first time
1880 // (to be able to use clipboard elements between several levels)
1881 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1884 if (IS_ENVELOPE(element))
1886 int envelope_nr = element - EL_ENVELOPE_1;
1888 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1890 level->envelope[envelope_nr] = xx_envelope;
1893 if (IS_CUSTOM_ELEMENT(element) ||
1894 IS_GROUP_ELEMENT(element) ||
1895 IS_INTERNAL_ELEMENT(element))
1897 xx_ei = *ei; // copy element data into temporary buffer
1899 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1904 setElementChangePages(ei, 1);
1905 setElementChangeInfoToDefaults(ei->change);
1907 if (IS_CUSTOM_ELEMENT(element) ||
1908 IS_GROUP_ELEMENT(element))
1910 setElementDescriptionToDefault(ei);
1912 ei->modified_settings = FALSE;
1915 if (IS_CUSTOM_ELEMENT(element) ||
1916 IS_INTERNAL_ELEMENT(element))
1918 // internal values used in level editor
1920 ei->access_type = 0;
1921 ei->access_layer = 0;
1922 ei->access_protected = 0;
1923 ei->walk_to_action = 0;
1924 ei->smash_targets = 0;
1927 ei->can_explode_by_fire = FALSE;
1928 ei->can_explode_smashed = FALSE;
1929 ei->can_explode_impact = FALSE;
1931 ei->current_change_page = 0;
1934 if (IS_GROUP_ELEMENT(element) ||
1935 IS_INTERNAL_ELEMENT(element))
1937 struct ElementGroupInfo *group;
1939 // initialize memory for list of elements in group
1940 if (ei->group == NULL)
1941 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1945 xx_group = *group; // copy group data into temporary buffer
1947 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1952 if (IS_EMPTY_ELEMENT(element) ||
1953 IS_INTERNAL_ELEMENT(element))
1955 xx_ei = *ei; // copy element data into temporary buffer
1957 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1963 clipboard_elements_initialized = TRUE;
1966 static void setLevelInfoToDefaults(struct LevelInfo *level,
1967 boolean level_info_only,
1968 boolean reset_file_status)
1970 setLevelInfoToDefaults_Level(level);
1972 if (!level_info_only)
1973 setLevelInfoToDefaults_Elements(level);
1975 if (reset_file_status)
1977 level->no_valid_file = FALSE;
1978 level->no_level_file = FALSE;
1981 level->changed = FALSE;
1984 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1986 level_file_info->nr = 0;
1987 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1988 level_file_info->packed = FALSE;
1990 setString(&level_file_info->basename, NULL);
1991 setString(&level_file_info->filename, NULL);
1994 int getMappedElement_SB(int, boolean);
1996 static void ActivateLevelTemplate(void)
2000 if (check_special_flags("load_xsb_to_ces"))
2002 // fill smaller playfields with padding "beyond border wall" elements
2003 if (level.fieldx < level_template.fieldx ||
2004 level.fieldy < level_template.fieldy)
2006 short field[level.fieldx][level.fieldy];
2007 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2008 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2009 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2010 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2012 // copy old playfield (which is smaller than the visible area)
2013 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2014 field[x][y] = level.field[x][y];
2016 // fill new, larger playfield with "beyond border wall" elements
2017 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2018 level.field[x][y] = getMappedElement_SB('_', TRUE);
2020 // copy the old playfield to the middle of the new playfield
2021 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2022 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2024 level.fieldx = new_fieldx;
2025 level.fieldy = new_fieldy;
2029 // Currently there is no special action needed to activate the template
2030 // data, because 'element_info' property settings overwrite the original
2031 // level data, while all other variables do not change.
2033 // Exception: 'from_level_template' elements in the original level playfield
2034 // are overwritten with the corresponding elements at the same position in
2035 // playfield from the level template.
2037 for (x = 0; x < level.fieldx; x++)
2038 for (y = 0; y < level.fieldy; y++)
2039 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2040 level.field[x][y] = level_template.field[x][y];
2042 if (check_special_flags("load_xsb_to_ces"))
2044 struct LevelInfo level_backup = level;
2046 // overwrite all individual level settings from template level settings
2047 level = level_template;
2049 // restore level file info
2050 level.file_info = level_backup.file_info;
2052 // restore playfield size
2053 level.fieldx = level_backup.fieldx;
2054 level.fieldy = level_backup.fieldy;
2056 // restore playfield content
2057 for (x = 0; x < level.fieldx; x++)
2058 for (y = 0; y < level.fieldy; y++)
2059 level.field[x][y] = level_backup.field[x][y];
2061 // restore name and author from individual level
2062 strcpy(level.name, level_backup.name);
2063 strcpy(level.author, level_backup.author);
2065 // restore flag "use_custom_template"
2066 level.use_custom_template = level_backup.use_custom_template;
2070 static boolean checkForPackageFromBasename_BD(char *basename)
2072 // check for native BD level file extensions
2073 if (!strSuffixLower(basename, ".bd") &&
2074 !strSuffixLower(basename, ".bdr") &&
2075 !strSuffixLower(basename, ".brc") &&
2076 !strSuffixLower(basename, ".gds"))
2079 // check for standard single-level BD files (like "001.bd")
2080 if (strSuffixLower(basename, ".bd") &&
2081 strlen(basename) == 6 &&
2082 basename[0] >= '0' && basename[0] <= '9' &&
2083 basename[1] >= '0' && basename[1] <= '9' &&
2084 basename[2] >= '0' && basename[2] <= '9')
2087 // this is a level package in native BD file format
2091 static char *getLevelFilenameFromBasename(char *basename)
2093 static char *filename = NULL;
2095 checked_free(filename);
2097 filename = getPath2(getCurrentLevelDir(), basename);
2102 static int getFileTypeFromBasename(char *basename)
2104 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2106 static char *filename = NULL;
2107 struct stat file_status;
2109 // ---------- try to determine file type from filename ----------
2111 // check for typical filename of a Supaplex level package file
2112 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2113 return LEVEL_FILE_TYPE_SP;
2115 // check for typical filename of a Diamond Caves II level package file
2116 if (strSuffixLower(basename, ".dc") ||
2117 strSuffixLower(basename, ".dc2"))
2118 return LEVEL_FILE_TYPE_DC;
2120 // check for typical filename of a Sokoban level package file
2121 if (strSuffixLower(basename, ".xsb") &&
2122 strchr(basename, '%') == NULL)
2123 return LEVEL_FILE_TYPE_SB;
2125 // check for typical filename of a Boulder Dash (GDash) level package file
2126 if (checkForPackageFromBasename_BD(basename))
2127 return LEVEL_FILE_TYPE_BD;
2129 // ---------- try to determine file type from filesize ----------
2131 checked_free(filename);
2132 filename = getPath2(getCurrentLevelDir(), basename);
2134 if (stat(filename, &file_status) == 0)
2136 // check for typical filesize of a Supaplex level package file
2137 if (file_status.st_size == 170496)
2138 return LEVEL_FILE_TYPE_SP;
2141 return LEVEL_FILE_TYPE_UNKNOWN;
2144 static int getFileTypeFromMagicBytes(char *filename, int type)
2148 if ((file = openFile(filename, MODE_READ)))
2150 char chunk_name[CHUNK_ID_LEN + 1];
2152 getFileChunkBE(file, chunk_name, NULL);
2154 if (strEqual(chunk_name, "MMII") ||
2155 strEqual(chunk_name, "MIRR"))
2156 type = LEVEL_FILE_TYPE_MM;
2164 static boolean checkForPackageFromBasename(char *basename)
2166 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2167 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2169 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2172 static char *getSingleLevelBasenameExt(int nr, char *extension)
2174 static char basename[MAX_FILENAME_LEN];
2177 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2179 sprintf(basename, "%03d.%s", nr, extension);
2184 static char *getSingleLevelBasename(int nr)
2186 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2189 static char *getPackedLevelBasename(int type)
2191 static char basename[MAX_FILENAME_LEN];
2192 char *directory = getCurrentLevelDir();
2194 DirectoryEntry *dir_entry;
2196 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2198 if ((dir = openDirectory(directory)) == NULL)
2200 Warn("cannot read current level directory '%s'", directory);
2205 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2207 char *entry_basename = dir_entry->basename;
2208 int entry_type = getFileTypeFromBasename(entry_basename);
2210 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2212 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2215 strcpy(basename, entry_basename);
2222 closeDirectory(dir);
2227 static char *getSingleLevelFilename(int nr)
2229 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2232 #if ENABLE_UNUSED_CODE
2233 static char *getPackedLevelFilename(int type)
2235 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2239 char *getDefaultLevelFilename(int nr)
2241 return getSingleLevelFilename(nr);
2244 #if ENABLE_UNUSED_CODE
2245 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2249 lfi->packed = FALSE;
2251 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2252 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2256 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2257 int type, char *format, ...)
2259 static char basename[MAX_FILENAME_LEN];
2262 va_start(ap, format);
2263 vsprintf(basename, format, ap);
2267 lfi->packed = FALSE;
2269 setString(&lfi->basename, basename);
2270 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2273 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2279 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2280 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2283 static int getFiletypeFromID(char *filetype_id)
2285 char *filetype_id_lower;
2286 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2289 if (filetype_id == NULL)
2290 return LEVEL_FILE_TYPE_UNKNOWN;
2292 filetype_id_lower = getStringToLower(filetype_id);
2294 for (i = 0; filetype_id_list[i].id != NULL; i++)
2296 char *id_lower = getStringToLower(filetype_id_list[i].id);
2298 if (strEqual(filetype_id_lower, id_lower))
2299 filetype = filetype_id_list[i].filetype;
2303 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2307 free(filetype_id_lower);
2312 char *getLocalLevelTemplateFilename(void)
2314 return getDefaultLevelFilename(-1);
2317 char *getGlobalLevelTemplateFilename(void)
2319 // global variable "leveldir_current" must be modified in the loop below
2320 LevelDirTree *leveldir_current_last = leveldir_current;
2321 char *filename = NULL;
2323 // check for template level in path from current to topmost tree node
2325 while (leveldir_current != NULL)
2327 filename = getDefaultLevelFilename(-1);
2329 if (fileExists(filename))
2332 leveldir_current = leveldir_current->node_parent;
2335 // restore global variable "leveldir_current" modified in above loop
2336 leveldir_current = leveldir_current_last;
2341 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2345 // special case: level number is negative => check for level template file
2348 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2349 getSingleLevelBasename(-1));
2351 // replace local level template filename with global template filename
2352 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2354 // no fallback if template file not existing
2358 // special case: check for file name/pattern specified in "levelinfo.conf"
2359 if (leveldir_current->level_filename != NULL)
2361 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2363 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2364 leveldir_current->level_filename, nr);
2366 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2368 if (fileExists(lfi->filename))
2371 else if (leveldir_current->level_filetype != NULL)
2373 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2375 // check for specified native level file with standard file name
2376 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2377 "%03d.%s", nr, LEVELFILE_EXTENSION);
2378 if (fileExists(lfi->filename))
2382 // check for native Rocks'n'Diamonds level file
2383 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2384 "%03d.%s", nr, LEVELFILE_EXTENSION);
2385 if (fileExists(lfi->filename))
2388 // check for native Boulder Dash level file
2389 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2390 if (fileExists(lfi->filename))
2393 // check for Emerald Mine level file (V1)
2394 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2395 'a' + (nr / 10) % 26, '0' + nr % 10);
2396 if (fileExists(lfi->filename))
2398 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2399 'A' + (nr / 10) % 26, '0' + nr % 10);
2400 if (fileExists(lfi->filename))
2403 // check for Emerald Mine level file (V2 to V5)
2404 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2405 if (fileExists(lfi->filename))
2408 // check for Emerald Mine level file (V6 / single mode)
2409 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2410 if (fileExists(lfi->filename))
2412 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2413 if (fileExists(lfi->filename))
2416 // check for Emerald Mine level file (V6 / teamwork mode)
2417 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2418 if (fileExists(lfi->filename))
2420 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2421 if (fileExists(lfi->filename))
2424 // check for various packed level file formats
2425 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2426 if (fileExists(lfi->filename))
2429 // no known level file found -- use default values (and fail later)
2430 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2431 "%03d.%s", nr, LEVELFILE_EXTENSION);
2434 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2436 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2437 lfi->type = getFileTypeFromBasename(lfi->basename);
2439 if (lfi->type == LEVEL_FILE_TYPE_RND)
2440 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2443 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2445 // always start with reliable default values
2446 setFileInfoToDefaults(level_file_info);
2448 level_file_info->nr = nr; // set requested level number
2450 determineLevelFileInfo_Filename(level_file_info);
2451 determineLevelFileInfo_Filetype(level_file_info);
2454 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2455 struct LevelFileInfo *lfi_to)
2457 lfi_to->nr = lfi_from->nr;
2458 lfi_to->type = lfi_from->type;
2459 lfi_to->packed = lfi_from->packed;
2461 setString(&lfi_to->basename, lfi_from->basename);
2462 setString(&lfi_to->filename, lfi_from->filename);
2465 // ----------------------------------------------------------------------------
2466 // functions for loading R'n'D level
2467 // ----------------------------------------------------------------------------
2469 int getMappedElement(int element)
2471 // remap some (historic, now obsolete) elements
2475 case EL_PLAYER_OBSOLETE:
2476 element = EL_PLAYER_1;
2479 case EL_KEY_OBSOLETE:
2483 case EL_EM_KEY_1_FILE_OBSOLETE:
2484 element = EL_EM_KEY_1;
2487 case EL_EM_KEY_2_FILE_OBSOLETE:
2488 element = EL_EM_KEY_2;
2491 case EL_EM_KEY_3_FILE_OBSOLETE:
2492 element = EL_EM_KEY_3;
2495 case EL_EM_KEY_4_FILE_OBSOLETE:
2496 element = EL_EM_KEY_4;
2499 case EL_ENVELOPE_OBSOLETE:
2500 element = EL_ENVELOPE_1;
2508 if (element >= NUM_FILE_ELEMENTS)
2510 Warn("invalid level element %d", element);
2512 element = EL_UNKNOWN;
2520 static int getMappedElementByVersion(int element, int game_version)
2522 // remap some elements due to certain game version
2524 if (game_version <= VERSION_IDENT(2,2,0,0))
2526 // map game font elements
2527 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2528 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2529 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2530 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2533 if (game_version < VERSION_IDENT(3,0,0,0))
2535 // map Supaplex gravity tube elements
2536 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2537 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2538 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2539 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2546 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2548 level->file_version = getFileVersion(file);
2549 level->game_version = getFileVersion(file);
2554 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2556 level->creation_date.year = getFile16BitBE(file);
2557 level->creation_date.month = getFile8Bit(file);
2558 level->creation_date.day = getFile8Bit(file);
2560 level->creation_date.src = DATE_SRC_LEVELFILE;
2565 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2567 int initial_player_stepsize;
2568 int initial_player_gravity;
2571 level->fieldx = getFile8Bit(file);
2572 level->fieldy = getFile8Bit(file);
2574 level->time = getFile16BitBE(file);
2575 level->gems_needed = getFile16BitBE(file);
2577 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2578 level->name[i] = getFile8Bit(file);
2579 level->name[MAX_LEVEL_NAME_LEN] = 0;
2581 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2582 level->score[i] = getFile8Bit(file);
2584 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2585 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2586 for (y = 0; y < 3; y++)
2587 for (x = 0; x < 3; x++)
2588 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2590 level->amoeba_speed = getFile8Bit(file);
2591 level->time_magic_wall = getFile8Bit(file);
2592 level->time_wheel = getFile8Bit(file);
2593 level->amoeba_content = getMappedElement(getFile8Bit(file));
2595 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2598 for (i = 0; i < MAX_PLAYERS; i++)
2599 level->initial_player_stepsize[i] = initial_player_stepsize;
2601 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2603 for (i = 0; i < MAX_PLAYERS; i++)
2604 level->initial_player_gravity[i] = initial_player_gravity;
2606 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2607 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2609 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2611 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2612 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2613 level->can_move_into_acid_bits = getFile32BitBE(file);
2614 level->dont_collide_with_bits = getFile8Bit(file);
2616 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2617 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2619 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2620 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2621 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2623 level->game_engine_type = getFile8Bit(file);
2625 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2630 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2634 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2635 level->name[i] = getFile8Bit(file);
2636 level->name[MAX_LEVEL_NAME_LEN] = 0;
2641 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2645 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2646 level->author[i] = getFile8Bit(file);
2647 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2652 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2655 int chunk_size_expected = level->fieldx * level->fieldy;
2657 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2658 stored with 16-bit encoding (and should be twice as big then).
2659 Even worse, playfield data was stored 16-bit when only yamyam content
2660 contained 16-bit elements and vice versa. */
2662 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2663 chunk_size_expected *= 2;
2665 if (chunk_size_expected != chunk_size)
2667 ReadUnusedBytesFromFile(file, chunk_size);
2668 return chunk_size_expected;
2671 for (y = 0; y < level->fieldy; y++)
2672 for (x = 0; x < level->fieldx; x++)
2673 level->field[x][y] =
2674 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2679 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2682 int header_size = 4;
2683 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2684 int chunk_size_expected = header_size + content_size;
2686 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2687 stored with 16-bit encoding (and should be twice as big then).
2688 Even worse, playfield data was stored 16-bit when only yamyam content
2689 contained 16-bit elements and vice versa. */
2691 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2692 chunk_size_expected += content_size;
2694 if (chunk_size_expected != chunk_size)
2696 ReadUnusedBytesFromFile(file, chunk_size);
2697 return chunk_size_expected;
2701 level->num_yamyam_contents = getFile8Bit(file);
2705 // correct invalid number of content fields -- should never happen
2706 if (level->num_yamyam_contents < 1 ||
2707 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2708 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2710 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2711 for (y = 0; y < 3; y++)
2712 for (x = 0; x < 3; x++)
2713 level->yamyam_content[i].e[x][y] =
2714 getMappedElement(level->encoding_16bit_field ?
2715 getFile16BitBE(file) : getFile8Bit(file));
2719 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2724 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2726 element = getMappedElement(getFile16BitBE(file));
2727 num_contents = getFile8Bit(file);
2729 getFile8Bit(file); // content x size (unused)
2730 getFile8Bit(file); // content y size (unused)
2732 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2734 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2735 for (y = 0; y < 3; y++)
2736 for (x = 0; x < 3; x++)
2737 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2739 // correct invalid number of content fields -- should never happen
2740 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2741 num_contents = STD_ELEMENT_CONTENTS;
2743 if (element == EL_YAMYAM)
2745 level->num_yamyam_contents = num_contents;
2747 for (i = 0; i < num_contents; i++)
2748 for (y = 0; y < 3; y++)
2749 for (x = 0; x < 3; x++)
2750 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2752 else if (element == EL_BD_AMOEBA)
2754 level->amoeba_content = content_array[0][0][0];
2758 Warn("cannot load content for element '%d'", element);
2764 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2770 int chunk_size_expected;
2772 element = getMappedElement(getFile16BitBE(file));
2773 if (!IS_ENVELOPE(element))
2774 element = EL_ENVELOPE_1;
2776 envelope_nr = element - EL_ENVELOPE_1;
2778 envelope_len = getFile16BitBE(file);
2780 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2781 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2783 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2785 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2786 if (chunk_size_expected != chunk_size)
2788 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2789 return chunk_size_expected;
2792 for (i = 0; i < envelope_len; i++)
2793 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2798 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2800 int num_changed_custom_elements = getFile16BitBE(file);
2801 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2804 if (chunk_size_expected != chunk_size)
2806 ReadUnusedBytesFromFile(file, chunk_size - 2);
2807 return chunk_size_expected;
2810 for (i = 0; i < num_changed_custom_elements; i++)
2812 int element = getMappedElement(getFile16BitBE(file));
2813 int properties = getFile32BitBE(file);
2815 if (IS_CUSTOM_ELEMENT(element))
2816 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2818 Warn("invalid custom element number %d", element);
2820 // older game versions that wrote level files with CUS1 chunks used
2821 // different default push delay values (not yet stored in level file)
2822 element_info[element].push_delay_fixed = 2;
2823 element_info[element].push_delay_random = 8;
2826 level->file_has_custom_elements = TRUE;
2831 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2833 int num_changed_custom_elements = getFile16BitBE(file);
2834 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2837 if (chunk_size_expected != chunk_size)
2839 ReadUnusedBytesFromFile(file, chunk_size - 2);
2840 return chunk_size_expected;
2843 for (i = 0; i < num_changed_custom_elements; i++)
2845 int element = getMappedElement(getFile16BitBE(file));
2846 int custom_target_element = getMappedElement(getFile16BitBE(file));
2848 if (IS_CUSTOM_ELEMENT(element))
2849 element_info[element].change->target_element = custom_target_element;
2851 Warn("invalid custom element number %d", element);
2854 level->file_has_custom_elements = TRUE;
2859 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2861 int num_changed_custom_elements = getFile16BitBE(file);
2862 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2865 if (chunk_size_expected != chunk_size)
2867 ReadUnusedBytesFromFile(file, chunk_size - 2);
2868 return chunk_size_expected;
2871 for (i = 0; i < num_changed_custom_elements; i++)
2873 int element = getMappedElement(getFile16BitBE(file));
2874 struct ElementInfo *ei = &element_info[element];
2875 unsigned int event_bits;
2877 if (!IS_CUSTOM_ELEMENT(element))
2879 Warn("invalid custom element number %d", element);
2881 element = EL_INTERNAL_DUMMY;
2884 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2885 ei->description[j] = getFile8Bit(file);
2886 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2888 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2890 // some free bytes for future properties and padding
2891 ReadUnusedBytesFromFile(file, 7);
2893 ei->use_gfx_element = getFile8Bit(file);
2894 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2896 ei->collect_score_initial = getFile8Bit(file);
2897 ei->collect_count_initial = getFile8Bit(file);
2899 ei->push_delay_fixed = getFile16BitBE(file);
2900 ei->push_delay_random = getFile16BitBE(file);
2901 ei->move_delay_fixed = getFile16BitBE(file);
2902 ei->move_delay_random = getFile16BitBE(file);
2904 ei->move_pattern = getFile16BitBE(file);
2905 ei->move_direction_initial = getFile8Bit(file);
2906 ei->move_stepsize = getFile8Bit(file);
2908 for (y = 0; y < 3; y++)
2909 for (x = 0; x < 3; x++)
2910 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2912 // bits 0 - 31 of "has_event[]"
2913 event_bits = getFile32BitBE(file);
2914 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2915 if (event_bits & (1u << j))
2916 ei->change->has_event[j] = TRUE;
2918 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2920 ei->change->delay_fixed = getFile16BitBE(file);
2921 ei->change->delay_random = getFile16BitBE(file);
2922 ei->change->delay_frames = getFile16BitBE(file);
2924 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2926 ei->change->explode = getFile8Bit(file);
2927 ei->change->use_target_content = getFile8Bit(file);
2928 ei->change->only_if_complete = getFile8Bit(file);
2929 ei->change->use_random_replace = getFile8Bit(file);
2931 ei->change->random_percentage = getFile8Bit(file);
2932 ei->change->replace_when = getFile8Bit(file);
2934 for (y = 0; y < 3; y++)
2935 for (x = 0; x < 3; x++)
2936 ei->change->target_content.e[x][y] =
2937 getMappedElement(getFile16BitBE(file));
2939 ei->slippery_type = getFile8Bit(file);
2941 // some free bytes for future properties and padding
2942 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2944 // mark that this custom element has been modified
2945 ei->modified_settings = TRUE;
2948 level->file_has_custom_elements = TRUE;
2953 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2955 struct ElementInfo *ei;
2956 int chunk_size_expected;
2960 // ---------- custom element base property values (96 bytes) ----------------
2962 element = getMappedElement(getFile16BitBE(file));
2964 if (!IS_CUSTOM_ELEMENT(element))
2966 Warn("invalid custom element number %d", element);
2968 ReadUnusedBytesFromFile(file, chunk_size - 2);
2973 ei = &element_info[element];
2975 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2976 ei->description[i] = getFile8Bit(file);
2977 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2979 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2981 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2983 ei->num_change_pages = getFile8Bit(file);
2985 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2986 if (chunk_size_expected != chunk_size)
2988 ReadUnusedBytesFromFile(file, chunk_size - 43);
2989 return chunk_size_expected;
2992 ei->ce_value_fixed_initial = getFile16BitBE(file);
2993 ei->ce_value_random_initial = getFile16BitBE(file);
2994 ei->use_last_ce_value = getFile8Bit(file);
2996 ei->use_gfx_element = getFile8Bit(file);
2997 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2999 ei->collect_score_initial = getFile8Bit(file);
3000 ei->collect_count_initial = getFile8Bit(file);
3002 ei->drop_delay_fixed = getFile8Bit(file);
3003 ei->push_delay_fixed = getFile8Bit(file);
3004 ei->drop_delay_random = getFile8Bit(file);
3005 ei->push_delay_random = getFile8Bit(file);
3006 ei->move_delay_fixed = getFile16BitBE(file);
3007 ei->move_delay_random = getFile16BitBE(file);
3009 // bits 0 - 15 of "move_pattern" ...
3010 ei->move_pattern = getFile16BitBE(file);
3011 ei->move_direction_initial = getFile8Bit(file);
3012 ei->move_stepsize = getFile8Bit(file);
3014 ei->slippery_type = getFile8Bit(file);
3016 for (y = 0; y < 3; y++)
3017 for (x = 0; x < 3; x++)
3018 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3020 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3021 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3022 ei->move_leave_type = getFile8Bit(file);
3024 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3025 ei->move_pattern |= (getFile16BitBE(file) << 16);
3027 ei->access_direction = getFile8Bit(file);
3029 ei->explosion_delay = getFile8Bit(file);
3030 ei->ignition_delay = getFile8Bit(file);
3031 ei->explosion_type = getFile8Bit(file);
3033 // some free bytes for future custom property values and padding
3034 ReadUnusedBytesFromFile(file, 1);
3036 // ---------- change page property values (48 bytes) ------------------------
3038 setElementChangePages(ei, ei->num_change_pages);
3040 for (i = 0; i < ei->num_change_pages; i++)
3042 struct ElementChangeInfo *change = &ei->change_page[i];
3043 unsigned int event_bits;
3045 // always start with reliable default values
3046 setElementChangeInfoToDefaults(change);
3048 // bits 0 - 31 of "has_event[]" ...
3049 event_bits = getFile32BitBE(file);
3050 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3051 if (event_bits & (1u << j))
3052 change->has_event[j] = TRUE;
3054 change->target_element = getMappedElement(getFile16BitBE(file));
3056 change->delay_fixed = getFile16BitBE(file);
3057 change->delay_random = getFile16BitBE(file);
3058 change->delay_frames = getFile16BitBE(file);
3060 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3062 change->explode = getFile8Bit(file);
3063 change->use_target_content = getFile8Bit(file);
3064 change->only_if_complete = getFile8Bit(file);
3065 change->use_random_replace = getFile8Bit(file);
3067 change->random_percentage = getFile8Bit(file);
3068 change->replace_when = getFile8Bit(file);
3070 for (y = 0; y < 3; y++)
3071 for (x = 0; x < 3; x++)
3072 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3074 change->can_change = getFile8Bit(file);
3076 change->trigger_side = getFile8Bit(file);
3078 change->trigger_player = getFile8Bit(file);
3079 change->trigger_page = getFile8Bit(file);
3081 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3082 CH_PAGE_ANY : (1 << change->trigger_page));
3084 change->has_action = getFile8Bit(file);
3085 change->action_type = getFile8Bit(file);
3086 change->action_mode = getFile8Bit(file);
3087 change->action_arg = getFile16BitBE(file);
3089 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3090 event_bits = getFile8Bit(file);
3091 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3092 if (event_bits & (1u << (j - 32)))
3093 change->has_event[j] = TRUE;
3096 // mark this custom element as modified
3097 ei->modified_settings = TRUE;
3099 level->file_has_custom_elements = TRUE;
3104 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3106 struct ElementInfo *ei;
3107 struct ElementGroupInfo *group;
3111 element = getMappedElement(getFile16BitBE(file));
3113 if (!IS_GROUP_ELEMENT(element))
3115 Warn("invalid group element number %d", element);
3117 ReadUnusedBytesFromFile(file, chunk_size - 2);
3122 ei = &element_info[element];
3124 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3125 ei->description[i] = getFile8Bit(file);
3126 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3128 group = element_info[element].group;
3130 group->num_elements = getFile8Bit(file);
3132 ei->use_gfx_element = getFile8Bit(file);
3133 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3135 group->choice_mode = getFile8Bit(file);
3137 // some free bytes for future values and padding
3138 ReadUnusedBytesFromFile(file, 3);
3140 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3141 group->element[i] = getMappedElement(getFile16BitBE(file));
3143 // mark this group element as modified
3144 element_info[element].modified_settings = TRUE;
3146 level->file_has_custom_elements = TRUE;
3151 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3152 int element, int real_element)
3154 int micro_chunk_size = 0;
3155 int conf_type = getFile8Bit(file);
3156 int byte_mask = conf_type & CONF_MASK_BYTES;
3157 boolean element_found = FALSE;
3160 micro_chunk_size += 1;
3162 if (byte_mask == CONF_MASK_MULTI_BYTES)
3164 int num_bytes = getFile16BitBE(file);
3165 byte *buffer = checked_malloc(num_bytes);
3167 ReadBytesFromFile(file, buffer, num_bytes);
3169 for (i = 0; conf[i].data_type != -1; i++)
3171 if (conf[i].element == element &&
3172 conf[i].conf_type == conf_type)
3174 int data_type = conf[i].data_type;
3175 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3176 int max_num_entities = conf[i].max_num_entities;
3178 if (num_entities > max_num_entities)
3180 Warn("truncating number of entities for element %d from %d to %d",
3181 element, num_entities, max_num_entities);
3183 num_entities = max_num_entities;
3186 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3187 data_type == TYPE_CONTENT_LIST))
3189 // for element and content lists, zero entities are not allowed
3190 Warn("found empty list of entities for element %d", element);
3192 // do not set "num_entities" here to prevent reading behind buffer
3194 *(int *)(conf[i].num_entities) = 1; // at least one is required
3198 *(int *)(conf[i].num_entities) = num_entities;
3201 element_found = TRUE;
3203 if (data_type == TYPE_STRING)
3205 char *string = (char *)(conf[i].value);
3208 for (j = 0; j < max_num_entities; j++)
3209 string[j] = (j < num_entities ? buffer[j] : '\0');
3211 else if (data_type == TYPE_ELEMENT_LIST)
3213 int *element_array = (int *)(conf[i].value);
3216 for (j = 0; j < num_entities; j++)
3218 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3220 else if (data_type == TYPE_CONTENT_LIST)
3222 struct Content *content= (struct Content *)(conf[i].value);
3225 for (c = 0; c < num_entities; c++)
3226 for (y = 0; y < 3; y++)
3227 for (x = 0; x < 3; x++)
3228 content[c].e[x][y] =
3229 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3232 element_found = FALSE;
3238 checked_free(buffer);
3240 micro_chunk_size += 2 + num_bytes;
3242 else // constant size configuration data (1, 2 or 4 bytes)
3244 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3245 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3246 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3248 for (i = 0; conf[i].data_type != -1; i++)
3250 if (conf[i].element == element &&
3251 conf[i].conf_type == conf_type)
3253 int data_type = conf[i].data_type;
3255 if (data_type == TYPE_ELEMENT)
3256 value = getMappedElement(value);
3258 if (data_type == TYPE_BOOLEAN)
3259 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3261 *(int *) (conf[i].value) = value;
3263 element_found = TRUE;
3269 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3274 char *error_conf_chunk_bytes =
3275 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3276 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3277 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3278 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3279 int error_element = real_element;
3281 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3282 error_conf_chunk_bytes, error_conf_chunk_token,
3283 error_element, EL_NAME(error_element));
3286 return micro_chunk_size;
3289 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3291 int real_chunk_size = 0;
3293 li = *level; // copy level data into temporary buffer
3295 while (!checkEndOfFile(file))
3297 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3299 if (real_chunk_size >= chunk_size)
3303 *level = li; // copy temporary buffer back to level data
3305 return real_chunk_size;
3308 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3310 int real_chunk_size = 0;
3312 li = *level; // copy level data into temporary buffer
3314 while (!checkEndOfFile(file))
3316 int element = getMappedElement(getFile16BitBE(file));
3318 real_chunk_size += 2;
3319 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3321 if (real_chunk_size >= chunk_size)
3325 *level = li; // copy temporary buffer back to level data
3327 return real_chunk_size;
3330 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3332 int real_chunk_size = 0;
3334 li = *level; // copy level data into temporary buffer
3336 while (!checkEndOfFile(file))
3338 int element = getMappedElement(getFile16BitBE(file));
3340 real_chunk_size += 2;
3341 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3343 if (real_chunk_size >= chunk_size)
3347 *level = li; // copy temporary buffer back to level data
3349 return real_chunk_size;
3352 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3354 int element = getMappedElement(getFile16BitBE(file));
3355 int envelope_nr = element - EL_ENVELOPE_1;
3356 int real_chunk_size = 2;
3358 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3360 while (!checkEndOfFile(file))
3362 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3365 if (real_chunk_size >= chunk_size)
3369 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3371 return real_chunk_size;
3374 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3376 int element = getMappedElement(getFile16BitBE(file));
3377 int real_chunk_size = 2;
3378 struct ElementInfo *ei = &element_info[element];
3381 xx_ei = *ei; // copy element data into temporary buffer
3383 xx_ei.num_change_pages = -1;
3385 while (!checkEndOfFile(file))
3387 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3389 if (xx_ei.num_change_pages != -1)
3392 if (real_chunk_size >= chunk_size)
3398 if (ei->num_change_pages == -1)
3400 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3403 ei->num_change_pages = 1;
3405 setElementChangePages(ei, 1);
3406 setElementChangeInfoToDefaults(ei->change);
3408 return real_chunk_size;
3411 // initialize number of change pages stored for this custom element
3412 setElementChangePages(ei, ei->num_change_pages);
3413 for (i = 0; i < ei->num_change_pages; i++)
3414 setElementChangeInfoToDefaults(&ei->change_page[i]);
3416 // start with reading properties for the first change page
3417 xx_current_change_page = 0;
3419 while (!checkEndOfFile(file))
3421 // level file might contain invalid change page number
3422 if (xx_current_change_page >= ei->num_change_pages)
3425 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3427 xx_change = *change; // copy change data into temporary buffer
3429 resetEventBits(); // reset bits; change page might have changed
3431 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3434 *change = xx_change;
3436 setEventFlagsFromEventBits(change);
3438 if (real_chunk_size >= chunk_size)
3442 level->file_has_custom_elements = TRUE;
3444 return real_chunk_size;
3447 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int real_chunk_size = 2;
3451 struct ElementInfo *ei = &element_info[element];
3452 struct ElementGroupInfo *group = ei->group;
3457 xx_ei = *ei; // copy element data into temporary buffer
3458 xx_group = *group; // copy group data into temporary buffer
3460 while (!checkEndOfFile(file))
3462 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3465 if (real_chunk_size >= chunk_size)
3472 level->file_has_custom_elements = TRUE;
3474 return real_chunk_size;
3477 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3479 int element = getMappedElement(getFile16BitBE(file));
3480 int real_chunk_size = 2;
3481 struct ElementInfo *ei = &element_info[element];
3483 xx_ei = *ei; // copy element data into temporary buffer
3485 while (!checkEndOfFile(file))
3487 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3490 if (real_chunk_size >= chunk_size)
3496 level->file_has_custom_elements = TRUE;
3498 return real_chunk_size;
3501 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3502 struct LevelFileInfo *level_file_info,
3503 boolean level_info_only)
3505 char *filename = level_file_info->filename;
3506 char cookie[MAX_LINE_LEN];
3507 char chunk_name[CHUNK_ID_LEN + 1];
3511 if (!(file = openFile(filename, MODE_READ)))
3513 level->no_valid_file = TRUE;
3514 level->no_level_file = TRUE;
3516 if (level_info_only)
3519 Warn("cannot read level '%s' -- using empty level", filename);
3521 if (!setup.editor.use_template_for_new_levels)
3524 // if level file not found, try to initialize level data from template
3525 filename = getGlobalLevelTemplateFilename();
3527 if (!(file = openFile(filename, MODE_READ)))
3530 // default: for empty levels, use level template for custom elements
3531 level->use_custom_template = TRUE;
3533 level->no_valid_file = FALSE;
3536 getFileChunkBE(file, chunk_name, NULL);
3537 if (strEqual(chunk_name, "RND1"))
3539 getFile32BitBE(file); // not used
3541 getFileChunkBE(file, chunk_name, NULL);
3542 if (!strEqual(chunk_name, "CAVE"))
3544 level->no_valid_file = TRUE;
3546 Warn("unknown format of level file '%s'", filename);
3553 else // check for pre-2.0 file format with cookie string
3555 strcpy(cookie, chunk_name);
3556 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3558 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3559 cookie[strlen(cookie) - 1] = '\0';
3561 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3563 level->no_valid_file = TRUE;
3565 Warn("unknown format of level file '%s'", filename);
3572 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3574 level->no_valid_file = TRUE;
3576 Warn("unsupported version of level file '%s'", filename);
3583 // pre-2.0 level files have no game version, so use file version here
3584 level->game_version = level->file_version;
3587 if (level->file_version < FILE_VERSION_1_2)
3589 // level files from versions before 1.2.0 without chunk structure
3590 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3591 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3599 int (*loader)(File *, int, struct LevelInfo *);
3603 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3604 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3605 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3606 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3607 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3608 { "INFO", -1, LoadLevel_INFO },
3609 { "BODY", -1, LoadLevel_BODY },
3610 { "CONT", -1, LoadLevel_CONT },
3611 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3612 { "CNT3", -1, LoadLevel_CNT3 },
3613 { "CUS1", -1, LoadLevel_CUS1 },
3614 { "CUS2", -1, LoadLevel_CUS2 },
3615 { "CUS3", -1, LoadLevel_CUS3 },
3616 { "CUS4", -1, LoadLevel_CUS4 },
3617 { "GRP1", -1, LoadLevel_GRP1 },
3618 { "CONF", -1, LoadLevel_CONF },
3619 { "ELEM", -1, LoadLevel_ELEM },
3620 { "NOTE", -1, LoadLevel_NOTE },
3621 { "CUSX", -1, LoadLevel_CUSX },
3622 { "GRPX", -1, LoadLevel_GRPX },
3623 { "EMPX", -1, LoadLevel_EMPX },
3628 while (getFileChunkBE(file, chunk_name, &chunk_size))
3632 while (chunk_info[i].name != NULL &&
3633 !strEqual(chunk_name, chunk_info[i].name))
3636 if (chunk_info[i].name == NULL)
3638 Warn("unknown chunk '%s' in level file '%s'",
3639 chunk_name, filename);
3641 ReadUnusedBytesFromFile(file, chunk_size);
3643 else if (chunk_info[i].size != -1 &&
3644 chunk_info[i].size != chunk_size)
3646 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3647 chunk_size, chunk_name, filename);
3649 ReadUnusedBytesFromFile(file, chunk_size);
3653 // call function to load this level chunk
3654 int chunk_size_expected =
3655 (chunk_info[i].loader)(file, chunk_size, level);
3657 if (chunk_size_expected < 0)
3659 Warn("error reading chunk '%s' in level file '%s'",
3660 chunk_name, filename);
3665 // the size of some chunks cannot be checked before reading other
3666 // chunks first (like "HEAD" and "BODY") that contain some header
3667 // information, so check them here
3668 if (chunk_size_expected != chunk_size)
3670 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3671 chunk_size, chunk_name, filename);
3683 // ----------------------------------------------------------------------------
3684 // functions for loading BD level
3685 // ----------------------------------------------------------------------------
3687 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3689 struct LevelInfo_BD *level_bd = level->native_bd_level;
3690 GdCave *cave = NULL; // will be changed below
3691 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3692 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3695 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3697 // cave and map newly allocated when set to defaults above
3698 cave = level_bd->cave;
3700 for (i = 0; i < 5; i++)
3702 cave->level_time[i] = level->time;
3703 cave->level_diamonds[i] = level->gems_needed;
3704 cave->level_magic_wall_time[i] = level->time_magic_wall;
3705 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3708 cave->diamond_value = level->score[SC_DIAMOND];
3709 cave->extra_diamond_value = level->score[SC_DIAMOND];
3711 cave->level_speed[0] = 160; // set cave speed
3713 strncpy(cave->name, level->name, sizeof(GdString));
3714 cave->name[sizeof(GdString) - 1] = '\0';
3716 for (x = 0; x < cave->w; x++)
3717 for (y = 0; y < cave->h; y++)
3718 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3721 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3723 struct LevelInfo_BD *level_bd = level->native_bd_level;
3724 GdCave *cave = level_bd->cave;
3725 int bd_level_nr = level_bd->level_nr;
3728 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3729 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3731 level->time = cave->level_time[bd_level_nr];
3732 level->gems_needed = cave->level_diamonds[bd_level_nr];
3733 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3735 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3736 level->score[SC_DIAMOND] = cave->diamond_value;
3738 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3739 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3741 for (x = 0; x < level->fieldx; x++)
3742 for (y = 0; y < level->fieldy; y++)
3743 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3746 static void setTapeInfoToDefaults(void);
3748 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3750 struct LevelInfo_BD *level_bd = level->native_bd_level;
3751 GdCave *cave = level_bd->cave;
3752 GdReplay *replay = level_bd->replay;
3758 // always start with reliable default values
3759 setTapeInfoToDefaults();
3761 tape.level_nr = level_nr; // (currently not used)
3762 tape.random_seed = replay->seed;
3764 TapeSetDateFromIsoDateString(replay->date);
3767 tape.pos[tape.counter].delay = 0;
3769 tape.bd_replay = TRUE;
3771 // all time calculations only used to display approximate tape time
3772 int cave_speed = cave->speed;
3773 int milliseconds_game = 0;
3774 int milliseconds_elapsed = 20;
3776 for (i = 0; i < replay->movements->len; i++)
3778 int replay_action = replay->movements->data[i];
3779 int tape_action = map_action_BD_to_RND(replay_action);
3780 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3781 boolean success = 0;
3785 success = TapeAddAction(action);
3787 milliseconds_game += milliseconds_elapsed;
3789 if (milliseconds_game >= cave_speed)
3791 milliseconds_game -= cave_speed;
3798 tape.pos[tape.counter].delay = 0;
3799 tape.pos[tape.counter].action[0] = 0;
3803 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3809 TapeHaltRecording();
3813 // ----------------------------------------------------------------------------
3814 // functions for loading EM level
3815 // ----------------------------------------------------------------------------
3817 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3819 static int ball_xy[8][2] =
3830 struct LevelInfo_EM *level_em = level->native_em_level;
3831 struct CAVE *cav = level_em->cav;
3834 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3835 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3837 cav->time_seconds = level->time;
3838 cav->gems_needed = level->gems_needed;
3840 cav->emerald_score = level->score[SC_EMERALD];
3841 cav->diamond_score = level->score[SC_DIAMOND];
3842 cav->alien_score = level->score[SC_ROBOT];
3843 cav->tank_score = level->score[SC_SPACESHIP];
3844 cav->bug_score = level->score[SC_BUG];
3845 cav->eater_score = level->score[SC_YAMYAM];
3846 cav->nut_score = level->score[SC_NUT];
3847 cav->dynamite_score = level->score[SC_DYNAMITE];
3848 cav->key_score = level->score[SC_KEY];
3849 cav->exit_score = level->score[SC_TIME_BONUS];
3851 cav->num_eater_arrays = level->num_yamyam_contents;
3853 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3854 for (y = 0; y < 3; y++)
3855 for (x = 0; x < 3; x++)
3856 cav->eater_array[i][y * 3 + x] =
3857 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3859 cav->amoeba_time = level->amoeba_speed;
3860 cav->wonderwall_time = level->time_magic_wall;
3861 cav->wheel_time = level->time_wheel;
3863 cav->android_move_time = level->android_move_time;
3864 cav->android_clone_time = level->android_clone_time;
3865 cav->ball_random = level->ball_random;
3866 cav->ball_active = level->ball_active_initial;
3867 cav->ball_time = level->ball_time;
3868 cav->num_ball_arrays = level->num_ball_contents;
3870 cav->lenses_score = level->lenses_score;
3871 cav->magnify_score = level->magnify_score;
3872 cav->slurp_score = level->slurp_score;
3874 cav->lenses_time = level->lenses_time;
3875 cav->magnify_time = level->magnify_time;
3877 cav->wind_time = 9999;
3878 cav->wind_direction =
3879 map_direction_RND_to_EM(level->wind_direction_initial);
3881 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3882 for (j = 0; j < 8; j++)
3883 cav->ball_array[i][j] =
3884 map_element_RND_to_EM_cave(level->ball_content[i].
3885 e[ball_xy[j][0]][ball_xy[j][1]]);
3887 map_android_clone_elements_RND_to_EM(level);
3889 // first fill the complete playfield with the empty space element
3890 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3891 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3892 cav->cave[x][y] = Cblank;
3894 // then copy the real level contents from level file into the playfield
3895 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3897 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3899 if (level->field[x][y] == EL_AMOEBA_DEAD)
3900 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3902 cav->cave[x][y] = new_element;
3905 for (i = 0; i < MAX_PLAYERS; i++)
3907 cav->player_x[i] = -1;
3908 cav->player_y[i] = -1;
3911 // initialize player positions and delete players from the playfield
3912 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3914 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3916 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3918 cav->player_x[player_nr] = x;
3919 cav->player_y[player_nr] = y;
3921 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3926 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3928 static int ball_xy[8][2] =
3939 struct LevelInfo_EM *level_em = level->native_em_level;
3940 struct CAVE *cav = level_em->cav;
3943 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3944 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3946 level->time = cav->time_seconds;
3947 level->gems_needed = cav->gems_needed;
3949 sprintf(level->name, "Level %d", level->file_info.nr);
3951 level->score[SC_EMERALD] = cav->emerald_score;
3952 level->score[SC_DIAMOND] = cav->diamond_score;
3953 level->score[SC_ROBOT] = cav->alien_score;
3954 level->score[SC_SPACESHIP] = cav->tank_score;
3955 level->score[SC_BUG] = cav->bug_score;
3956 level->score[SC_YAMYAM] = cav->eater_score;
3957 level->score[SC_NUT] = cav->nut_score;
3958 level->score[SC_DYNAMITE] = cav->dynamite_score;
3959 level->score[SC_KEY] = cav->key_score;
3960 level->score[SC_TIME_BONUS] = cav->exit_score;
3962 level->num_yamyam_contents = cav->num_eater_arrays;
3964 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3965 for (y = 0; y < 3; y++)
3966 for (x = 0; x < 3; x++)
3967 level->yamyam_content[i].e[x][y] =
3968 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3970 level->amoeba_speed = cav->amoeba_time;
3971 level->time_magic_wall = cav->wonderwall_time;
3972 level->time_wheel = cav->wheel_time;
3974 level->android_move_time = cav->android_move_time;
3975 level->android_clone_time = cav->android_clone_time;
3976 level->ball_random = cav->ball_random;
3977 level->ball_active_initial = cav->ball_active;
3978 level->ball_time = cav->ball_time;
3979 level->num_ball_contents = cav->num_ball_arrays;
3981 level->lenses_score = cav->lenses_score;
3982 level->magnify_score = cav->magnify_score;
3983 level->slurp_score = cav->slurp_score;
3985 level->lenses_time = cav->lenses_time;
3986 level->magnify_time = cav->magnify_time;
3988 level->wind_direction_initial =
3989 map_direction_EM_to_RND(cav->wind_direction);
3991 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3992 for (j = 0; j < 8; j++)
3993 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3994 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3996 map_android_clone_elements_EM_to_RND(level);
3998 // convert the playfield (some elements need special treatment)
3999 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4001 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4003 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4004 new_element = EL_AMOEBA_DEAD;
4006 level->field[x][y] = new_element;
4009 for (i = 0; i < MAX_PLAYERS; i++)
4011 // in case of all players set to the same field, use the first player
4012 int nr = MAX_PLAYERS - i - 1;
4013 int jx = cav->player_x[nr];
4014 int jy = cav->player_y[nr];
4016 if (jx != -1 && jy != -1)
4017 level->field[jx][jy] = EL_PLAYER_1 + nr;
4020 // time score is counted for each 10 seconds left in Emerald Mine levels
4021 level->time_score_base = 10;
4025 // ----------------------------------------------------------------------------
4026 // functions for loading SP level
4027 // ----------------------------------------------------------------------------
4029 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4031 struct LevelInfo_SP *level_sp = level->native_sp_level;
4032 LevelInfoType *header = &level_sp->header;
4035 level_sp->width = level->fieldx;
4036 level_sp->height = level->fieldy;
4038 for (x = 0; x < level->fieldx; x++)
4039 for (y = 0; y < level->fieldy; y++)
4040 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4042 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4044 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4045 header->LevelTitle[i] = level->name[i];
4046 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4048 header->InfotronsNeeded = level->gems_needed;
4050 header->SpecialPortCount = 0;
4052 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4054 boolean gravity_port_found = FALSE;
4055 boolean gravity_port_valid = FALSE;
4056 int gravity_port_flag;
4057 int gravity_port_base_element;
4058 int element = level->field[x][y];
4060 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4061 element <= EL_SP_GRAVITY_ON_PORT_UP)
4063 gravity_port_found = TRUE;
4064 gravity_port_valid = TRUE;
4065 gravity_port_flag = 1;
4066 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4068 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4069 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4071 gravity_port_found = TRUE;
4072 gravity_port_valid = TRUE;
4073 gravity_port_flag = 0;
4074 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4076 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4077 element <= EL_SP_GRAVITY_PORT_UP)
4079 // change R'n'D style gravity inverting special port to normal port
4080 // (there are no gravity inverting ports in native Supaplex engine)
4082 gravity_port_found = TRUE;
4083 gravity_port_valid = FALSE;
4084 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4087 if (gravity_port_found)
4089 if (gravity_port_valid &&
4090 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4092 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4094 port->PortLocation = (y * level->fieldx + x) * 2;
4095 port->Gravity = gravity_port_flag;
4097 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4099 header->SpecialPortCount++;
4103 // change special gravity port to normal port
4105 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4108 level_sp->playfield[x][y] = element - EL_SP_START;
4113 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4115 struct LevelInfo_SP *level_sp = level->native_sp_level;
4116 LevelInfoType *header = &level_sp->header;
4117 boolean num_invalid_elements = 0;
4120 level->fieldx = level_sp->width;
4121 level->fieldy = level_sp->height;
4123 for (x = 0; x < level->fieldx; x++)
4125 for (y = 0; y < level->fieldy; y++)
4127 int element_old = level_sp->playfield[x][y];
4128 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4130 if (element_new == EL_UNKNOWN)
4132 num_invalid_elements++;
4134 Debug("level:native:SP", "invalid element %d at position %d, %d",
4138 level->field[x][y] = element_new;
4142 if (num_invalid_elements > 0)
4143 Warn("found %d invalid elements%s", num_invalid_elements,
4144 (!options.debug ? " (use '--debug' for more details)" : ""));
4146 for (i = 0; i < MAX_PLAYERS; i++)
4147 level->initial_player_gravity[i] =
4148 (header->InitialGravity == 1 ? TRUE : FALSE);
4150 // skip leading spaces
4151 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4152 if (header->LevelTitle[i] != ' ')
4156 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4157 level->name[j] = header->LevelTitle[i];
4158 level->name[j] = '\0';
4160 // cut trailing spaces
4162 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4163 level->name[j - 1] = '\0';
4165 level->gems_needed = header->InfotronsNeeded;
4167 for (i = 0; i < header->SpecialPortCount; i++)
4169 SpecialPortType *port = &header->SpecialPort[i];
4170 int port_location = port->PortLocation;
4171 int gravity = port->Gravity;
4172 int port_x, port_y, port_element;
4174 port_x = (port_location / 2) % level->fieldx;
4175 port_y = (port_location / 2) / level->fieldx;
4177 if (port_x < 0 || port_x >= level->fieldx ||
4178 port_y < 0 || port_y >= level->fieldy)
4180 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4185 port_element = level->field[port_x][port_y];
4187 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4188 port_element > EL_SP_GRAVITY_PORT_UP)
4190 Warn("no special port at position (%d, %d)", port_x, port_y);
4195 // change previous (wrong) gravity inverting special port to either
4196 // gravity enabling special port or gravity disabling special port
4197 level->field[port_x][port_y] +=
4198 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4199 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4202 // change special gravity ports without database entries to normal ports
4203 for (x = 0; x < level->fieldx; x++)
4204 for (y = 0; y < level->fieldy; y++)
4205 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4206 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4207 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4209 level->time = 0; // no time limit
4210 level->amoeba_speed = 0;
4211 level->time_magic_wall = 0;
4212 level->time_wheel = 0;
4213 level->amoeba_content = EL_EMPTY;
4215 // original Supaplex does not use score values -- rate by playing time
4216 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4217 level->score[i] = 0;
4219 level->rate_time_over_score = TRUE;
4221 // there are no yamyams in supaplex levels
4222 for (i = 0; i < level->num_yamyam_contents; i++)
4223 for (x = 0; x < 3; x++)
4224 for (y = 0; y < 3; y++)
4225 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4228 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4230 struct LevelInfo_SP *level_sp = level->native_sp_level;
4231 struct DemoInfo_SP *demo = &level_sp->demo;
4234 // always start with reliable default values
4235 demo->is_available = FALSE;
4238 if (TAPE_IS_EMPTY(tape))
4241 demo->level_nr = tape.level_nr; // (currently not used)
4243 level_sp->header.DemoRandomSeed = tape.random_seed;
4247 for (i = 0; i < tape.length; i++)
4249 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4250 int demo_repeat = tape.pos[i].delay;
4251 int demo_entries = (demo_repeat + 15) / 16;
4253 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4255 Warn("tape truncated: size exceeds maximum SP demo size %d",
4261 for (j = 0; j < demo_repeat / 16; j++)
4262 demo->data[demo->length++] = 0xf0 | demo_action;
4264 if (demo_repeat % 16)
4265 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4268 demo->is_available = TRUE;
4271 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4273 struct LevelInfo_SP *level_sp = level->native_sp_level;
4274 struct DemoInfo_SP *demo = &level_sp->demo;
4275 char *filename = level->file_info.filename;
4278 // always start with reliable default values
4279 setTapeInfoToDefaults();
4281 if (!demo->is_available)
4284 tape.level_nr = demo->level_nr; // (currently not used)
4285 tape.random_seed = level_sp->header.DemoRandomSeed;
4287 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4290 tape.pos[tape.counter].delay = 0;
4292 for (i = 0; i < demo->length; i++)
4294 int demo_action = demo->data[i] & 0x0f;
4295 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4296 int tape_action = map_key_SP_to_RND(demo_action);
4297 int tape_repeat = demo_repeat + 1;
4298 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4299 boolean success = 0;
4302 for (j = 0; j < tape_repeat; j++)
4303 success = TapeAddAction(action);
4307 Warn("SP demo truncated: size exceeds maximum tape size %d",
4314 TapeHaltRecording();
4318 // ----------------------------------------------------------------------------
4319 // functions for loading MM level
4320 // ----------------------------------------------------------------------------
4322 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4324 struct LevelInfo_MM *level_mm = level->native_mm_level;
4327 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4328 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4330 level_mm->time = level->time;
4331 level_mm->kettles_needed = level->gems_needed;
4332 level_mm->auto_count_kettles = level->auto_count_gems;
4334 level_mm->mm_laser_red = level->mm_laser_red;
4335 level_mm->mm_laser_green = level->mm_laser_green;
4336 level_mm->mm_laser_blue = level->mm_laser_blue;
4338 level_mm->df_laser_red = level->df_laser_red;
4339 level_mm->df_laser_green = level->df_laser_green;
4340 level_mm->df_laser_blue = level->df_laser_blue;
4342 strcpy(level_mm->name, level->name);
4343 strcpy(level_mm->author, level->author);
4345 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4346 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4347 level_mm->score[SC_KEY] = level->score[SC_KEY];
4348 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4349 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4351 level_mm->amoeba_speed = level->amoeba_speed;
4352 level_mm->time_fuse = level->mm_time_fuse;
4353 level_mm->time_bomb = level->mm_time_bomb;
4354 level_mm->time_ball = level->mm_time_ball;
4355 level_mm->time_block = level->mm_time_block;
4357 level_mm->num_ball_contents = level->num_mm_ball_contents;
4358 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4359 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4360 level_mm->explode_ball = level->explode_mm_ball;
4362 for (i = 0; i < level->num_mm_ball_contents; i++)
4363 level_mm->ball_content[i] =
4364 map_element_RND_to_MM(level->mm_ball_content[i]);
4366 for (x = 0; x < level->fieldx; x++)
4367 for (y = 0; y < level->fieldy; y++)
4369 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4372 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4374 struct LevelInfo_MM *level_mm = level->native_mm_level;
4377 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4378 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4380 level->time = level_mm->time;
4381 level->gems_needed = level_mm->kettles_needed;
4382 level->auto_count_gems = level_mm->auto_count_kettles;
4384 level->mm_laser_red = level_mm->mm_laser_red;
4385 level->mm_laser_green = level_mm->mm_laser_green;
4386 level->mm_laser_blue = level_mm->mm_laser_blue;
4388 level->df_laser_red = level_mm->df_laser_red;
4389 level->df_laser_green = level_mm->df_laser_green;
4390 level->df_laser_blue = level_mm->df_laser_blue;
4392 strcpy(level->name, level_mm->name);
4394 // only overwrite author from 'levelinfo.conf' if author defined in level
4395 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4396 strcpy(level->author, level_mm->author);
4398 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4399 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4400 level->score[SC_KEY] = level_mm->score[SC_KEY];
4401 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4402 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4404 level->amoeba_speed = level_mm->amoeba_speed;
4405 level->mm_time_fuse = level_mm->time_fuse;
4406 level->mm_time_bomb = level_mm->time_bomb;
4407 level->mm_time_ball = level_mm->time_ball;
4408 level->mm_time_block = level_mm->time_block;
4410 level->num_mm_ball_contents = level_mm->num_ball_contents;
4411 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4412 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4413 level->explode_mm_ball = level_mm->explode_ball;
4415 for (i = 0; i < level->num_mm_ball_contents; i++)
4416 level->mm_ball_content[i] =
4417 map_element_MM_to_RND(level_mm->ball_content[i]);
4419 for (x = 0; x < level->fieldx; x++)
4420 for (y = 0; y < level->fieldy; y++)
4421 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4425 // ----------------------------------------------------------------------------
4426 // functions for loading DC level
4427 // ----------------------------------------------------------------------------
4429 #define DC_LEVEL_HEADER_SIZE 344
4431 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4434 static int last_data_encoded;
4438 int diff_hi, diff_lo;
4439 int data_hi, data_lo;
4440 unsigned short data_decoded;
4444 last_data_encoded = 0;
4451 diff = data_encoded - last_data_encoded;
4452 diff_hi = diff & ~0xff;
4453 diff_lo = diff & 0xff;
4457 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4458 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4459 data_hi = data_hi & 0xff00;
4461 data_decoded = data_hi | data_lo;
4463 last_data_encoded = data_encoded;
4465 offset1 = (offset1 + 1) % 31;
4466 offset2 = offset2 & 0xff;
4468 return data_decoded;
4471 static int getMappedElement_DC(int element)
4479 // 0x0117 - 0x036e: (?)
4482 // 0x042d - 0x0684: (?)
4498 element = EL_CRYSTAL;
4501 case 0x0e77: // quicksand (boulder)
4502 element = EL_QUICKSAND_FAST_FULL;
4505 case 0x0e99: // slow quicksand (boulder)
4506 element = EL_QUICKSAND_FULL;
4510 element = EL_EM_EXIT_OPEN;
4514 element = EL_EM_EXIT_CLOSED;
4518 element = EL_EM_STEEL_EXIT_OPEN;
4522 element = EL_EM_STEEL_EXIT_CLOSED;
4525 case 0x0f4f: // dynamite (lit 1)
4526 element = EL_EM_DYNAMITE_ACTIVE;
4529 case 0x0f57: // dynamite (lit 2)
4530 element = EL_EM_DYNAMITE_ACTIVE;
4533 case 0x0f5f: // dynamite (lit 3)
4534 element = EL_EM_DYNAMITE_ACTIVE;
4537 case 0x0f67: // dynamite (lit 4)
4538 element = EL_EM_DYNAMITE_ACTIVE;
4545 element = EL_AMOEBA_WET;
4549 element = EL_AMOEBA_DROP;
4553 element = EL_DC_MAGIC_WALL;
4557 element = EL_SPACESHIP_UP;
4561 element = EL_SPACESHIP_DOWN;
4565 element = EL_SPACESHIP_LEFT;
4569 element = EL_SPACESHIP_RIGHT;
4573 element = EL_BUG_UP;
4577 element = EL_BUG_DOWN;
4581 element = EL_BUG_LEFT;
4585 element = EL_BUG_RIGHT;
4589 element = EL_MOLE_UP;
4593 element = EL_MOLE_DOWN;
4597 element = EL_MOLE_LEFT;
4601 element = EL_MOLE_RIGHT;
4609 element = EL_YAMYAM_UP;
4613 element = EL_SWITCHGATE_OPEN;
4617 element = EL_SWITCHGATE_CLOSED;
4621 element = EL_DC_SWITCHGATE_SWITCH_UP;
4625 element = EL_TIMEGATE_CLOSED;
4628 case 0x144c: // conveyor belt switch (green)
4629 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4632 case 0x144f: // conveyor belt switch (red)
4633 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4636 case 0x1452: // conveyor belt switch (blue)
4637 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4641 element = EL_CONVEYOR_BELT_3_MIDDLE;
4645 element = EL_CONVEYOR_BELT_3_LEFT;
4649 element = EL_CONVEYOR_BELT_3_RIGHT;
4653 element = EL_CONVEYOR_BELT_1_MIDDLE;
4657 element = EL_CONVEYOR_BELT_1_LEFT;
4661 element = EL_CONVEYOR_BELT_1_RIGHT;
4665 element = EL_CONVEYOR_BELT_4_MIDDLE;
4669 element = EL_CONVEYOR_BELT_4_LEFT;
4673 element = EL_CONVEYOR_BELT_4_RIGHT;
4677 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4681 element = EL_EXPANDABLE_WALL_VERTICAL;
4685 element = EL_EXPANDABLE_WALL_ANY;
4688 case 0x14ce: // growing steel wall (left/right)
4689 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4692 case 0x14df: // growing steel wall (up/down)
4693 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4696 case 0x14e8: // growing steel wall (up/down/left/right)
4697 element = EL_EXPANDABLE_STEELWALL_ANY;
4701 element = EL_SHIELD_DEADLY;
4705 element = EL_EXTRA_TIME;
4713 element = EL_EMPTY_SPACE;
4716 case 0x1578: // quicksand (empty)
4717 element = EL_QUICKSAND_FAST_EMPTY;
4720 case 0x1579: // slow quicksand (empty)
4721 element = EL_QUICKSAND_EMPTY;
4731 element = EL_EM_DYNAMITE;
4734 case 0x15a1: // key (red)
4735 element = EL_EM_KEY_1;
4738 case 0x15a2: // key (yellow)
4739 element = EL_EM_KEY_2;
4742 case 0x15a3: // key (blue)
4743 element = EL_EM_KEY_4;
4746 case 0x15a4: // key (green)
4747 element = EL_EM_KEY_3;
4750 case 0x15a5: // key (white)
4751 element = EL_DC_KEY_WHITE;
4755 element = EL_WALL_SLIPPERY;
4762 case 0x15a8: // wall (not round)
4766 case 0x15a9: // (blue)
4767 element = EL_CHAR_A;
4770 case 0x15aa: // (blue)
4771 element = EL_CHAR_B;
4774 case 0x15ab: // (blue)
4775 element = EL_CHAR_C;
4778 case 0x15ac: // (blue)
4779 element = EL_CHAR_D;
4782 case 0x15ad: // (blue)
4783 element = EL_CHAR_E;
4786 case 0x15ae: // (blue)
4787 element = EL_CHAR_F;
4790 case 0x15af: // (blue)
4791 element = EL_CHAR_G;
4794 case 0x15b0: // (blue)
4795 element = EL_CHAR_H;
4798 case 0x15b1: // (blue)
4799 element = EL_CHAR_I;
4802 case 0x15b2: // (blue)
4803 element = EL_CHAR_J;
4806 case 0x15b3: // (blue)
4807 element = EL_CHAR_K;
4810 case 0x15b4: // (blue)
4811 element = EL_CHAR_L;
4814 case 0x15b5: // (blue)
4815 element = EL_CHAR_M;
4818 case 0x15b6: // (blue)
4819 element = EL_CHAR_N;
4822 case 0x15b7: // (blue)
4823 element = EL_CHAR_O;
4826 case 0x15b8: // (blue)
4827 element = EL_CHAR_P;
4830 case 0x15b9: // (blue)
4831 element = EL_CHAR_Q;
4834 case 0x15ba: // (blue)
4835 element = EL_CHAR_R;
4838 case 0x15bb: // (blue)
4839 element = EL_CHAR_S;
4842 case 0x15bc: // (blue)
4843 element = EL_CHAR_T;
4846 case 0x15bd: // (blue)
4847 element = EL_CHAR_U;
4850 case 0x15be: // (blue)
4851 element = EL_CHAR_V;
4854 case 0x15bf: // (blue)
4855 element = EL_CHAR_W;
4858 case 0x15c0: // (blue)
4859 element = EL_CHAR_X;
4862 case 0x15c1: // (blue)
4863 element = EL_CHAR_Y;
4866 case 0x15c2: // (blue)
4867 element = EL_CHAR_Z;
4870 case 0x15c3: // (blue)
4871 element = EL_CHAR_AUMLAUT;
4874 case 0x15c4: // (blue)
4875 element = EL_CHAR_OUMLAUT;
4878 case 0x15c5: // (blue)
4879 element = EL_CHAR_UUMLAUT;
4882 case 0x15c6: // (blue)
4883 element = EL_CHAR_0;
4886 case 0x15c7: // (blue)
4887 element = EL_CHAR_1;
4890 case 0x15c8: // (blue)
4891 element = EL_CHAR_2;
4894 case 0x15c9: // (blue)
4895 element = EL_CHAR_3;
4898 case 0x15ca: // (blue)
4899 element = EL_CHAR_4;
4902 case 0x15cb: // (blue)
4903 element = EL_CHAR_5;
4906 case 0x15cc: // (blue)
4907 element = EL_CHAR_6;
4910 case 0x15cd: // (blue)
4911 element = EL_CHAR_7;
4914 case 0x15ce: // (blue)
4915 element = EL_CHAR_8;
4918 case 0x15cf: // (blue)
4919 element = EL_CHAR_9;
4922 case 0x15d0: // (blue)
4923 element = EL_CHAR_PERIOD;
4926 case 0x15d1: // (blue)
4927 element = EL_CHAR_EXCLAM;
4930 case 0x15d2: // (blue)
4931 element = EL_CHAR_COLON;
4934 case 0x15d3: // (blue)
4935 element = EL_CHAR_LESS;
4938 case 0x15d4: // (blue)
4939 element = EL_CHAR_GREATER;
4942 case 0x15d5: // (blue)
4943 element = EL_CHAR_QUESTION;
4946 case 0x15d6: // (blue)
4947 element = EL_CHAR_COPYRIGHT;
4950 case 0x15d7: // (blue)
4951 element = EL_CHAR_UP;
4954 case 0x15d8: // (blue)
4955 element = EL_CHAR_DOWN;
4958 case 0x15d9: // (blue)
4959 element = EL_CHAR_BUTTON;
4962 case 0x15da: // (blue)
4963 element = EL_CHAR_PLUS;
4966 case 0x15db: // (blue)
4967 element = EL_CHAR_MINUS;
4970 case 0x15dc: // (blue)
4971 element = EL_CHAR_APOSTROPHE;
4974 case 0x15dd: // (blue)
4975 element = EL_CHAR_PARENLEFT;
4978 case 0x15de: // (blue)
4979 element = EL_CHAR_PARENRIGHT;
4982 case 0x15df: // (green)
4983 element = EL_CHAR_A;
4986 case 0x15e0: // (green)
4987 element = EL_CHAR_B;
4990 case 0x15e1: // (green)
4991 element = EL_CHAR_C;
4994 case 0x15e2: // (green)
4995 element = EL_CHAR_D;
4998 case 0x15e3: // (green)
4999 element = EL_CHAR_E;
5002 case 0x15e4: // (green)
5003 element = EL_CHAR_F;
5006 case 0x15e5: // (green)
5007 element = EL_CHAR_G;
5010 case 0x15e6: // (green)
5011 element = EL_CHAR_H;
5014 case 0x15e7: // (green)
5015 element = EL_CHAR_I;
5018 case 0x15e8: // (green)
5019 element = EL_CHAR_J;
5022 case 0x15e9: // (green)
5023 element = EL_CHAR_K;
5026 case 0x15ea: // (green)
5027 element = EL_CHAR_L;
5030 case 0x15eb: // (green)
5031 element = EL_CHAR_M;
5034 case 0x15ec: // (green)
5035 element = EL_CHAR_N;
5038 case 0x15ed: // (green)
5039 element = EL_CHAR_O;
5042 case 0x15ee: // (green)
5043 element = EL_CHAR_P;
5046 case 0x15ef: // (green)
5047 element = EL_CHAR_Q;
5050 case 0x15f0: // (green)
5051 element = EL_CHAR_R;
5054 case 0x15f1: // (green)
5055 element = EL_CHAR_S;
5058 case 0x15f2: // (green)
5059 element = EL_CHAR_T;
5062 case 0x15f3: // (green)
5063 element = EL_CHAR_U;
5066 case 0x15f4: // (green)
5067 element = EL_CHAR_V;
5070 case 0x15f5: // (green)
5071 element = EL_CHAR_W;
5074 case 0x15f6: // (green)
5075 element = EL_CHAR_X;
5078 case 0x15f7: // (green)
5079 element = EL_CHAR_Y;
5082 case 0x15f8: // (green)
5083 element = EL_CHAR_Z;
5086 case 0x15f9: // (green)
5087 element = EL_CHAR_AUMLAUT;
5090 case 0x15fa: // (green)
5091 element = EL_CHAR_OUMLAUT;
5094 case 0x15fb: // (green)
5095 element = EL_CHAR_UUMLAUT;
5098 case 0x15fc: // (green)
5099 element = EL_CHAR_0;
5102 case 0x15fd: // (green)
5103 element = EL_CHAR_1;
5106 case 0x15fe: // (green)
5107 element = EL_CHAR_2;
5110 case 0x15ff: // (green)
5111 element = EL_CHAR_3;
5114 case 0x1600: // (green)
5115 element = EL_CHAR_4;
5118 case 0x1601: // (green)
5119 element = EL_CHAR_5;
5122 case 0x1602: // (green)
5123 element = EL_CHAR_6;
5126 case 0x1603: // (green)
5127 element = EL_CHAR_7;
5130 case 0x1604: // (green)
5131 element = EL_CHAR_8;
5134 case 0x1605: // (green)
5135 element = EL_CHAR_9;
5138 case 0x1606: // (green)
5139 element = EL_CHAR_PERIOD;
5142 case 0x1607: // (green)
5143 element = EL_CHAR_EXCLAM;
5146 case 0x1608: // (green)
5147 element = EL_CHAR_COLON;
5150 case 0x1609: // (green)
5151 element = EL_CHAR_LESS;
5154 case 0x160a: // (green)
5155 element = EL_CHAR_GREATER;
5158 case 0x160b: // (green)
5159 element = EL_CHAR_QUESTION;
5162 case 0x160c: // (green)
5163 element = EL_CHAR_COPYRIGHT;
5166 case 0x160d: // (green)
5167 element = EL_CHAR_UP;
5170 case 0x160e: // (green)
5171 element = EL_CHAR_DOWN;
5174 case 0x160f: // (green)
5175 element = EL_CHAR_BUTTON;
5178 case 0x1610: // (green)
5179 element = EL_CHAR_PLUS;
5182 case 0x1611: // (green)
5183 element = EL_CHAR_MINUS;
5186 case 0x1612: // (green)
5187 element = EL_CHAR_APOSTROPHE;
5190 case 0x1613: // (green)
5191 element = EL_CHAR_PARENLEFT;
5194 case 0x1614: // (green)
5195 element = EL_CHAR_PARENRIGHT;
5198 case 0x1615: // (blue steel)
5199 element = EL_STEEL_CHAR_A;
5202 case 0x1616: // (blue steel)
5203 element = EL_STEEL_CHAR_B;
5206 case 0x1617: // (blue steel)
5207 element = EL_STEEL_CHAR_C;
5210 case 0x1618: // (blue steel)
5211 element = EL_STEEL_CHAR_D;
5214 case 0x1619: // (blue steel)
5215 element = EL_STEEL_CHAR_E;
5218 case 0x161a: // (blue steel)
5219 element = EL_STEEL_CHAR_F;
5222 case 0x161b: // (blue steel)
5223 element = EL_STEEL_CHAR_G;
5226 case 0x161c: // (blue steel)
5227 element = EL_STEEL_CHAR_H;
5230 case 0x161d: // (blue steel)
5231 element = EL_STEEL_CHAR_I;
5234 case 0x161e: // (blue steel)
5235 element = EL_STEEL_CHAR_J;
5238 case 0x161f: // (blue steel)
5239 element = EL_STEEL_CHAR_K;
5242 case 0x1620: // (blue steel)
5243 element = EL_STEEL_CHAR_L;
5246 case 0x1621: // (blue steel)
5247 element = EL_STEEL_CHAR_M;
5250 case 0x1622: // (blue steel)
5251 element = EL_STEEL_CHAR_N;
5254 case 0x1623: // (blue steel)
5255 element = EL_STEEL_CHAR_O;
5258 case 0x1624: // (blue steel)
5259 element = EL_STEEL_CHAR_P;
5262 case 0x1625: // (blue steel)
5263 element = EL_STEEL_CHAR_Q;
5266 case 0x1626: // (blue steel)
5267 element = EL_STEEL_CHAR_R;
5270 case 0x1627: // (blue steel)
5271 element = EL_STEEL_CHAR_S;
5274 case 0x1628: // (blue steel)
5275 element = EL_STEEL_CHAR_T;
5278 case 0x1629: // (blue steel)
5279 element = EL_STEEL_CHAR_U;
5282 case 0x162a: // (blue steel)
5283 element = EL_STEEL_CHAR_V;
5286 case 0x162b: // (blue steel)
5287 element = EL_STEEL_CHAR_W;
5290 case 0x162c: // (blue steel)
5291 element = EL_STEEL_CHAR_X;
5294 case 0x162d: // (blue steel)
5295 element = EL_STEEL_CHAR_Y;
5298 case 0x162e: // (blue steel)
5299 element = EL_STEEL_CHAR_Z;
5302 case 0x162f: // (blue steel)
5303 element = EL_STEEL_CHAR_AUMLAUT;
5306 case 0x1630: // (blue steel)
5307 element = EL_STEEL_CHAR_OUMLAUT;
5310 case 0x1631: // (blue steel)
5311 element = EL_STEEL_CHAR_UUMLAUT;
5314 case 0x1632: // (blue steel)
5315 element = EL_STEEL_CHAR_0;
5318 case 0x1633: // (blue steel)
5319 element = EL_STEEL_CHAR_1;
5322 case 0x1634: // (blue steel)
5323 element = EL_STEEL_CHAR_2;
5326 case 0x1635: // (blue steel)
5327 element = EL_STEEL_CHAR_3;
5330 case 0x1636: // (blue steel)
5331 element = EL_STEEL_CHAR_4;
5334 case 0x1637: // (blue steel)
5335 element = EL_STEEL_CHAR_5;
5338 case 0x1638: // (blue steel)
5339 element = EL_STEEL_CHAR_6;
5342 case 0x1639: // (blue steel)
5343 element = EL_STEEL_CHAR_7;
5346 case 0x163a: // (blue steel)
5347 element = EL_STEEL_CHAR_8;
5350 case 0x163b: // (blue steel)
5351 element = EL_STEEL_CHAR_9;
5354 case 0x163c: // (blue steel)
5355 element = EL_STEEL_CHAR_PERIOD;
5358 case 0x163d: // (blue steel)
5359 element = EL_STEEL_CHAR_EXCLAM;
5362 case 0x163e: // (blue steel)
5363 element = EL_STEEL_CHAR_COLON;
5366 case 0x163f: // (blue steel)
5367 element = EL_STEEL_CHAR_LESS;
5370 case 0x1640: // (blue steel)
5371 element = EL_STEEL_CHAR_GREATER;
5374 case 0x1641: // (blue steel)
5375 element = EL_STEEL_CHAR_QUESTION;
5378 case 0x1642: // (blue steel)
5379 element = EL_STEEL_CHAR_COPYRIGHT;
5382 case 0x1643: // (blue steel)
5383 element = EL_STEEL_CHAR_UP;
5386 case 0x1644: // (blue steel)
5387 element = EL_STEEL_CHAR_DOWN;
5390 case 0x1645: // (blue steel)
5391 element = EL_STEEL_CHAR_BUTTON;
5394 case 0x1646: // (blue steel)
5395 element = EL_STEEL_CHAR_PLUS;
5398 case 0x1647: // (blue steel)
5399 element = EL_STEEL_CHAR_MINUS;
5402 case 0x1648: // (blue steel)
5403 element = EL_STEEL_CHAR_APOSTROPHE;
5406 case 0x1649: // (blue steel)
5407 element = EL_STEEL_CHAR_PARENLEFT;
5410 case 0x164a: // (blue steel)
5411 element = EL_STEEL_CHAR_PARENRIGHT;
5414 case 0x164b: // (green steel)
5415 element = EL_STEEL_CHAR_A;
5418 case 0x164c: // (green steel)
5419 element = EL_STEEL_CHAR_B;
5422 case 0x164d: // (green steel)
5423 element = EL_STEEL_CHAR_C;
5426 case 0x164e: // (green steel)
5427 element = EL_STEEL_CHAR_D;
5430 case 0x164f: // (green steel)
5431 element = EL_STEEL_CHAR_E;
5434 case 0x1650: // (green steel)
5435 element = EL_STEEL_CHAR_F;
5438 case 0x1651: // (green steel)
5439 element = EL_STEEL_CHAR_G;
5442 case 0x1652: // (green steel)
5443 element = EL_STEEL_CHAR_H;
5446 case 0x1653: // (green steel)
5447 element = EL_STEEL_CHAR_I;
5450 case 0x1654: // (green steel)
5451 element = EL_STEEL_CHAR_J;
5454 case 0x1655: // (green steel)
5455 element = EL_STEEL_CHAR_K;
5458 case 0x1656: // (green steel)
5459 element = EL_STEEL_CHAR_L;
5462 case 0x1657: // (green steel)
5463 element = EL_STEEL_CHAR_M;
5466 case 0x1658: // (green steel)
5467 element = EL_STEEL_CHAR_N;
5470 case 0x1659: // (green steel)
5471 element = EL_STEEL_CHAR_O;
5474 case 0x165a: // (green steel)
5475 element = EL_STEEL_CHAR_P;
5478 case 0x165b: // (green steel)
5479 element = EL_STEEL_CHAR_Q;
5482 case 0x165c: // (green steel)
5483 element = EL_STEEL_CHAR_R;
5486 case 0x165d: // (green steel)
5487 element = EL_STEEL_CHAR_S;
5490 case 0x165e: // (green steel)
5491 element = EL_STEEL_CHAR_T;
5494 case 0x165f: // (green steel)
5495 element = EL_STEEL_CHAR_U;
5498 case 0x1660: // (green steel)
5499 element = EL_STEEL_CHAR_V;
5502 case 0x1661: // (green steel)
5503 element = EL_STEEL_CHAR_W;
5506 case 0x1662: // (green steel)
5507 element = EL_STEEL_CHAR_X;
5510 case 0x1663: // (green steel)
5511 element = EL_STEEL_CHAR_Y;
5514 case 0x1664: // (green steel)
5515 element = EL_STEEL_CHAR_Z;
5518 case 0x1665: // (green steel)
5519 element = EL_STEEL_CHAR_AUMLAUT;
5522 case 0x1666: // (green steel)
5523 element = EL_STEEL_CHAR_OUMLAUT;
5526 case 0x1667: // (green steel)
5527 element = EL_STEEL_CHAR_UUMLAUT;
5530 case 0x1668: // (green steel)
5531 element = EL_STEEL_CHAR_0;
5534 case 0x1669: // (green steel)
5535 element = EL_STEEL_CHAR_1;
5538 case 0x166a: // (green steel)
5539 element = EL_STEEL_CHAR_2;
5542 case 0x166b: // (green steel)
5543 element = EL_STEEL_CHAR_3;
5546 case 0x166c: // (green steel)
5547 element = EL_STEEL_CHAR_4;
5550 case 0x166d: // (green steel)
5551 element = EL_STEEL_CHAR_5;
5554 case 0x166e: // (green steel)
5555 element = EL_STEEL_CHAR_6;
5558 case 0x166f: // (green steel)
5559 element = EL_STEEL_CHAR_7;
5562 case 0x1670: // (green steel)
5563 element = EL_STEEL_CHAR_8;
5566 case 0x1671: // (green steel)
5567 element = EL_STEEL_CHAR_9;
5570 case 0x1672: // (green steel)
5571 element = EL_STEEL_CHAR_PERIOD;
5574 case 0x1673: // (green steel)
5575 element = EL_STEEL_CHAR_EXCLAM;
5578 case 0x1674: // (green steel)
5579 element = EL_STEEL_CHAR_COLON;
5582 case 0x1675: // (green steel)
5583 element = EL_STEEL_CHAR_LESS;
5586 case 0x1676: // (green steel)
5587 element = EL_STEEL_CHAR_GREATER;
5590 case 0x1677: // (green steel)
5591 element = EL_STEEL_CHAR_QUESTION;
5594 case 0x1678: // (green steel)
5595 element = EL_STEEL_CHAR_COPYRIGHT;
5598 case 0x1679: // (green steel)
5599 element = EL_STEEL_CHAR_UP;
5602 case 0x167a: // (green steel)
5603 element = EL_STEEL_CHAR_DOWN;
5606 case 0x167b: // (green steel)
5607 element = EL_STEEL_CHAR_BUTTON;
5610 case 0x167c: // (green steel)
5611 element = EL_STEEL_CHAR_PLUS;
5614 case 0x167d: // (green steel)
5615 element = EL_STEEL_CHAR_MINUS;
5618 case 0x167e: // (green steel)
5619 element = EL_STEEL_CHAR_APOSTROPHE;
5622 case 0x167f: // (green steel)
5623 element = EL_STEEL_CHAR_PARENLEFT;
5626 case 0x1680: // (green steel)
5627 element = EL_STEEL_CHAR_PARENRIGHT;
5630 case 0x1681: // gate (red)
5631 element = EL_EM_GATE_1;
5634 case 0x1682: // secret gate (red)
5635 element = EL_EM_GATE_1_GRAY;
5638 case 0x1683: // gate (yellow)
5639 element = EL_EM_GATE_2;
5642 case 0x1684: // secret gate (yellow)
5643 element = EL_EM_GATE_2_GRAY;
5646 case 0x1685: // gate (blue)
5647 element = EL_EM_GATE_4;
5650 case 0x1686: // secret gate (blue)
5651 element = EL_EM_GATE_4_GRAY;
5654 case 0x1687: // gate (green)
5655 element = EL_EM_GATE_3;
5658 case 0x1688: // secret gate (green)
5659 element = EL_EM_GATE_3_GRAY;
5662 case 0x1689: // gate (white)
5663 element = EL_DC_GATE_WHITE;
5666 case 0x168a: // secret gate (white)
5667 element = EL_DC_GATE_WHITE_GRAY;
5670 case 0x168b: // secret gate (no key)
5671 element = EL_DC_GATE_FAKE_GRAY;
5675 element = EL_ROBOT_WHEEL;
5679 element = EL_DC_TIMEGATE_SWITCH;
5683 element = EL_ACID_POOL_BOTTOM;
5687 element = EL_ACID_POOL_TOPLEFT;
5691 element = EL_ACID_POOL_TOPRIGHT;
5695 element = EL_ACID_POOL_BOTTOMLEFT;
5699 element = EL_ACID_POOL_BOTTOMRIGHT;
5703 element = EL_STEELWALL;
5707 element = EL_STEELWALL_SLIPPERY;
5710 case 0x1695: // steel wall (not round)
5711 element = EL_STEELWALL;
5714 case 0x1696: // steel wall (left)
5715 element = EL_DC_STEELWALL_1_LEFT;
5718 case 0x1697: // steel wall (bottom)
5719 element = EL_DC_STEELWALL_1_BOTTOM;
5722 case 0x1698: // steel wall (right)
5723 element = EL_DC_STEELWALL_1_RIGHT;
5726 case 0x1699: // steel wall (top)
5727 element = EL_DC_STEELWALL_1_TOP;
5730 case 0x169a: // steel wall (left/bottom)
5731 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5734 case 0x169b: // steel wall (right/bottom)
5735 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5738 case 0x169c: // steel wall (right/top)
5739 element = EL_DC_STEELWALL_1_TOPRIGHT;
5742 case 0x169d: // steel wall (left/top)
5743 element = EL_DC_STEELWALL_1_TOPLEFT;
5746 case 0x169e: // steel wall (right/bottom small)
5747 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5750 case 0x169f: // steel wall (left/bottom small)
5751 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5754 case 0x16a0: // steel wall (right/top small)
5755 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5758 case 0x16a1: // steel wall (left/top small)
5759 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5762 case 0x16a2: // steel wall (left/right)
5763 element = EL_DC_STEELWALL_1_VERTICAL;
5766 case 0x16a3: // steel wall (top/bottom)
5767 element = EL_DC_STEELWALL_1_HORIZONTAL;
5770 case 0x16a4: // steel wall 2 (left end)
5771 element = EL_DC_STEELWALL_2_LEFT;
5774 case 0x16a5: // steel wall 2 (right end)
5775 element = EL_DC_STEELWALL_2_RIGHT;
5778 case 0x16a6: // steel wall 2 (top end)
5779 element = EL_DC_STEELWALL_2_TOP;
5782 case 0x16a7: // steel wall 2 (bottom end)
5783 element = EL_DC_STEELWALL_2_BOTTOM;
5786 case 0x16a8: // steel wall 2 (left/right)
5787 element = EL_DC_STEELWALL_2_HORIZONTAL;
5790 case 0x16a9: // steel wall 2 (up/down)
5791 element = EL_DC_STEELWALL_2_VERTICAL;
5794 case 0x16aa: // steel wall 2 (mid)
5795 element = EL_DC_STEELWALL_2_MIDDLE;
5799 element = EL_SIGN_EXCLAMATION;
5803 element = EL_SIGN_RADIOACTIVITY;
5807 element = EL_SIGN_STOP;
5811 element = EL_SIGN_WHEELCHAIR;
5815 element = EL_SIGN_PARKING;
5819 element = EL_SIGN_NO_ENTRY;
5823 element = EL_SIGN_HEART;
5827 element = EL_SIGN_GIVE_WAY;
5831 element = EL_SIGN_ENTRY_FORBIDDEN;
5835 element = EL_SIGN_EMERGENCY_EXIT;
5839 element = EL_SIGN_YIN_YANG;
5843 element = EL_WALL_EMERALD;
5847 element = EL_WALL_DIAMOND;
5851 element = EL_WALL_PEARL;
5855 element = EL_WALL_CRYSTAL;
5859 element = EL_INVISIBLE_WALL;
5863 element = EL_INVISIBLE_STEELWALL;
5867 // EL_INVISIBLE_SAND
5870 element = EL_LIGHT_SWITCH;
5874 element = EL_ENVELOPE_1;
5878 if (element >= 0x0117 && element <= 0x036e) // (?)
5879 element = EL_DIAMOND;
5880 else if (element >= 0x042d && element <= 0x0684) // (?)
5881 element = EL_EMERALD;
5882 else if (element >= 0x157c && element <= 0x158b)
5884 else if (element >= 0x1590 && element <= 0x159f)
5885 element = EL_DC_LANDMINE;
5886 else if (element >= 0x16bc && element <= 0x16cb)
5887 element = EL_INVISIBLE_SAND;
5890 Warn("unknown Diamond Caves element 0x%04x", element);
5892 element = EL_UNKNOWN;
5897 return getMappedElement(element);
5900 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5902 byte header[DC_LEVEL_HEADER_SIZE];
5904 int envelope_header_pos = 62;
5905 int envelope_content_pos = 94;
5906 int level_name_pos = 251;
5907 int level_author_pos = 292;
5908 int envelope_header_len;
5909 int envelope_content_len;
5911 int level_author_len;
5913 int num_yamyam_contents;
5916 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5918 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5920 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5922 header[i * 2 + 0] = header_word >> 8;
5923 header[i * 2 + 1] = header_word & 0xff;
5926 // read some values from level header to check level decoding integrity
5927 fieldx = header[6] | (header[7] << 8);
5928 fieldy = header[8] | (header[9] << 8);
5929 num_yamyam_contents = header[60] | (header[61] << 8);
5931 // do some simple sanity checks to ensure that level was correctly decoded
5932 if (fieldx < 1 || fieldx > 256 ||
5933 fieldy < 1 || fieldy > 256 ||
5934 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5936 level->no_valid_file = TRUE;
5938 Warn("cannot decode level from stream -- using empty level");
5943 // maximum envelope header size is 31 bytes
5944 envelope_header_len = header[envelope_header_pos];
5945 // maximum envelope content size is 110 (156?) bytes
5946 envelope_content_len = header[envelope_content_pos];
5948 // maximum level title size is 40 bytes
5949 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5950 // maximum level author size is 30 (51?) bytes
5951 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5955 for (i = 0; i < envelope_header_len; i++)
5956 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5957 level->envelope[0].text[envelope_size++] =
5958 header[envelope_header_pos + 1 + i];
5960 if (envelope_header_len > 0 && envelope_content_len > 0)
5962 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5963 level->envelope[0].text[envelope_size++] = '\n';
5964 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5965 level->envelope[0].text[envelope_size++] = '\n';
5968 for (i = 0; i < envelope_content_len; i++)
5969 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5970 level->envelope[0].text[envelope_size++] =
5971 header[envelope_content_pos + 1 + i];
5973 level->envelope[0].text[envelope_size] = '\0';
5975 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5976 level->envelope[0].ysize = 10;
5977 level->envelope[0].autowrap = TRUE;
5978 level->envelope[0].centered = TRUE;
5980 for (i = 0; i < level_name_len; i++)
5981 level->name[i] = header[level_name_pos + 1 + i];
5982 level->name[level_name_len] = '\0';
5984 for (i = 0; i < level_author_len; i++)
5985 level->author[i] = header[level_author_pos + 1 + i];
5986 level->author[level_author_len] = '\0';
5988 num_yamyam_contents = header[60] | (header[61] << 8);
5989 level->num_yamyam_contents =
5990 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5992 for (i = 0; i < num_yamyam_contents; i++)
5994 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5996 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5997 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5999 if (i < MAX_ELEMENT_CONTENTS)
6000 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6004 fieldx = header[6] | (header[7] << 8);
6005 fieldy = header[8] | (header[9] << 8);
6006 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6007 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6009 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6011 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6012 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6014 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6015 level->field[x][y] = getMappedElement_DC(element_dc);
6018 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6019 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6020 level->field[x][y] = EL_PLAYER_1;
6022 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6023 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6024 level->field[x][y] = EL_PLAYER_2;
6026 level->gems_needed = header[18] | (header[19] << 8);
6028 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6029 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6030 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6031 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6032 level->score[SC_NUT] = header[28] | (header[29] << 8);
6033 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6034 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6035 level->score[SC_BUG] = header[34] | (header[35] << 8);
6036 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6037 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6038 level->score[SC_KEY] = header[40] | (header[41] << 8);
6039 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6041 level->time = header[44] | (header[45] << 8);
6043 level->amoeba_speed = header[46] | (header[47] << 8);
6044 level->time_light = header[48] | (header[49] << 8);
6045 level->time_timegate = header[50] | (header[51] << 8);
6046 level->time_wheel = header[52] | (header[53] << 8);
6047 level->time_magic_wall = header[54] | (header[55] << 8);
6048 level->extra_time = header[56] | (header[57] << 8);
6049 level->shield_normal_time = header[58] | (header[59] << 8);
6051 // shield and extra time elements do not have a score
6052 level->score[SC_SHIELD] = 0;
6053 level->extra_time_score = 0;
6055 // set time for normal and deadly shields to the same value
6056 level->shield_deadly_time = level->shield_normal_time;
6058 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6059 // can slip down from flat walls, like normal walls and steel walls
6060 level->em_slippery_gems = TRUE;
6062 // time score is counted for each 10 seconds left in Diamond Caves levels
6063 level->time_score_base = 10;
6066 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6067 struct LevelFileInfo *level_file_info,
6068 boolean level_info_only)
6070 char *filename = level_file_info->filename;
6072 int num_magic_bytes = 8;
6073 char magic_bytes[num_magic_bytes + 1];
6074 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6076 if (!(file = openFile(filename, MODE_READ)))
6078 level->no_valid_file = TRUE;
6080 if (!level_info_only)
6081 Warn("cannot read level '%s' -- using empty level", filename);
6086 // fseek(file, 0x0000, SEEK_SET);
6088 if (level_file_info->packed)
6090 // read "magic bytes" from start of file
6091 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6092 magic_bytes[0] = '\0';
6094 // check "magic bytes" for correct file format
6095 if (!strPrefix(magic_bytes, "DC2"))
6097 level->no_valid_file = TRUE;
6099 Warn("unknown DC level file '%s' -- using empty level", filename);
6104 if (strPrefix(magic_bytes, "DC2Win95") ||
6105 strPrefix(magic_bytes, "DC2Win98"))
6107 int position_first_level = 0x00fa;
6108 int extra_bytes = 4;
6111 // advance file stream to first level inside the level package
6112 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6114 // each block of level data is followed by block of non-level data
6115 num_levels_to_skip *= 2;
6117 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6118 while (num_levels_to_skip >= 0)
6120 // advance file stream to next level inside the level package
6121 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6123 level->no_valid_file = TRUE;
6125 Warn("cannot fseek in file '%s' -- using empty level", filename);
6130 // skip apparently unused extra bytes following each level
6131 ReadUnusedBytesFromFile(file, extra_bytes);
6133 // read size of next level in level package
6134 skip_bytes = getFile32BitLE(file);
6136 num_levels_to_skip--;
6141 level->no_valid_file = TRUE;
6143 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6149 LoadLevelFromFileStream_DC(file, level);
6155 // ----------------------------------------------------------------------------
6156 // functions for loading SB level
6157 // ----------------------------------------------------------------------------
6159 int getMappedElement_SB(int element_ascii, boolean use_ces)
6167 sb_element_mapping[] =
6169 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6170 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6171 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6172 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6173 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6174 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6175 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6176 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6183 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6184 if (element_ascii == sb_element_mapping[i].ascii)
6185 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6187 return EL_UNDEFINED;
6190 static void SetLevelSettings_SB(struct LevelInfo *level)
6194 level->use_step_counter = TRUE;
6197 level->score[SC_TIME_BONUS] = 0;
6198 level->time_score_base = 1;
6199 level->rate_time_over_score = TRUE;
6202 level->auto_exit_sokoban = TRUE;
6205 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6206 struct LevelFileInfo *level_file_info,
6207 boolean level_info_only)
6209 char *filename = level_file_info->filename;
6210 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6211 char last_comment[MAX_LINE_LEN];
6212 char level_name[MAX_LINE_LEN];
6215 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6216 boolean read_continued_line = FALSE;
6217 boolean reading_playfield = FALSE;
6218 boolean got_valid_playfield_line = FALSE;
6219 boolean invalid_playfield_char = FALSE;
6220 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6221 int file_level_nr = 0;
6222 int x = 0, y = 0; // initialized to make compilers happy
6224 last_comment[0] = '\0';
6225 level_name[0] = '\0';
6227 if (!(file = openFile(filename, MODE_READ)))
6229 level->no_valid_file = TRUE;
6231 if (!level_info_only)
6232 Warn("cannot read level '%s' -- using empty level", filename);
6237 while (!checkEndOfFile(file))
6239 // level successfully read, but next level may follow here
6240 if (!got_valid_playfield_line && reading_playfield)
6242 // read playfield from single level file -- skip remaining file
6243 if (!level_file_info->packed)
6246 if (file_level_nr >= num_levels_to_skip)
6251 last_comment[0] = '\0';
6252 level_name[0] = '\0';
6254 reading_playfield = FALSE;
6257 got_valid_playfield_line = FALSE;
6259 // read next line of input file
6260 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6263 // cut trailing line break (this can be newline and/or carriage return)
6264 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6265 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6268 // copy raw input line for later use (mainly debugging output)
6269 strcpy(line_raw, line);
6271 if (read_continued_line)
6273 // append new line to existing line, if there is enough space
6274 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6275 strcat(previous_line, line_ptr);
6277 strcpy(line, previous_line); // copy storage buffer to line
6279 read_continued_line = FALSE;
6282 // if the last character is '\', continue at next line
6283 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6285 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6286 strcpy(previous_line, line); // copy line to storage buffer
6288 read_continued_line = TRUE;
6294 if (line[0] == '\0')
6297 // extract comment text from comment line
6300 for (line_ptr = line; *line_ptr; line_ptr++)
6301 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6304 strcpy(last_comment, line_ptr);
6309 // extract level title text from line containing level title
6310 if (line[0] == '\'')
6312 strcpy(level_name, &line[1]);
6314 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6315 level_name[strlen(level_name) - 1] = '\0';
6320 // skip lines containing only spaces (or empty lines)
6321 for (line_ptr = line; *line_ptr; line_ptr++)
6322 if (*line_ptr != ' ')
6324 if (*line_ptr == '\0')
6327 // at this point, we have found a line containing part of a playfield
6329 got_valid_playfield_line = TRUE;
6331 if (!reading_playfield)
6333 reading_playfield = TRUE;
6334 invalid_playfield_char = FALSE;
6336 for (x = 0; x < MAX_LEV_FIELDX; x++)
6337 for (y = 0; y < MAX_LEV_FIELDY; y++)
6338 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6343 // start with topmost tile row
6347 // skip playfield line if larger row than allowed
6348 if (y >= MAX_LEV_FIELDY)
6351 // start with leftmost tile column
6354 // read playfield elements from line
6355 for (line_ptr = line; *line_ptr; line_ptr++)
6357 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6359 // stop parsing playfield line if larger column than allowed
6360 if (x >= MAX_LEV_FIELDX)
6363 if (mapped_sb_element == EL_UNDEFINED)
6365 invalid_playfield_char = TRUE;
6370 level->field[x][y] = mapped_sb_element;
6372 // continue with next tile column
6375 level->fieldx = MAX(x, level->fieldx);
6378 if (invalid_playfield_char)
6380 // if first playfield line, treat invalid lines as comment lines
6382 reading_playfield = FALSE;
6387 // continue with next tile row
6395 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6396 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6398 if (!reading_playfield)
6400 level->no_valid_file = TRUE;
6402 Warn("cannot read level '%s' -- using empty level", filename);
6407 if (*level_name != '\0')
6409 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6410 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6412 else if (*last_comment != '\0')
6414 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6415 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6419 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6422 // set all empty fields beyond the border walls to invisible steel wall
6423 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6425 if ((x == 0 || x == level->fieldx - 1 ||
6426 y == 0 || y == level->fieldy - 1) &&
6427 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6428 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6429 level->field, level->fieldx, level->fieldy);
6432 // set special level settings for Sokoban levels
6433 SetLevelSettings_SB(level);
6435 if (load_xsb_to_ces)
6437 // special global settings can now be set in level template
6438 level->use_custom_template = TRUE;
6443 // -------------------------------------------------------------------------
6444 // functions for handling native levels
6445 // -------------------------------------------------------------------------
6447 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6448 struct LevelFileInfo *level_file_info,
6449 boolean level_info_only)
6453 // determine position of requested level inside level package
6454 if (level_file_info->packed)
6455 pos = level_file_info->nr - leveldir_current->first_level;
6457 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6458 level->no_valid_file = TRUE;
6461 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6462 struct LevelFileInfo *level_file_info,
6463 boolean level_info_only)
6465 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6466 level->no_valid_file = TRUE;
6469 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6470 struct LevelFileInfo *level_file_info,
6471 boolean level_info_only)
6475 // determine position of requested level inside level package
6476 if (level_file_info->packed)
6477 pos = level_file_info->nr - leveldir_current->first_level;
6479 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6480 level->no_valid_file = TRUE;
6483 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6484 struct LevelFileInfo *level_file_info,
6485 boolean level_info_only)
6487 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6488 level->no_valid_file = TRUE;
6491 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6493 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6494 CopyNativeLevel_RND_to_BD(level);
6495 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6496 CopyNativeLevel_RND_to_EM(level);
6497 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6498 CopyNativeLevel_RND_to_SP(level);
6499 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6500 CopyNativeLevel_RND_to_MM(level);
6503 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6505 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6506 CopyNativeLevel_BD_to_RND(level);
6507 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6508 CopyNativeLevel_EM_to_RND(level);
6509 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6510 CopyNativeLevel_SP_to_RND(level);
6511 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6512 CopyNativeLevel_MM_to_RND(level);
6515 void SaveNativeLevel(struct LevelInfo *level)
6517 // saving native level files only supported for some game engines
6518 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6519 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6522 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6523 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6524 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6525 char *filename = getLevelFilenameFromBasename(basename);
6527 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6530 boolean success = FALSE;
6532 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6534 CopyNativeLevel_RND_to_BD(level);
6535 // CopyNativeTape_RND_to_BD(level);
6537 success = SaveNativeLevel_BD(filename);
6539 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6541 CopyNativeLevel_RND_to_SP(level);
6542 CopyNativeTape_RND_to_SP(level);
6544 success = SaveNativeLevel_SP(filename);
6548 Request("Native level file saved!", REQ_CONFIRM);
6550 Request("Failed to save native level file!", REQ_CONFIRM);
6554 // ----------------------------------------------------------------------------
6555 // functions for loading generic level
6556 // ----------------------------------------------------------------------------
6558 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6559 struct LevelFileInfo *level_file_info,
6560 boolean level_info_only)
6562 // always start with reliable default values
6563 setLevelInfoToDefaults(level, level_info_only, TRUE);
6565 switch (level_file_info->type)
6567 case LEVEL_FILE_TYPE_RND:
6568 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6571 case LEVEL_FILE_TYPE_BD:
6572 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6573 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6576 case LEVEL_FILE_TYPE_EM:
6577 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6578 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6581 case LEVEL_FILE_TYPE_SP:
6582 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6583 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6586 case LEVEL_FILE_TYPE_MM:
6587 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6588 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6591 case LEVEL_FILE_TYPE_DC:
6592 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6595 case LEVEL_FILE_TYPE_SB:
6596 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6600 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6604 // if level file is invalid, restore level structure to default values
6605 if (level->no_valid_file)
6606 setLevelInfoToDefaults(level, level_info_only, FALSE);
6608 if (check_special_flags("use_native_bd_game_engine"))
6609 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6611 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6612 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6614 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6615 CopyNativeLevel_Native_to_RND(level);
6618 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6620 static struct LevelFileInfo level_file_info;
6622 // always start with reliable default values
6623 setFileInfoToDefaults(&level_file_info);
6625 level_file_info.nr = 0; // unknown level number
6626 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6628 setString(&level_file_info.filename, filename);
6630 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6633 static void LoadLevel_InitVersion(struct LevelInfo *level)
6637 if (leveldir_current == NULL) // only when dumping level
6640 // all engine modifications also valid for levels which use latest engine
6641 if (level->game_version < VERSION_IDENT(3,2,0,5))
6643 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6644 level->time_score_base = 10;
6647 if (leveldir_current->latest_engine)
6649 // ---------- use latest game engine --------------------------------------
6651 /* For all levels which are forced to use the latest game engine version
6652 (normally all but user contributed, private and undefined levels), set
6653 the game engine version to the actual version; this allows for actual
6654 corrections in the game engine to take effect for existing, converted
6655 levels (from "classic" or other existing games) to make the emulation
6656 of the corresponding game more accurate, while (hopefully) not breaking
6657 existing levels created from other players. */
6659 level->game_version = GAME_VERSION_ACTUAL;
6661 /* Set special EM style gems behaviour: EM style gems slip down from
6662 normal, steel and growing wall. As this is a more fundamental change,
6663 it seems better to set the default behaviour to "off" (as it is more
6664 natural) and make it configurable in the level editor (as a property
6665 of gem style elements). Already existing converted levels (neither
6666 private nor contributed levels) are changed to the new behaviour. */
6668 if (level->file_version < FILE_VERSION_2_0)
6669 level->em_slippery_gems = TRUE;
6674 // ---------- use game engine the level was created with --------------------
6676 /* For all levels which are not forced to use the latest game engine
6677 version (normally user contributed, private and undefined levels),
6678 use the version of the game engine the levels were created for.
6680 Since 2.0.1, the game engine version is now directly stored
6681 in the level file (chunk "VERS"), so there is no need anymore
6682 to set the game version from the file version (except for old,
6683 pre-2.0 levels, where the game version is still taken from the
6684 file format version used to store the level -- see above). */
6686 // player was faster than enemies in 1.0.0 and before
6687 if (level->file_version == FILE_VERSION_1_0)
6688 for (i = 0; i < MAX_PLAYERS; i++)
6689 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6691 // default behaviour for EM style gems was "slippery" only in 2.0.1
6692 if (level->game_version == VERSION_IDENT(2,0,1,0))
6693 level->em_slippery_gems = TRUE;
6695 // springs could be pushed over pits before (pre-release version) 2.2.0
6696 if (level->game_version < VERSION_IDENT(2,2,0,0))
6697 level->use_spring_bug = TRUE;
6699 if (level->game_version < VERSION_IDENT(3,2,0,5))
6701 // time orb caused limited time in endless time levels before 3.2.0-5
6702 level->use_time_orb_bug = TRUE;
6704 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6705 level->block_snap_field = FALSE;
6707 // extra time score was same value as time left score before 3.2.0-5
6708 level->extra_time_score = level->score[SC_TIME_BONUS];
6711 if (level->game_version < VERSION_IDENT(3,2,0,7))
6713 // default behaviour for snapping was "not continuous" before 3.2.0-7
6714 level->continuous_snapping = FALSE;
6717 // only few elements were able to actively move into acid before 3.1.0
6718 // trigger settings did not exist before 3.1.0; set to default "any"
6719 if (level->game_version < VERSION_IDENT(3,1,0,0))
6721 // correct "can move into acid" settings (all zero in old levels)
6723 level->can_move_into_acid_bits = 0; // nothing can move into acid
6724 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6726 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6727 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6728 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6729 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6731 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6732 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6734 // correct trigger settings (stored as zero == "none" in old levels)
6736 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6738 int element = EL_CUSTOM_START + i;
6739 struct ElementInfo *ei = &element_info[element];
6741 for (j = 0; j < ei->num_change_pages; j++)
6743 struct ElementChangeInfo *change = &ei->change_page[j];
6745 change->trigger_player = CH_PLAYER_ANY;
6746 change->trigger_page = CH_PAGE_ANY;
6751 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6753 int element = EL_CUSTOM_256;
6754 struct ElementInfo *ei = &element_info[element];
6755 struct ElementChangeInfo *change = &ei->change_page[0];
6757 /* This is needed to fix a problem that was caused by a bugfix in function
6758 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6759 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6760 not replace walkable elements, but instead just placed the player on it,
6761 without placing the Sokoban field under the player). Unfortunately, this
6762 breaks "Snake Bite" style levels when the snake is halfway through a door
6763 that just closes (the snake head is still alive and can be moved in this
6764 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6765 player (without Sokoban element) which then gets killed as designed). */
6767 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6768 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6769 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6770 change->target_element = EL_PLAYER_1;
6773 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6774 if (level->game_version < VERSION_IDENT(3,2,5,0))
6776 /* This is needed to fix a problem that was caused by a bugfix in function
6777 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6778 corrects the behaviour when a custom element changes to another custom
6779 element with a higher element number that has change actions defined.
6780 Normally, only one change per frame is allowed for custom elements.
6781 Therefore, it is checked if a custom element already changed in the
6782 current frame; if it did, subsequent changes are suppressed.
6783 Unfortunately, this is only checked for element changes, but not for
6784 change actions, which are still executed. As the function above loops
6785 through all custom elements from lower to higher, an element change
6786 resulting in a lower CE number won't be checked again, while a target
6787 element with a higher number will also be checked, and potential change
6788 actions will get executed for this CE, too (which is wrong), while
6789 further changes are ignored (which is correct). As this bugfix breaks
6790 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6791 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6792 behaviour for existing levels and tapes that make use of this bug */
6794 level->use_action_after_change_bug = TRUE;
6797 // not centering level after relocating player was default only in 3.2.3
6798 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6799 level->shifted_relocation = TRUE;
6801 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6802 if (level->game_version < VERSION_IDENT(3,2,6,0))
6803 level->em_explodes_by_fire = TRUE;
6805 // levels were solved by the first player entering an exit up to 4.1.0.0
6806 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6807 level->solved_by_one_player = TRUE;
6809 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6810 if (level->game_version < VERSION_IDENT(4,1,1,1))
6811 level->use_life_bugs = TRUE;
6813 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6814 if (level->game_version < VERSION_IDENT(4,1,1,1))
6815 level->sb_objects_needed = FALSE;
6817 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6818 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6819 level->finish_dig_collect = FALSE;
6821 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6822 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6823 level->keep_walkable_ce = TRUE;
6826 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6828 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6831 // check if this level is (not) a Sokoban level
6832 for (y = 0; y < level->fieldy; y++)
6833 for (x = 0; x < level->fieldx; x++)
6834 if (!IS_SB_ELEMENT(Tile[x][y]))
6835 is_sokoban_level = FALSE;
6837 if (is_sokoban_level)
6839 // set special level settings for Sokoban levels
6840 SetLevelSettings_SB(level);
6844 static void LoadLevel_InitSettings(struct LevelInfo *level)
6846 // adjust level settings for (non-native) Sokoban-style levels
6847 LoadLevel_InitSettings_SB(level);
6849 // rename levels with title "nameless level" or if renaming is forced
6850 if (leveldir_current->empty_level_name != NULL &&
6851 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6852 leveldir_current->force_level_name))
6853 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6854 leveldir_current->empty_level_name, level_nr);
6857 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6861 // map elements that have changed in newer versions
6862 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6863 level->game_version);
6864 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6865 for (x = 0; x < 3; x++)
6866 for (y = 0; y < 3; y++)
6867 level->yamyam_content[i].e[x][y] =
6868 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6869 level->game_version);
6873 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6877 // map custom element change events that have changed in newer versions
6878 // (these following values were accidentally changed in version 3.0.1)
6879 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6880 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6882 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6884 int element = EL_CUSTOM_START + i;
6886 // order of checking and copying events to be mapped is important
6887 // (do not change the start and end value -- they are constant)
6888 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6890 if (HAS_CHANGE_EVENT(element, j - 2))
6892 SET_CHANGE_EVENT(element, j - 2, FALSE);
6893 SET_CHANGE_EVENT(element, j, TRUE);
6897 // order of checking and copying events to be mapped is important
6898 // (do not change the start and end value -- they are constant)
6899 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6901 if (HAS_CHANGE_EVENT(element, j - 1))
6903 SET_CHANGE_EVENT(element, j - 1, FALSE);
6904 SET_CHANGE_EVENT(element, j, TRUE);
6910 // initialize "can_change" field for old levels with only one change page
6911 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6913 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6915 int element = EL_CUSTOM_START + i;
6917 if (CAN_CHANGE(element))
6918 element_info[element].change->can_change = TRUE;
6922 // correct custom element values (for old levels without these options)
6923 if (level->game_version < VERSION_IDENT(3,1,1,0))
6925 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6927 int element = EL_CUSTOM_START + i;
6928 struct ElementInfo *ei = &element_info[element];
6930 if (ei->access_direction == MV_NO_DIRECTION)
6931 ei->access_direction = MV_ALL_DIRECTIONS;
6935 // correct custom element values (fix invalid values for all versions)
6938 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6940 int element = EL_CUSTOM_START + i;
6941 struct ElementInfo *ei = &element_info[element];
6943 for (j = 0; j < ei->num_change_pages; j++)
6945 struct ElementChangeInfo *change = &ei->change_page[j];
6947 if (change->trigger_player == CH_PLAYER_NONE)
6948 change->trigger_player = CH_PLAYER_ANY;
6950 if (change->trigger_side == CH_SIDE_NONE)
6951 change->trigger_side = CH_SIDE_ANY;
6956 // initialize "can_explode" field for old levels which did not store this
6957 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6958 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6960 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6962 int element = EL_CUSTOM_START + i;
6964 if (EXPLODES_1X1_OLD(element))
6965 element_info[element].explosion_type = EXPLODES_1X1;
6967 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6968 EXPLODES_SMASHED(element) ||
6969 EXPLODES_IMPACT(element)));
6973 // correct previously hard-coded move delay values for maze runner style
6974 if (level->game_version < VERSION_IDENT(3,1,1,0))
6976 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6978 int element = EL_CUSTOM_START + i;
6980 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6982 // previously hard-coded and therefore ignored
6983 element_info[element].move_delay_fixed = 9;
6984 element_info[element].move_delay_random = 0;
6989 // set some other uninitialized values of custom elements in older levels
6990 if (level->game_version < VERSION_IDENT(3,1,0,0))
6992 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6994 int element = EL_CUSTOM_START + i;
6996 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6998 element_info[element].explosion_delay = 17;
6999 element_info[element].ignition_delay = 8;
7003 // set mouse click change events to work for left/middle/right mouse button
7004 if (level->game_version < VERSION_IDENT(4,2,3,0))
7006 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7008 int element = EL_CUSTOM_START + i;
7009 struct ElementInfo *ei = &element_info[element];
7011 for (j = 0; j < ei->num_change_pages; j++)
7013 struct ElementChangeInfo *change = &ei->change_page[j];
7015 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7016 change->has_event[CE_PRESSED_BY_MOUSE] ||
7017 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7018 change->has_event[CE_MOUSE_PRESSED_ON_X])
7019 change->trigger_side = CH_SIDE_ANY;
7025 static void LoadLevel_InitElements(struct LevelInfo *level)
7027 LoadLevel_InitStandardElements(level);
7029 if (level->file_has_custom_elements)
7030 LoadLevel_InitCustomElements(level);
7032 // initialize element properties for level editor etc.
7033 InitElementPropertiesEngine(level->game_version);
7034 InitElementPropertiesGfxElement();
7037 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7041 // map elements that have changed in newer versions
7042 for (y = 0; y < level->fieldy; y++)
7043 for (x = 0; x < level->fieldx; x++)
7044 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7045 level->game_version);
7047 // clear unused playfield data (nicer if level gets resized in editor)
7048 for (x = 0; x < MAX_LEV_FIELDX; x++)
7049 for (y = 0; y < MAX_LEV_FIELDY; y++)
7050 if (x >= level->fieldx || y >= level->fieldy)
7051 level->field[x][y] = EL_EMPTY;
7053 // copy elements to runtime playfield array
7054 for (x = 0; x < MAX_LEV_FIELDX; x++)
7055 for (y = 0; y < MAX_LEV_FIELDY; y++)
7056 Tile[x][y] = level->field[x][y];
7058 // initialize level size variables for faster access
7059 lev_fieldx = level->fieldx;
7060 lev_fieldy = level->fieldy;
7062 // determine border element for this level
7063 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7064 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7069 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7071 struct LevelFileInfo *level_file_info = &level->file_info;
7073 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7074 CopyNativeLevel_RND_to_Native(level);
7077 static void LoadLevelTemplate_LoadAndInit(void)
7079 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7081 LoadLevel_InitVersion(&level_template);
7082 LoadLevel_InitElements(&level_template);
7083 LoadLevel_InitSettings(&level_template);
7085 ActivateLevelTemplate();
7088 void LoadLevelTemplate(int nr)
7090 if (!fileExists(getGlobalLevelTemplateFilename()))
7092 Warn("no level template found for this level");
7097 setLevelFileInfo(&level_template.file_info, nr);
7099 LoadLevelTemplate_LoadAndInit();
7102 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7104 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7106 LoadLevelTemplate_LoadAndInit();
7109 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7111 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7113 if (level.use_custom_template)
7115 if (network_level != NULL)
7116 LoadNetworkLevelTemplate(network_level);
7118 LoadLevelTemplate(-1);
7121 LoadLevel_InitVersion(&level);
7122 LoadLevel_InitElements(&level);
7123 LoadLevel_InitPlayfield(&level);
7124 LoadLevel_InitSettings(&level);
7126 LoadLevel_InitNativeEngines(&level);
7129 void LoadLevel(int nr)
7131 SetLevelSetInfo(leveldir_current->identifier, nr);
7133 setLevelFileInfo(&level.file_info, nr);
7135 LoadLevel_LoadAndInit(NULL);
7138 void LoadLevelInfoOnly(int nr)
7140 setLevelFileInfo(&level.file_info, nr);
7142 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7145 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7147 SetLevelSetInfo(network_level->leveldir_identifier,
7148 network_level->file_info.nr);
7150 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7152 LoadLevel_LoadAndInit(network_level);
7155 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7159 chunk_size += putFileVersion(file, level->file_version);
7160 chunk_size += putFileVersion(file, level->game_version);
7165 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7169 chunk_size += putFile16BitBE(file, level->creation_date.year);
7170 chunk_size += putFile8Bit(file, level->creation_date.month);
7171 chunk_size += putFile8Bit(file, level->creation_date.day);
7176 #if ENABLE_HISTORIC_CHUNKS
7177 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7181 putFile8Bit(file, level->fieldx);
7182 putFile8Bit(file, level->fieldy);
7184 putFile16BitBE(file, level->time);
7185 putFile16BitBE(file, level->gems_needed);
7187 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7188 putFile8Bit(file, level->name[i]);
7190 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7191 putFile8Bit(file, level->score[i]);
7193 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7194 for (y = 0; y < 3; y++)
7195 for (x = 0; x < 3; x++)
7196 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7197 level->yamyam_content[i].e[x][y]));
7198 putFile8Bit(file, level->amoeba_speed);
7199 putFile8Bit(file, level->time_magic_wall);
7200 putFile8Bit(file, level->time_wheel);
7201 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7202 level->amoeba_content));
7203 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7204 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7205 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7206 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7208 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7210 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7211 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7212 putFile32BitBE(file, level->can_move_into_acid_bits);
7213 putFile8Bit(file, level->dont_collide_with_bits);
7215 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7216 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7218 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7219 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7220 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7222 putFile8Bit(file, level->game_engine_type);
7224 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7228 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7233 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7234 chunk_size += putFile8Bit(file, level->name[i]);
7239 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7244 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7245 chunk_size += putFile8Bit(file, level->author[i]);
7250 #if ENABLE_HISTORIC_CHUNKS
7251 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7256 for (y = 0; y < level->fieldy; y++)
7257 for (x = 0; x < level->fieldx; x++)
7258 if (level->encoding_16bit_field)
7259 chunk_size += putFile16BitBE(file, level->field[x][y]);
7261 chunk_size += putFile8Bit(file, level->field[x][y]);
7267 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7272 for (y = 0; y < level->fieldy; y++)
7273 for (x = 0; x < level->fieldx; x++)
7274 chunk_size += putFile16BitBE(file, level->field[x][y]);
7279 #if ENABLE_HISTORIC_CHUNKS
7280 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7284 putFile8Bit(file, EL_YAMYAM);
7285 putFile8Bit(file, level->num_yamyam_contents);
7286 putFile8Bit(file, 0);
7287 putFile8Bit(file, 0);
7289 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7290 for (y = 0; y < 3; y++)
7291 for (x = 0; x < 3; x++)
7292 if (level->encoding_16bit_field)
7293 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7295 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7299 #if ENABLE_HISTORIC_CHUNKS
7300 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7303 int num_contents, content_xsize, content_ysize;
7304 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7306 if (element == EL_YAMYAM)
7308 num_contents = level->num_yamyam_contents;
7312 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7313 for (y = 0; y < 3; y++)
7314 for (x = 0; x < 3; x++)
7315 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7317 else if (element == EL_BD_AMOEBA)
7323 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7324 for (y = 0; y < 3; y++)
7325 for (x = 0; x < 3; x++)
7326 content_array[i][x][y] = EL_EMPTY;
7327 content_array[0][0][0] = level->amoeba_content;
7331 // chunk header already written -- write empty chunk data
7332 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7334 Warn("cannot save content for element '%d'", element);
7339 putFile16BitBE(file, element);
7340 putFile8Bit(file, num_contents);
7341 putFile8Bit(file, content_xsize);
7342 putFile8Bit(file, content_ysize);
7344 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7346 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7347 for (y = 0; y < 3; y++)
7348 for (x = 0; x < 3; x++)
7349 putFile16BitBE(file, content_array[i][x][y]);
7353 #if ENABLE_HISTORIC_CHUNKS
7354 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7356 int envelope_nr = element - EL_ENVELOPE_1;
7357 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7361 chunk_size += putFile16BitBE(file, element);
7362 chunk_size += putFile16BitBE(file, envelope_len);
7363 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7364 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7366 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7367 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7369 for (i = 0; i < envelope_len; i++)
7370 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7376 #if ENABLE_HISTORIC_CHUNKS
7377 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7378 int num_changed_custom_elements)
7382 putFile16BitBE(file, num_changed_custom_elements);
7384 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7386 int element = EL_CUSTOM_START + i;
7388 struct ElementInfo *ei = &element_info[element];
7390 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7392 if (check < num_changed_custom_elements)
7394 putFile16BitBE(file, element);
7395 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7402 if (check != num_changed_custom_elements) // should not happen
7403 Warn("inconsistent number of custom element properties");
7407 #if ENABLE_HISTORIC_CHUNKS
7408 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7409 int num_changed_custom_elements)
7413 putFile16BitBE(file, num_changed_custom_elements);
7415 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7417 int element = EL_CUSTOM_START + i;
7419 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7421 if (check < num_changed_custom_elements)
7423 putFile16BitBE(file, element);
7424 putFile16BitBE(file, element_info[element].change->target_element);
7431 if (check != num_changed_custom_elements) // should not happen
7432 Warn("inconsistent number of custom target elements");
7436 #if ENABLE_HISTORIC_CHUNKS
7437 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7438 int num_changed_custom_elements)
7440 int i, j, x, y, check = 0;
7442 putFile16BitBE(file, num_changed_custom_elements);
7444 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7446 int element = EL_CUSTOM_START + i;
7447 struct ElementInfo *ei = &element_info[element];
7449 if (ei->modified_settings)
7451 if (check < num_changed_custom_elements)
7453 putFile16BitBE(file, element);
7455 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7456 putFile8Bit(file, ei->description[j]);
7458 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7460 // some free bytes for future properties and padding
7461 WriteUnusedBytesToFile(file, 7);
7463 putFile8Bit(file, ei->use_gfx_element);
7464 putFile16BitBE(file, ei->gfx_element_initial);
7466 putFile8Bit(file, ei->collect_score_initial);
7467 putFile8Bit(file, ei->collect_count_initial);
7469 putFile16BitBE(file, ei->push_delay_fixed);
7470 putFile16BitBE(file, ei->push_delay_random);
7471 putFile16BitBE(file, ei->move_delay_fixed);
7472 putFile16BitBE(file, ei->move_delay_random);
7474 putFile16BitBE(file, ei->move_pattern);
7475 putFile8Bit(file, ei->move_direction_initial);
7476 putFile8Bit(file, ei->move_stepsize);
7478 for (y = 0; y < 3; y++)
7479 for (x = 0; x < 3; x++)
7480 putFile16BitBE(file, ei->content.e[x][y]);
7482 putFile32BitBE(file, ei->change->events);
7484 putFile16BitBE(file, ei->change->target_element);
7486 putFile16BitBE(file, ei->change->delay_fixed);
7487 putFile16BitBE(file, ei->change->delay_random);
7488 putFile16BitBE(file, ei->change->delay_frames);
7490 putFile16BitBE(file, ei->change->initial_trigger_element);
7492 putFile8Bit(file, ei->change->explode);
7493 putFile8Bit(file, ei->change->use_target_content);
7494 putFile8Bit(file, ei->change->only_if_complete);
7495 putFile8Bit(file, ei->change->use_random_replace);
7497 putFile8Bit(file, ei->change->random_percentage);
7498 putFile8Bit(file, ei->change->replace_when);
7500 for (y = 0; y < 3; y++)
7501 for (x = 0; x < 3; x++)
7502 putFile16BitBE(file, ei->change->content.e[x][y]);
7504 putFile8Bit(file, ei->slippery_type);
7506 // some free bytes for future properties and padding
7507 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7514 if (check != num_changed_custom_elements) // should not happen
7515 Warn("inconsistent number of custom element properties");
7519 #if ENABLE_HISTORIC_CHUNKS
7520 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7522 struct ElementInfo *ei = &element_info[element];
7525 // ---------- custom element base property values (96 bytes) ----------------
7527 putFile16BitBE(file, element);
7529 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7530 putFile8Bit(file, ei->description[i]);
7532 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7534 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7536 putFile8Bit(file, ei->num_change_pages);
7538 putFile16BitBE(file, ei->ce_value_fixed_initial);
7539 putFile16BitBE(file, ei->ce_value_random_initial);
7540 putFile8Bit(file, ei->use_last_ce_value);
7542 putFile8Bit(file, ei->use_gfx_element);
7543 putFile16BitBE(file, ei->gfx_element_initial);
7545 putFile8Bit(file, ei->collect_score_initial);
7546 putFile8Bit(file, ei->collect_count_initial);
7548 putFile8Bit(file, ei->drop_delay_fixed);
7549 putFile8Bit(file, ei->push_delay_fixed);
7550 putFile8Bit(file, ei->drop_delay_random);
7551 putFile8Bit(file, ei->push_delay_random);
7552 putFile16BitBE(file, ei->move_delay_fixed);
7553 putFile16BitBE(file, ei->move_delay_random);
7555 // bits 0 - 15 of "move_pattern" ...
7556 putFile16BitBE(file, ei->move_pattern & 0xffff);
7557 putFile8Bit(file, ei->move_direction_initial);
7558 putFile8Bit(file, ei->move_stepsize);
7560 putFile8Bit(file, ei->slippery_type);
7562 for (y = 0; y < 3; y++)
7563 for (x = 0; x < 3; x++)
7564 putFile16BitBE(file, ei->content.e[x][y]);
7566 putFile16BitBE(file, ei->move_enter_element);
7567 putFile16BitBE(file, ei->move_leave_element);
7568 putFile8Bit(file, ei->move_leave_type);
7570 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7571 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7573 putFile8Bit(file, ei->access_direction);
7575 putFile8Bit(file, ei->explosion_delay);
7576 putFile8Bit(file, ei->ignition_delay);
7577 putFile8Bit(file, ei->explosion_type);
7579 // some free bytes for future custom property values and padding
7580 WriteUnusedBytesToFile(file, 1);
7582 // ---------- change page property values (48 bytes) ------------------------
7584 for (i = 0; i < ei->num_change_pages; i++)
7586 struct ElementChangeInfo *change = &ei->change_page[i];
7587 unsigned int event_bits;
7589 // bits 0 - 31 of "has_event[]" ...
7591 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7592 if (change->has_event[j])
7593 event_bits |= (1u << j);
7594 putFile32BitBE(file, event_bits);
7596 putFile16BitBE(file, change->target_element);
7598 putFile16BitBE(file, change->delay_fixed);
7599 putFile16BitBE(file, change->delay_random);
7600 putFile16BitBE(file, change->delay_frames);
7602 putFile16BitBE(file, change->initial_trigger_element);
7604 putFile8Bit(file, change->explode);
7605 putFile8Bit(file, change->use_target_content);
7606 putFile8Bit(file, change->only_if_complete);
7607 putFile8Bit(file, change->use_random_replace);
7609 putFile8Bit(file, change->random_percentage);
7610 putFile8Bit(file, change->replace_when);
7612 for (y = 0; y < 3; y++)
7613 for (x = 0; x < 3; x++)
7614 putFile16BitBE(file, change->target_content.e[x][y]);
7616 putFile8Bit(file, change->can_change);
7618 putFile8Bit(file, change->trigger_side);
7620 putFile8Bit(file, change->trigger_player);
7621 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7622 log_2(change->trigger_page)));
7624 putFile8Bit(file, change->has_action);
7625 putFile8Bit(file, change->action_type);
7626 putFile8Bit(file, change->action_mode);
7627 putFile16BitBE(file, change->action_arg);
7629 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7631 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7632 if (change->has_event[j])
7633 event_bits |= (1u << (j - 32));
7634 putFile8Bit(file, event_bits);
7639 #if ENABLE_HISTORIC_CHUNKS
7640 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7642 struct ElementInfo *ei = &element_info[element];
7643 struct ElementGroupInfo *group = ei->group;
7646 putFile16BitBE(file, element);
7648 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7649 putFile8Bit(file, ei->description[i]);
7651 putFile8Bit(file, group->num_elements);
7653 putFile8Bit(file, ei->use_gfx_element);
7654 putFile16BitBE(file, ei->gfx_element_initial);
7656 putFile8Bit(file, group->choice_mode);
7658 // some free bytes for future values and padding
7659 WriteUnusedBytesToFile(file, 3);
7661 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7662 putFile16BitBE(file, group->element[i]);
7666 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7667 boolean write_element)
7669 int save_type = entry->save_type;
7670 int data_type = entry->data_type;
7671 int conf_type = entry->conf_type;
7672 int byte_mask = conf_type & CONF_MASK_BYTES;
7673 int element = entry->element;
7674 int default_value = entry->default_value;
7676 boolean modified = FALSE;
7678 if (byte_mask != CONF_MASK_MULTI_BYTES)
7680 void *value_ptr = entry->value;
7681 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7684 // check if any settings have been modified before saving them
7685 if (value != default_value)
7688 // do not save if explicitly told or if unmodified default settings
7689 if ((save_type == SAVE_CONF_NEVER) ||
7690 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7694 num_bytes += putFile16BitBE(file, element);
7696 num_bytes += putFile8Bit(file, conf_type);
7697 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7698 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7699 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7702 else if (data_type == TYPE_STRING)
7704 char *default_string = entry->default_string;
7705 char *string = (char *)(entry->value);
7706 int string_length = strlen(string);
7709 // check if any settings have been modified before saving them
7710 if (!strEqual(string, default_string))
7713 // do not save if explicitly told or if unmodified default settings
7714 if ((save_type == SAVE_CONF_NEVER) ||
7715 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7719 num_bytes += putFile16BitBE(file, element);
7721 num_bytes += putFile8Bit(file, conf_type);
7722 num_bytes += putFile16BitBE(file, string_length);
7724 for (i = 0; i < string_length; i++)
7725 num_bytes += putFile8Bit(file, string[i]);
7727 else if (data_type == TYPE_ELEMENT_LIST)
7729 int *element_array = (int *)(entry->value);
7730 int num_elements = *(int *)(entry->num_entities);
7733 // check if any settings have been modified before saving them
7734 for (i = 0; i < num_elements; i++)
7735 if (element_array[i] != default_value)
7738 // do not save if explicitly told or if unmodified default settings
7739 if ((save_type == SAVE_CONF_NEVER) ||
7740 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7744 num_bytes += putFile16BitBE(file, element);
7746 num_bytes += putFile8Bit(file, conf_type);
7747 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7749 for (i = 0; i < num_elements; i++)
7750 num_bytes += putFile16BitBE(file, element_array[i]);
7752 else if (data_type == TYPE_CONTENT_LIST)
7754 struct Content *content = (struct Content *)(entry->value);
7755 int num_contents = *(int *)(entry->num_entities);
7758 // check if any settings have been modified before saving them
7759 for (i = 0; i < num_contents; i++)
7760 for (y = 0; y < 3; y++)
7761 for (x = 0; x < 3; x++)
7762 if (content[i].e[x][y] != default_value)
7765 // do not save if explicitly told or if unmodified default settings
7766 if ((save_type == SAVE_CONF_NEVER) ||
7767 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7771 num_bytes += putFile16BitBE(file, element);
7773 num_bytes += putFile8Bit(file, conf_type);
7774 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7776 for (i = 0; i < num_contents; i++)
7777 for (y = 0; y < 3; y++)
7778 for (x = 0; x < 3; x++)
7779 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7785 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7790 li = *level; // copy level data into temporary buffer
7792 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7793 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7798 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7803 li = *level; // copy level data into temporary buffer
7805 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7806 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7811 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7813 int envelope_nr = element - EL_ENVELOPE_1;
7817 chunk_size += putFile16BitBE(file, element);
7819 // copy envelope data into temporary buffer
7820 xx_envelope = level->envelope[envelope_nr];
7822 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7823 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7828 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7830 struct ElementInfo *ei = &element_info[element];
7834 chunk_size += putFile16BitBE(file, element);
7836 xx_ei = *ei; // copy element data into temporary buffer
7838 // set default description string for this specific element
7839 strcpy(xx_default_description, getDefaultElementDescription(ei));
7841 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7842 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7844 for (i = 0; i < ei->num_change_pages; i++)
7846 struct ElementChangeInfo *change = &ei->change_page[i];
7848 xx_current_change_page = i;
7850 xx_change = *change; // copy change data into temporary buffer
7853 setEventBitsFromEventFlags(change);
7855 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7856 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7863 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7865 struct ElementInfo *ei = &element_info[element];
7866 struct ElementGroupInfo *group = ei->group;
7870 chunk_size += putFile16BitBE(file, element);
7872 xx_ei = *ei; // copy element data into temporary buffer
7873 xx_group = *group; // copy group data into temporary buffer
7875 // set default description string for this specific element
7876 strcpy(xx_default_description, getDefaultElementDescription(ei));
7878 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7879 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7884 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7886 struct ElementInfo *ei = &element_info[element];
7890 chunk_size += putFile16BitBE(file, element);
7892 xx_ei = *ei; // copy element data into temporary buffer
7894 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7895 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7900 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7901 boolean save_as_template)
7907 if (!(file = fopen(filename, MODE_WRITE)))
7909 Warn("cannot save level file '%s'", filename);
7914 level->file_version = FILE_VERSION_ACTUAL;
7915 level->game_version = GAME_VERSION_ACTUAL;
7917 level->creation_date = getCurrentDate();
7919 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7920 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7922 chunk_size = SaveLevel_VERS(NULL, level);
7923 putFileChunkBE(file, "VERS", chunk_size);
7924 SaveLevel_VERS(file, level);
7926 chunk_size = SaveLevel_DATE(NULL, level);
7927 putFileChunkBE(file, "DATE", chunk_size);
7928 SaveLevel_DATE(file, level);
7930 chunk_size = SaveLevel_NAME(NULL, level);
7931 putFileChunkBE(file, "NAME", chunk_size);
7932 SaveLevel_NAME(file, level);
7934 chunk_size = SaveLevel_AUTH(NULL, level);
7935 putFileChunkBE(file, "AUTH", chunk_size);
7936 SaveLevel_AUTH(file, level);
7938 chunk_size = SaveLevel_INFO(NULL, level);
7939 putFileChunkBE(file, "INFO", chunk_size);
7940 SaveLevel_INFO(file, level);
7942 chunk_size = SaveLevel_BODY(NULL, level);
7943 putFileChunkBE(file, "BODY", chunk_size);
7944 SaveLevel_BODY(file, level);
7946 chunk_size = SaveLevel_ELEM(NULL, level);
7947 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7949 putFileChunkBE(file, "ELEM", chunk_size);
7950 SaveLevel_ELEM(file, level);
7953 for (i = 0; i < NUM_ENVELOPES; i++)
7955 int element = EL_ENVELOPE_1 + i;
7957 chunk_size = SaveLevel_NOTE(NULL, level, element);
7958 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7960 putFileChunkBE(file, "NOTE", chunk_size);
7961 SaveLevel_NOTE(file, level, element);
7965 // if not using template level, check for non-default custom/group elements
7966 if (!level->use_custom_template || save_as_template)
7968 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7970 int element = EL_CUSTOM_START + i;
7972 chunk_size = SaveLevel_CUSX(NULL, level, element);
7973 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7975 putFileChunkBE(file, "CUSX", chunk_size);
7976 SaveLevel_CUSX(file, level, element);
7980 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7982 int element = EL_GROUP_START + i;
7984 chunk_size = SaveLevel_GRPX(NULL, level, element);
7985 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7987 putFileChunkBE(file, "GRPX", chunk_size);
7988 SaveLevel_GRPX(file, level, element);
7992 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7994 int element = GET_EMPTY_ELEMENT(i);
7996 chunk_size = SaveLevel_EMPX(NULL, level, element);
7997 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7999 putFileChunkBE(file, "EMPX", chunk_size);
8000 SaveLevel_EMPX(file, level, element);
8007 SetFilePermissions(filename, PERMS_PRIVATE);
8010 void SaveLevel(int nr)
8012 char *filename = getDefaultLevelFilename(nr);
8014 SaveLevelFromFilename(&level, filename, FALSE);
8017 void SaveLevelTemplate(void)
8019 char *filename = getLocalLevelTemplateFilename();
8021 SaveLevelFromFilename(&level, filename, TRUE);
8024 boolean SaveLevelChecked(int nr)
8026 char *filename = getDefaultLevelFilename(nr);
8027 boolean new_level = !fileExists(filename);
8028 boolean level_saved = FALSE;
8030 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8035 Request("Level saved!", REQ_CONFIRM);
8043 void DumpLevel(struct LevelInfo *level)
8045 if (level->no_level_file || level->no_valid_file)
8047 Warn("cannot dump -- no valid level file found");
8053 Print("Level xxx (file version %08d, game version %08d)\n",
8054 level->file_version, level->game_version);
8057 Print("Level author: '%s'\n", level->author);
8058 Print("Level title: '%s'\n", level->name);
8060 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8062 Print("Level time: %d seconds\n", level->time);
8063 Print("Gems needed: %d\n", level->gems_needed);
8065 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8066 Print("Time for wheel: %d seconds\n", level->time_wheel);
8067 Print("Time for light: %d seconds\n", level->time_light);
8068 Print("Time for timegate: %d seconds\n", level->time_timegate);
8070 Print("Amoeba speed: %d\n", level->amoeba_speed);
8073 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8074 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8075 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8076 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8077 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8078 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8084 for (i = 0; i < NUM_ENVELOPES; i++)
8086 char *text = level->envelope[i].text;
8087 int text_len = strlen(text);
8088 boolean has_text = FALSE;
8090 for (j = 0; j < text_len; j++)
8091 if (text[j] != ' ' && text[j] != '\n')
8097 Print("Envelope %d:\n'%s'\n", i + 1, text);
8105 void DumpLevels(void)
8107 static LevelDirTree *dumplevel_leveldir = NULL;
8109 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8110 global.dumplevel_leveldir);
8112 if (dumplevel_leveldir == NULL)
8113 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8115 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8116 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8117 Fail("no such level number: %d", global.dumplevel_level_nr);
8119 leveldir_current = dumplevel_leveldir;
8121 LoadLevel(global.dumplevel_level_nr);
8128 // ============================================================================
8129 // tape file functions
8130 // ============================================================================
8132 static void setTapeInfoToDefaults(void)
8136 // always start with reliable default values (empty tape)
8139 // default values (also for pre-1.2 tapes) with only the first player
8140 tape.player_participates[0] = TRUE;
8141 for (i = 1; i < MAX_PLAYERS; i++)
8142 tape.player_participates[i] = FALSE;
8144 // at least one (default: the first) player participates in every tape
8145 tape.num_participating_players = 1;
8147 tape.property_bits = TAPE_PROPERTY_NONE;
8149 tape.level_nr = level_nr;
8151 tape.changed = FALSE;
8152 tape.solved = FALSE;
8154 tape.recording = FALSE;
8155 tape.playing = FALSE;
8156 tape.pausing = FALSE;
8158 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8159 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8161 tape.no_info_chunk = TRUE;
8162 tape.no_valid_file = FALSE;
8165 static int getTapePosSize(struct TapeInfo *tape)
8167 int tape_pos_size = 0;
8169 if (tape->use_key_actions)
8170 tape_pos_size += tape->num_participating_players;
8172 if (tape->use_mouse_actions)
8173 tape_pos_size += 3; // x and y position and mouse button mask
8175 tape_pos_size += 1; // tape action delay value
8177 return tape_pos_size;
8180 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8182 tape->use_key_actions = FALSE;
8183 tape->use_mouse_actions = FALSE;
8185 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8186 tape->use_key_actions = TRUE;
8188 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8189 tape->use_mouse_actions = TRUE;
8192 static int getTapeActionValue(struct TapeInfo *tape)
8194 return (tape->use_key_actions &&
8195 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8196 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8197 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8198 TAPE_ACTIONS_DEFAULT);
8201 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8203 tape->file_version = getFileVersion(file);
8204 tape->game_version = getFileVersion(file);
8209 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8213 tape->random_seed = getFile32BitBE(file);
8214 tape->date = getFile32BitBE(file);
8215 tape->length = getFile32BitBE(file);
8217 // read header fields that are new since version 1.2
8218 if (tape->file_version >= FILE_VERSION_1_2)
8220 byte store_participating_players = getFile8Bit(file);
8223 // since version 1.2, tapes store which players participate in the tape
8224 tape->num_participating_players = 0;
8225 for (i = 0; i < MAX_PLAYERS; i++)
8227 tape->player_participates[i] = FALSE;
8229 if (store_participating_players & (1 << i))
8231 tape->player_participates[i] = TRUE;
8232 tape->num_participating_players++;
8236 setTapeActionFlags(tape, getFile8Bit(file));
8238 tape->property_bits = getFile8Bit(file);
8239 tape->solved = getFile8Bit(file);
8241 engine_version = getFileVersion(file);
8242 if (engine_version > 0)
8243 tape->engine_version = engine_version;
8245 tape->engine_version = tape->game_version;
8251 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8253 tape->scr_fieldx = getFile8Bit(file);
8254 tape->scr_fieldy = getFile8Bit(file);
8259 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8261 char *level_identifier = NULL;
8262 int level_identifier_size;
8265 tape->no_info_chunk = FALSE;
8267 level_identifier_size = getFile16BitBE(file);
8269 level_identifier = checked_malloc(level_identifier_size);
8271 for (i = 0; i < level_identifier_size; i++)
8272 level_identifier[i] = getFile8Bit(file);
8274 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8275 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8277 checked_free(level_identifier);
8279 tape->level_nr = getFile16BitBE(file);
8281 chunk_size = 2 + level_identifier_size + 2;
8286 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8289 int tape_pos_size = getTapePosSize(tape);
8290 int chunk_size_expected = tape_pos_size * tape->length;
8292 if (chunk_size_expected != chunk_size)
8294 ReadUnusedBytesFromFile(file, chunk_size);
8295 return chunk_size_expected;
8298 for (i = 0; i < tape->length; i++)
8300 if (i >= MAX_TAPE_LEN)
8302 Warn("tape truncated -- size exceeds maximum tape size %d",
8305 // tape too large; read and ignore remaining tape data from this chunk
8306 for (;i < tape->length; i++)
8307 ReadUnusedBytesFromFile(file, tape_pos_size);
8312 if (tape->use_key_actions)
8314 for (j = 0; j < MAX_PLAYERS; j++)
8316 tape->pos[i].action[j] = MV_NONE;
8318 if (tape->player_participates[j])
8319 tape->pos[i].action[j] = getFile8Bit(file);
8323 if (tape->use_mouse_actions)
8325 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8326 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8327 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8330 tape->pos[i].delay = getFile8Bit(file);
8332 if (tape->file_version == FILE_VERSION_1_0)
8334 // eliminate possible diagonal moves in old tapes
8335 // this is only for backward compatibility
8337 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8338 byte action = tape->pos[i].action[0];
8339 int k, num_moves = 0;
8341 for (k = 0; k < 4; k++)
8343 if (action & joy_dir[k])
8345 tape->pos[i + num_moves].action[0] = joy_dir[k];
8347 tape->pos[i + num_moves].delay = 0;
8356 tape->length += num_moves;
8359 else if (tape->file_version < FILE_VERSION_2_0)
8361 // convert pre-2.0 tapes to new tape format
8363 if (tape->pos[i].delay > 1)
8366 tape->pos[i + 1] = tape->pos[i];
8367 tape->pos[i + 1].delay = 1;
8370 for (j = 0; j < MAX_PLAYERS; j++)
8371 tape->pos[i].action[j] = MV_NONE;
8372 tape->pos[i].delay--;
8379 if (checkEndOfFile(file))
8383 if (i != tape->length)
8384 chunk_size = tape_pos_size * i;
8389 static void LoadTape_SokobanSolution(char *filename)
8392 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8394 if (!(file = openFile(filename, MODE_READ)))
8396 tape.no_valid_file = TRUE;
8401 while (!checkEndOfFile(file))
8403 unsigned char c = getByteFromFile(file);
8405 if (checkEndOfFile(file))
8412 tape.pos[tape.length].action[0] = MV_UP;
8413 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8419 tape.pos[tape.length].action[0] = MV_DOWN;
8420 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8426 tape.pos[tape.length].action[0] = MV_LEFT;
8427 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8433 tape.pos[tape.length].action[0] = MV_RIGHT;
8434 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8442 // ignore white-space characters
8446 tape.no_valid_file = TRUE;
8448 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8456 if (tape.no_valid_file)
8459 tape.length_frames = GetTapeLengthFrames();
8460 tape.length_seconds = GetTapeLengthSeconds();
8463 void LoadTapeFromFilename(char *filename)
8465 char cookie[MAX_LINE_LEN];
8466 char chunk_name[CHUNK_ID_LEN + 1];
8470 // always start with reliable default values
8471 setTapeInfoToDefaults();
8473 if (strSuffix(filename, ".sln"))
8475 LoadTape_SokobanSolution(filename);
8480 if (!(file = openFile(filename, MODE_READ)))
8482 tape.no_valid_file = TRUE;
8487 getFileChunkBE(file, chunk_name, NULL);
8488 if (strEqual(chunk_name, "RND1"))
8490 getFile32BitBE(file); // not used
8492 getFileChunkBE(file, chunk_name, NULL);
8493 if (!strEqual(chunk_name, "TAPE"))
8495 tape.no_valid_file = TRUE;
8497 Warn("unknown format of tape file '%s'", filename);
8504 else // check for pre-2.0 file format with cookie string
8506 strcpy(cookie, chunk_name);
8507 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8509 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8510 cookie[strlen(cookie) - 1] = '\0';
8512 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8514 tape.no_valid_file = TRUE;
8516 Warn("unknown format of tape file '%s'", filename);
8523 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8525 tape.no_valid_file = TRUE;
8527 Warn("unsupported version of tape file '%s'", filename);
8534 // pre-2.0 tape files have no game version, so use file version here
8535 tape.game_version = tape.file_version;
8538 if (tape.file_version < FILE_VERSION_1_2)
8540 // tape files from versions before 1.2.0 without chunk structure
8541 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8542 LoadTape_BODY(file, 2 * tape.length, &tape);
8550 int (*loader)(File *, int, struct TapeInfo *);
8554 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8555 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8556 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8557 { "INFO", -1, LoadTape_INFO },
8558 { "BODY", -1, LoadTape_BODY },
8562 while (getFileChunkBE(file, chunk_name, &chunk_size))
8566 while (chunk_info[i].name != NULL &&
8567 !strEqual(chunk_name, chunk_info[i].name))
8570 if (chunk_info[i].name == NULL)
8572 Warn("unknown chunk '%s' in tape file '%s'",
8573 chunk_name, filename);
8575 ReadUnusedBytesFromFile(file, chunk_size);
8577 else if (chunk_info[i].size != -1 &&
8578 chunk_info[i].size != chunk_size)
8580 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8581 chunk_size, chunk_name, filename);
8583 ReadUnusedBytesFromFile(file, chunk_size);
8587 // call function to load this tape chunk
8588 int chunk_size_expected =
8589 (chunk_info[i].loader)(file, chunk_size, &tape);
8591 // the size of some chunks cannot be checked before reading other
8592 // chunks first (like "HEAD" and "BODY") that contain some header
8593 // information, so check them here
8594 if (chunk_size_expected != chunk_size)
8596 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8597 chunk_size, chunk_name, filename);
8605 tape.length_frames = GetTapeLengthFrames();
8606 tape.length_seconds = GetTapeLengthSeconds();
8609 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8611 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8613 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8614 tape.engine_version);
8618 void LoadTape(int nr)
8620 char *filename = getTapeFilename(nr);
8622 LoadTapeFromFilename(filename);
8625 void LoadSolutionTape(int nr)
8627 char *filename = getSolutionTapeFilename(nr);
8629 LoadTapeFromFilename(filename);
8631 if (TAPE_IS_EMPTY(tape))
8633 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8634 level.native_bd_level->replay != NULL)
8635 CopyNativeTape_BD_to_RND(&level);
8636 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8637 level.native_sp_level->demo.is_available)
8638 CopyNativeTape_SP_to_RND(&level);
8642 void LoadScoreTape(char *score_tape_basename, int nr)
8644 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8646 LoadTapeFromFilename(filename);
8649 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8651 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8653 LoadTapeFromFilename(filename);
8656 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8658 // chunk required for team mode tapes with non-default screen size
8659 return (tape->num_participating_players > 1 &&
8660 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8661 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8664 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8666 putFileVersion(file, tape->file_version);
8667 putFileVersion(file, tape->game_version);
8670 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8673 byte store_participating_players = 0;
8675 // set bits for participating players for compact storage
8676 for (i = 0; i < MAX_PLAYERS; i++)
8677 if (tape->player_participates[i])
8678 store_participating_players |= (1 << i);
8680 putFile32BitBE(file, tape->random_seed);
8681 putFile32BitBE(file, tape->date);
8682 putFile32BitBE(file, tape->length);
8684 putFile8Bit(file, store_participating_players);
8686 putFile8Bit(file, getTapeActionValue(tape));
8688 putFile8Bit(file, tape->property_bits);
8689 putFile8Bit(file, tape->solved);
8691 putFileVersion(file, tape->engine_version);
8694 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8696 putFile8Bit(file, tape->scr_fieldx);
8697 putFile8Bit(file, tape->scr_fieldy);
8700 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8702 int level_identifier_size = strlen(tape->level_identifier) + 1;
8705 putFile16BitBE(file, level_identifier_size);
8707 for (i = 0; i < level_identifier_size; i++)
8708 putFile8Bit(file, tape->level_identifier[i]);
8710 putFile16BitBE(file, tape->level_nr);
8713 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8717 for (i = 0; i < tape->length; i++)
8719 if (tape->use_key_actions)
8721 for (j = 0; j < MAX_PLAYERS; j++)
8722 if (tape->player_participates[j])
8723 putFile8Bit(file, tape->pos[i].action[j]);
8726 if (tape->use_mouse_actions)
8728 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8729 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8730 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8733 putFile8Bit(file, tape->pos[i].delay);
8737 void SaveTapeToFilename(char *filename)
8741 int info_chunk_size;
8742 int body_chunk_size;
8744 if (!(file = fopen(filename, MODE_WRITE)))
8746 Warn("cannot save level recording file '%s'", filename);
8751 tape_pos_size = getTapePosSize(&tape);
8753 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8754 body_chunk_size = tape_pos_size * tape.length;
8756 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8757 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8759 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8760 SaveTape_VERS(file, &tape);
8762 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8763 SaveTape_HEAD(file, &tape);
8765 if (checkSaveTape_SCRN(&tape))
8767 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8768 SaveTape_SCRN(file, &tape);
8771 putFileChunkBE(file, "INFO", info_chunk_size);
8772 SaveTape_INFO(file, &tape);
8774 putFileChunkBE(file, "BODY", body_chunk_size);
8775 SaveTape_BODY(file, &tape);
8779 SetFilePermissions(filename, PERMS_PRIVATE);
8782 static void SaveTapeExt(char *filename)
8786 tape.file_version = FILE_VERSION_ACTUAL;
8787 tape.game_version = GAME_VERSION_ACTUAL;
8789 tape.num_participating_players = 0;
8791 // count number of participating players
8792 for (i = 0; i < MAX_PLAYERS; i++)
8793 if (tape.player_participates[i])
8794 tape.num_participating_players++;
8796 SaveTapeToFilename(filename);
8798 tape.changed = FALSE;
8801 void SaveTape(int nr)
8803 char *filename = getTapeFilename(nr);
8805 InitTapeDirectory(leveldir_current->subdir);
8807 SaveTapeExt(filename);
8810 void SaveScoreTape(int nr)
8812 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8814 // used instead of "leveldir_current->subdir" (for network games)
8815 InitScoreTapeDirectory(levelset.identifier, nr);
8817 SaveTapeExt(filename);
8820 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8821 unsigned int req_state_added)
8823 char *filename = getTapeFilename(nr);
8824 boolean new_tape = !fileExists(filename);
8825 boolean tape_saved = FALSE;
8827 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8832 Request(msg_saved, REQ_CONFIRM | req_state_added);
8840 boolean SaveTapeChecked(int nr)
8842 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8845 boolean SaveTapeChecked_LevelSolved(int nr)
8847 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8848 "Level solved! Tape saved!", REQ_STAY_OPEN);
8851 void DumpTape(struct TapeInfo *tape)
8853 int tape_frame_counter;
8856 if (tape->no_valid_file)
8858 Warn("cannot dump -- no valid tape file found");
8865 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8866 tape->level_nr, tape->file_version, tape->game_version);
8867 Print(" (effective engine version %08d)\n",
8868 tape->engine_version);
8869 Print("Level series identifier: '%s'\n", tape->level_identifier);
8871 Print("Solution tape: %s\n",
8872 tape->solved ? "yes" :
8873 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8875 Print("Special tape properties: ");
8876 if (tape->property_bits == TAPE_PROPERTY_NONE)
8878 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8879 Print("[em_random_bug]");
8880 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8881 Print("[game_speed]");
8882 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8884 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8885 Print("[single_step]");
8886 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8887 Print("[snapshot]");
8888 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8889 Print("[replayed]");
8890 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8891 Print("[tas_keys]");
8892 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8893 Print("[small_graphics]");
8896 int year2 = tape->date / 10000;
8897 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8898 int month_index_raw = (tape->date / 100) % 100;
8899 int month_index = month_index_raw % 12; // prevent invalid index
8900 int month = month_index + 1;
8901 int day = tape->date % 100;
8903 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8907 tape_frame_counter = 0;
8909 for (i = 0; i < tape->length; i++)
8911 if (i >= MAX_TAPE_LEN)
8916 for (j = 0; j < MAX_PLAYERS; j++)
8918 if (tape->player_participates[j])
8920 int action = tape->pos[i].action[j];
8922 Print("%d:%02x ", j, action);
8923 Print("[%c%c%c%c|%c%c] - ",
8924 (action & JOY_LEFT ? '<' : ' '),
8925 (action & JOY_RIGHT ? '>' : ' '),
8926 (action & JOY_UP ? '^' : ' '),
8927 (action & JOY_DOWN ? 'v' : ' '),
8928 (action & JOY_BUTTON_1 ? '1' : ' '),
8929 (action & JOY_BUTTON_2 ? '2' : ' '));
8933 Print("(%03d) ", tape->pos[i].delay);
8934 Print("[%05d]\n", tape_frame_counter);
8936 tape_frame_counter += tape->pos[i].delay;
8942 void DumpTapes(void)
8944 static LevelDirTree *dumptape_leveldir = NULL;
8946 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8947 global.dumptape_leveldir);
8949 if (dumptape_leveldir == NULL)
8950 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8952 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8953 global.dumptape_level_nr > dumptape_leveldir->last_level)
8954 Fail("no such level number: %d", global.dumptape_level_nr);
8956 leveldir_current = dumptape_leveldir;
8958 if (options.mytapes)
8959 LoadTape(global.dumptape_level_nr);
8961 LoadSolutionTape(global.dumptape_level_nr);
8969 // ============================================================================
8970 // score file functions
8971 // ============================================================================
8973 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8977 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8979 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8980 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8981 scores->entry[i].score = 0;
8982 scores->entry[i].time = 0;
8984 scores->entry[i].id = -1;
8985 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8986 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8987 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8988 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8989 strcpy(scores->entry[i].country_code, "??");
8992 scores->num_entries = 0;
8993 scores->last_added = -1;
8994 scores->last_added_local = -1;
8996 scores->updated = FALSE;
8997 scores->uploaded = FALSE;
8998 scores->tape_downloaded = FALSE;
8999 scores->force_last_added = FALSE;
9001 // The following values are intentionally not reset here:
9005 // - continue_playing
9006 // - continue_on_return
9009 static void setScoreInfoToDefaults(void)
9011 setScoreInfoToDefaultsExt(&scores);
9014 static void setServerScoreInfoToDefaults(void)
9016 setScoreInfoToDefaultsExt(&server_scores);
9019 static void LoadScore_OLD(int nr)
9022 char *filename = getScoreFilename(nr);
9023 char cookie[MAX_LINE_LEN];
9024 char line[MAX_LINE_LEN];
9028 if (!(file = fopen(filename, MODE_READ)))
9031 // check file identifier
9032 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9034 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9035 cookie[strlen(cookie) - 1] = '\0';
9037 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9039 Warn("unknown format of score file '%s'", filename);
9046 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9048 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9049 Warn("fscanf() failed; %s", strerror(errno));
9051 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9054 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9055 line[strlen(line) - 1] = '\0';
9057 for (line_ptr = line; *line_ptr; line_ptr++)
9059 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9061 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9062 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9071 static void ConvertScore_OLD(void)
9073 // only convert score to time for levels that rate playing time over score
9074 if (!level.rate_time_over_score)
9077 // convert old score to playing time for score-less levels (like Supaplex)
9078 int time_final_max = 999;
9081 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9083 int score = scores.entry[i].score;
9085 if (score > 0 && score < time_final_max)
9086 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9090 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9092 scores->file_version = getFileVersion(file);
9093 scores->game_version = getFileVersion(file);
9098 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9100 char *level_identifier = NULL;
9101 int level_identifier_size;
9104 level_identifier_size = getFile16BitBE(file);
9106 level_identifier = checked_malloc(level_identifier_size);
9108 for (i = 0; i < level_identifier_size; i++)
9109 level_identifier[i] = getFile8Bit(file);
9111 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9112 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9114 checked_free(level_identifier);
9116 scores->level_nr = getFile16BitBE(file);
9117 scores->num_entries = getFile16BitBE(file);
9119 chunk_size = 2 + level_identifier_size + 2 + 2;
9124 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9128 for (i = 0; i < scores->num_entries; i++)
9130 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9131 scores->entry[i].name[j] = getFile8Bit(file);
9133 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9136 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9141 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9145 for (i = 0; i < scores->num_entries; i++)
9146 scores->entry[i].score = getFile16BitBE(file);
9148 chunk_size = scores->num_entries * 2;
9153 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9157 for (i = 0; i < scores->num_entries; i++)
9158 scores->entry[i].score = getFile32BitBE(file);
9160 chunk_size = scores->num_entries * 4;
9165 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9169 for (i = 0; i < scores->num_entries; i++)
9170 scores->entry[i].time = getFile32BitBE(file);
9172 chunk_size = scores->num_entries * 4;
9177 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9181 for (i = 0; i < scores->num_entries; i++)
9183 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9184 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9186 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9189 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9194 void LoadScore(int nr)
9196 char *filename = getScoreFilename(nr);
9197 char cookie[MAX_LINE_LEN];
9198 char chunk_name[CHUNK_ID_LEN + 1];
9200 boolean old_score_file_format = FALSE;
9203 // always start with reliable default values
9204 setScoreInfoToDefaults();
9206 if (!(file = openFile(filename, MODE_READ)))
9209 getFileChunkBE(file, chunk_name, NULL);
9210 if (strEqual(chunk_name, "RND1"))
9212 getFile32BitBE(file); // not used
9214 getFileChunkBE(file, chunk_name, NULL);
9215 if (!strEqual(chunk_name, "SCOR"))
9217 Warn("unknown format of score file '%s'", filename);
9224 else // check for old file format with cookie string
9226 strcpy(cookie, chunk_name);
9227 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9229 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9230 cookie[strlen(cookie) - 1] = '\0';
9232 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9234 Warn("unknown format of score file '%s'", filename);
9241 old_score_file_format = TRUE;
9244 if (old_score_file_format)
9246 // score files from versions before 4.2.4.0 without chunk structure
9249 // convert score to time, if possible (mainly for Supaplex levels)
9258 int (*loader)(File *, int, struct ScoreInfo *);
9262 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9263 { "INFO", -1, LoadScore_INFO },
9264 { "NAME", -1, LoadScore_NAME },
9265 { "SCOR", -1, LoadScore_SCOR },
9266 { "SC4R", -1, LoadScore_SC4R },
9267 { "TIME", -1, LoadScore_TIME },
9268 { "TAPE", -1, LoadScore_TAPE },
9273 while (getFileChunkBE(file, chunk_name, &chunk_size))
9277 while (chunk_info[i].name != NULL &&
9278 !strEqual(chunk_name, chunk_info[i].name))
9281 if (chunk_info[i].name == NULL)
9283 Warn("unknown chunk '%s' in score file '%s'",
9284 chunk_name, filename);
9286 ReadUnusedBytesFromFile(file, chunk_size);
9288 else if (chunk_info[i].size != -1 &&
9289 chunk_info[i].size != chunk_size)
9291 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9292 chunk_size, chunk_name, filename);
9294 ReadUnusedBytesFromFile(file, chunk_size);
9298 // call function to load this score chunk
9299 int chunk_size_expected =
9300 (chunk_info[i].loader)(file, chunk_size, &scores);
9302 // the size of some chunks cannot be checked before reading other
9303 // chunks first (like "HEAD" and "BODY") that contain some header
9304 // information, so check them here
9305 if (chunk_size_expected != chunk_size)
9307 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9308 chunk_size, chunk_name, filename);
9317 #if ENABLE_HISTORIC_CHUNKS
9318 void SaveScore_OLD(int nr)
9321 char *filename = getScoreFilename(nr);
9324 // used instead of "leveldir_current->subdir" (for network games)
9325 InitScoreDirectory(levelset.identifier);
9327 if (!(file = fopen(filename, MODE_WRITE)))
9329 Warn("cannot save score for level %d", nr);
9334 fprintf(file, "%s\n\n", SCORE_COOKIE);
9336 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9337 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9341 SetFilePermissions(filename, PERMS_PRIVATE);
9345 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9347 putFileVersion(file, scores->file_version);
9348 putFileVersion(file, scores->game_version);
9351 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9353 int level_identifier_size = strlen(scores->level_identifier) + 1;
9356 putFile16BitBE(file, level_identifier_size);
9358 for (i = 0; i < level_identifier_size; i++)
9359 putFile8Bit(file, scores->level_identifier[i]);
9361 putFile16BitBE(file, scores->level_nr);
9362 putFile16BitBE(file, scores->num_entries);
9365 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9369 for (i = 0; i < scores->num_entries; i++)
9371 int name_size = strlen(scores->entry[i].name);
9373 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9374 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9378 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9382 for (i = 0; i < scores->num_entries; i++)
9383 putFile16BitBE(file, scores->entry[i].score);
9386 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9390 for (i = 0; i < scores->num_entries; i++)
9391 putFile32BitBE(file, scores->entry[i].score);
9394 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9398 for (i = 0; i < scores->num_entries; i++)
9399 putFile32BitBE(file, scores->entry[i].time);
9402 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9406 for (i = 0; i < scores->num_entries; i++)
9408 int size = strlen(scores->entry[i].tape_basename);
9410 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9411 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9415 static void SaveScoreToFilename(char *filename)
9418 int info_chunk_size;
9419 int name_chunk_size;
9420 int scor_chunk_size;
9421 int sc4r_chunk_size;
9422 int time_chunk_size;
9423 int tape_chunk_size;
9424 boolean has_large_score_values;
9427 if (!(file = fopen(filename, MODE_WRITE)))
9429 Warn("cannot save score file '%s'", filename);
9434 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9435 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9436 scor_chunk_size = scores.num_entries * 2;
9437 sc4r_chunk_size = scores.num_entries * 4;
9438 time_chunk_size = scores.num_entries * 4;
9439 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9441 has_large_score_values = FALSE;
9442 for (i = 0; i < scores.num_entries; i++)
9443 if (scores.entry[i].score > 0xffff)
9444 has_large_score_values = TRUE;
9446 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9447 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9449 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9450 SaveScore_VERS(file, &scores);
9452 putFileChunkBE(file, "INFO", info_chunk_size);
9453 SaveScore_INFO(file, &scores);
9455 putFileChunkBE(file, "NAME", name_chunk_size);
9456 SaveScore_NAME(file, &scores);
9458 if (has_large_score_values)
9460 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9461 SaveScore_SC4R(file, &scores);
9465 putFileChunkBE(file, "SCOR", scor_chunk_size);
9466 SaveScore_SCOR(file, &scores);
9469 putFileChunkBE(file, "TIME", time_chunk_size);
9470 SaveScore_TIME(file, &scores);
9472 putFileChunkBE(file, "TAPE", tape_chunk_size);
9473 SaveScore_TAPE(file, &scores);
9477 SetFilePermissions(filename, PERMS_PRIVATE);
9480 void SaveScore(int nr)
9482 char *filename = getScoreFilename(nr);
9485 // used instead of "leveldir_current->subdir" (for network games)
9486 InitScoreDirectory(levelset.identifier);
9488 scores.file_version = FILE_VERSION_ACTUAL;
9489 scores.game_version = GAME_VERSION_ACTUAL;
9491 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9492 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9493 scores.level_nr = level_nr;
9495 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9496 if (scores.entry[i].score == 0 &&
9497 scores.entry[i].time == 0 &&
9498 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9501 scores.num_entries = i;
9503 if (scores.num_entries == 0)
9506 SaveScoreToFilename(filename);
9509 static void LoadServerScoreFromCache(int nr)
9511 struct ScoreEntry score_entry;
9520 { &score_entry.score, FALSE, 0 },
9521 { &score_entry.time, FALSE, 0 },
9522 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9523 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9524 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9525 { &score_entry.id, FALSE, 0 },
9526 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9527 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9528 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9529 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9533 char *filename = getScoreCacheFilename(nr);
9534 SetupFileHash *score_hash = loadSetupFileHash(filename);
9537 server_scores.num_entries = 0;
9539 if (score_hash == NULL)
9542 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9544 score_entry = server_scores.entry[i];
9546 for (j = 0; score_mapping[j].value != NULL; j++)
9550 sprintf(token, "%02d.%d", i, j);
9552 char *value = getHashEntry(score_hash, token);
9557 if (score_mapping[j].is_string)
9559 char *score_value = (char *)score_mapping[j].value;
9560 int value_size = score_mapping[j].string_size;
9562 strncpy(score_value, value, value_size);
9563 score_value[value_size] = '\0';
9567 int *score_value = (int *)score_mapping[j].value;
9569 *score_value = atoi(value);
9572 server_scores.num_entries = i + 1;
9575 server_scores.entry[i] = score_entry;
9578 freeSetupFileHash(score_hash);
9581 void LoadServerScore(int nr, boolean download_score)
9583 if (!setup.use_api_server)
9586 // always start with reliable default values
9587 setServerScoreInfoToDefaults();
9589 // 1st step: load server scores from cache file (which may not exist)
9590 // (this should prevent reading it while the thread is writing to it)
9591 LoadServerScoreFromCache(nr);
9593 if (download_score && runtime.use_api_server)
9595 // 2nd step: download server scores from score server to cache file
9596 // (as thread, as it might time out if the server is not reachable)
9597 ApiGetScoreAsThread(nr);
9601 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9603 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9605 // if score tape not uploaded, ask for uploading missing tapes later
9606 if (!setup.has_remaining_tapes)
9607 setup.ask_for_remaining_tapes = TRUE;
9609 setup.provide_uploading_tapes = TRUE;
9610 setup.has_remaining_tapes = TRUE;
9612 SaveSetup_ServerSetup();
9615 void SaveServerScore(int nr, boolean tape_saved)
9617 if (!runtime.use_api_server)
9619 PrepareScoreTapesForUpload(leveldir_current->subdir);
9624 ApiAddScoreAsThread(nr, tape_saved, NULL);
9627 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9628 char *score_tape_filename)
9630 if (!runtime.use_api_server)
9633 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9636 void LoadLocalAndServerScore(int nr, boolean download_score)
9638 int last_added_local = scores.last_added_local;
9639 boolean force_last_added = scores.force_last_added;
9641 // needed if only showing server scores
9642 setScoreInfoToDefaults();
9644 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9647 // restore last added local score entry (before merging server scores)
9648 scores.last_added = scores.last_added_local = last_added_local;
9650 if (setup.use_api_server &&
9651 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9653 // load server scores from cache file and trigger update from server
9654 LoadServerScore(nr, download_score);
9656 // merge local scores with scores from server
9660 if (force_last_added)
9661 scores.force_last_added = force_last_added;
9665 // ============================================================================
9666 // setup file functions
9667 // ============================================================================
9669 #define TOKEN_STR_PLAYER_PREFIX "player_"
9672 static struct TokenInfo global_setup_tokens[] =
9676 &setup.player_name, "player_name"
9680 &setup.multiple_users, "multiple_users"
9684 &setup.sound, "sound"
9688 &setup.sound_loops, "repeating_sound_loops"
9692 &setup.sound_music, "background_music"
9696 &setup.sound_simple, "simple_sound_effects"
9700 &setup.toons, "toons"
9704 &setup.global_animations, "global_animations"
9708 &setup.scroll_delay, "scroll_delay"
9712 &setup.forced_scroll_delay, "forced_scroll_delay"
9716 &setup.scroll_delay_value, "scroll_delay_value"
9720 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9724 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9728 &setup.fade_screens, "fade_screens"
9732 &setup.autorecord, "automatic_tape_recording"
9736 &setup.autorecord_after_replay, "autorecord_after_replay"
9740 &setup.auto_pause_on_start, "auto_pause_on_start"
9744 &setup.show_titlescreen, "show_titlescreen"
9748 &setup.quick_doors, "quick_doors"
9752 &setup.team_mode, "team_mode"
9756 &setup.handicap, "handicap"
9760 &setup.skip_levels, "skip_levels"
9764 &setup.increment_levels, "increment_levels"
9768 &setup.auto_play_next_level, "auto_play_next_level"
9772 &setup.count_score_after_game, "count_score_after_game"
9776 &setup.show_scores_after_game, "show_scores_after_game"
9780 &setup.time_limit, "time_limit"
9784 &setup.fullscreen, "fullscreen"
9788 &setup.window_scaling_percent, "window_scaling_percent"
9792 &setup.window_scaling_quality, "window_scaling_quality"
9796 &setup.screen_rendering_mode, "screen_rendering_mode"
9800 &setup.vsync_mode, "vsync_mode"
9804 &setup.ask_on_escape, "ask_on_escape"
9808 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9812 &setup.ask_on_game_over, "ask_on_game_over"
9816 &setup.ask_on_quit_game, "ask_on_quit_game"
9820 &setup.ask_on_quit_program, "ask_on_quit_program"
9824 &setup.quick_switch, "quick_player_switch"
9828 &setup.input_on_focus, "input_on_focus"
9832 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9836 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9840 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9844 &setup.game_speed_extended, "game_speed_extended"
9848 &setup.game_frame_delay, "game_frame_delay"
9852 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9856 &setup.bd_skip_hatching, "bd_skip_hatching"
9860 &setup.bd_scroll_delay, "bd_scroll_delay"
9864 &setup.bd_smooth_movements, "bd_smooth_movements"
9868 &setup.sp_show_border_elements, "sp_show_border_elements"
9872 &setup.small_game_graphics, "small_game_graphics"
9876 &setup.show_load_save_buttons, "show_load_save_buttons"
9880 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9884 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9888 &setup.graphics_set, "graphics_set"
9892 &setup.sounds_set, "sounds_set"
9896 &setup.music_set, "music_set"
9900 &setup.override_level_graphics, "override_level_graphics"
9904 &setup.override_level_sounds, "override_level_sounds"
9908 &setup.override_level_music, "override_level_music"
9912 &setup.volume_simple, "volume_simple"
9916 &setup.volume_loops, "volume_loops"
9920 &setup.volume_music, "volume_music"
9924 &setup.network_mode, "network_mode"
9928 &setup.network_player_nr, "network_player"
9932 &setup.network_server_hostname, "network_server_hostname"
9936 &setup.touch.control_type, "touch.control_type"
9940 &setup.touch.move_distance, "touch.move_distance"
9944 &setup.touch.drop_distance, "touch.drop_distance"
9948 &setup.touch.transparency, "touch.transparency"
9952 &setup.touch.draw_outlined, "touch.draw_outlined"
9956 &setup.touch.draw_pressed, "touch.draw_pressed"
9960 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9964 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9968 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9972 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9976 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9980 static struct TokenInfo auto_setup_tokens[] =
9984 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9988 static struct TokenInfo server_setup_tokens[] =
9992 &setup.player_uuid, "player_uuid"
9996 &setup.player_version, "player_version"
10000 &setup.use_api_server, TEST_PREFIX "use_api_server"
10004 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10008 &setup.api_server_password, TEST_PREFIX "api_server_password"
10012 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10016 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10020 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10024 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10028 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10032 static struct TokenInfo editor_setup_tokens[] =
10036 &setup.editor.el_classic, "editor.el_classic"
10040 &setup.editor.el_custom, "editor.el_custom"
10044 &setup.editor.el_user_defined, "editor.el_user_defined"
10048 &setup.editor.el_dynamic, "editor.el_dynamic"
10052 &setup.editor.el_headlines, "editor.el_headlines"
10056 &setup.editor.show_element_token, "editor.show_element_token"
10060 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10064 static struct TokenInfo editor_cascade_setup_tokens[] =
10068 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10072 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10076 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10080 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10084 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10088 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10092 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10096 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10100 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10104 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10108 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10112 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10116 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10120 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10124 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10128 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10132 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10136 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10140 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10144 static struct TokenInfo shortcut_setup_tokens[] =
10148 &setup.shortcut.save_game, "shortcut.save_game"
10152 &setup.shortcut.load_game, "shortcut.load_game"
10156 &setup.shortcut.restart_game, "shortcut.restart_game"
10160 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10164 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10168 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10172 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10176 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10180 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10184 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10188 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10192 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10196 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10200 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10204 &setup.shortcut.tape_record, "shortcut.tape_record"
10208 &setup.shortcut.tape_play, "shortcut.tape_play"
10212 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10216 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10220 &setup.shortcut.sound_music, "shortcut.sound_music"
10224 &setup.shortcut.snap_left, "shortcut.snap_left"
10228 &setup.shortcut.snap_right, "shortcut.snap_right"
10232 &setup.shortcut.snap_up, "shortcut.snap_up"
10236 &setup.shortcut.snap_down, "shortcut.snap_down"
10240 static struct SetupInputInfo setup_input;
10241 static struct TokenInfo player_setup_tokens[] =
10245 &setup_input.use_joystick, ".use_joystick"
10249 &setup_input.joy.device_name, ".joy.device_name"
10253 &setup_input.joy.xleft, ".joy.xleft"
10257 &setup_input.joy.xmiddle, ".joy.xmiddle"
10261 &setup_input.joy.xright, ".joy.xright"
10265 &setup_input.joy.yupper, ".joy.yupper"
10269 &setup_input.joy.ymiddle, ".joy.ymiddle"
10273 &setup_input.joy.ylower, ".joy.ylower"
10277 &setup_input.joy.snap, ".joy.snap_field"
10281 &setup_input.joy.drop, ".joy.place_bomb"
10285 &setup_input.key.left, ".key.move_left"
10289 &setup_input.key.right, ".key.move_right"
10293 &setup_input.key.up, ".key.move_up"
10297 &setup_input.key.down, ".key.move_down"
10301 &setup_input.key.snap, ".key.snap_field"
10305 &setup_input.key.drop, ".key.place_bomb"
10309 static struct TokenInfo system_setup_tokens[] =
10313 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10317 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10321 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10325 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10329 static struct TokenInfo internal_setup_tokens[] =
10333 &setup.internal.program_title, "program_title"
10337 &setup.internal.program_version, "program_version"
10341 &setup.internal.program_author, "program_author"
10345 &setup.internal.program_email, "program_email"
10349 &setup.internal.program_website, "program_website"
10353 &setup.internal.program_copyright, "program_copyright"
10357 &setup.internal.program_company, "program_company"
10361 &setup.internal.program_icon_file, "program_icon_file"
10365 &setup.internal.default_graphics_set, "default_graphics_set"
10369 &setup.internal.default_sounds_set, "default_sounds_set"
10373 &setup.internal.default_music_set, "default_music_set"
10377 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10381 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10385 &setup.internal.fallback_music_file, "fallback_music_file"
10389 &setup.internal.default_level_series, "default_level_series"
10393 &setup.internal.default_window_width, "default_window_width"
10397 &setup.internal.default_window_height, "default_window_height"
10401 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10405 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10409 &setup.internal.create_user_levelset, "create_user_levelset"
10413 &setup.internal.info_screens_from_main, "info_screens_from_main"
10417 &setup.internal.menu_game, "menu_game"
10421 &setup.internal.menu_engines, "menu_engines"
10425 &setup.internal.menu_editor, "menu_editor"
10429 &setup.internal.menu_graphics, "menu_graphics"
10433 &setup.internal.menu_sound, "menu_sound"
10437 &setup.internal.menu_artwork, "menu_artwork"
10441 &setup.internal.menu_input, "menu_input"
10445 &setup.internal.menu_touch, "menu_touch"
10449 &setup.internal.menu_shortcuts, "menu_shortcuts"
10453 &setup.internal.menu_exit, "menu_exit"
10457 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10461 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10465 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10469 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10473 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10477 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10481 &setup.internal.info_title, "info_title"
10485 &setup.internal.info_elements, "info_elements"
10489 &setup.internal.info_music, "info_music"
10493 &setup.internal.info_credits, "info_credits"
10497 &setup.internal.info_program, "info_program"
10501 &setup.internal.info_version, "info_version"
10505 &setup.internal.info_levelset, "info_levelset"
10509 &setup.internal.info_exit, "info_exit"
10513 static struct TokenInfo debug_setup_tokens[] =
10517 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10521 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10525 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10529 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10533 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10537 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10541 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10545 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10549 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10553 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10557 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10561 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10565 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10569 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10573 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10577 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10581 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10585 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10589 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10593 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10597 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10600 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10604 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10608 &setup.debug.xsn_mode, "debug.xsn_mode"
10612 &setup.debug.xsn_percent, "debug.xsn_percent"
10616 static struct TokenInfo options_setup_tokens[] =
10620 &setup.options.verbose, "options.verbose"
10624 &setup.options.debug, "options.debug"
10628 &setup.options.debug_mode, "options.debug_mode"
10632 static void setSetupInfoToDefaults(struct SetupInfo *si)
10636 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10638 si->multiple_users = TRUE;
10641 si->sound_loops = TRUE;
10642 si->sound_music = TRUE;
10643 si->sound_simple = TRUE;
10645 si->global_animations = TRUE;
10646 si->scroll_delay = TRUE;
10647 si->forced_scroll_delay = FALSE;
10648 si->scroll_delay_value = STD_SCROLL_DELAY;
10649 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10650 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10651 si->fade_screens = TRUE;
10652 si->autorecord = TRUE;
10653 si->autorecord_after_replay = TRUE;
10654 si->auto_pause_on_start = FALSE;
10655 si->show_titlescreen = TRUE;
10656 si->quick_doors = FALSE;
10657 si->team_mode = FALSE;
10658 si->handicap = TRUE;
10659 si->skip_levels = TRUE;
10660 si->increment_levels = TRUE;
10661 si->auto_play_next_level = TRUE;
10662 si->count_score_after_game = TRUE;
10663 si->show_scores_after_game = TRUE;
10664 si->time_limit = TRUE;
10665 si->fullscreen = FALSE;
10666 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10667 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10668 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10669 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10670 si->ask_on_escape = TRUE;
10671 si->ask_on_escape_editor = TRUE;
10672 si->ask_on_game_over = TRUE;
10673 si->ask_on_quit_game = TRUE;
10674 si->ask_on_quit_program = TRUE;
10675 si->quick_switch = FALSE;
10676 si->input_on_focus = FALSE;
10677 si->prefer_aga_graphics = TRUE;
10678 si->prefer_lowpass_sounds = FALSE;
10679 si->prefer_extra_panel_items = TRUE;
10680 si->game_speed_extended = FALSE;
10681 si->game_frame_delay = GAME_FRAME_DELAY;
10682 si->bd_skip_uncovering = FALSE;
10683 si->bd_skip_hatching = FALSE;
10684 si->bd_scroll_delay = TRUE;
10685 si->bd_smooth_movements = AUTO;
10686 si->sp_show_border_elements = FALSE;
10687 si->small_game_graphics = FALSE;
10688 si->show_load_save_buttons = FALSE;
10689 si->show_undo_redo_buttons = FALSE;
10690 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10692 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10693 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10694 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10696 si->override_level_graphics = FALSE;
10697 si->override_level_sounds = FALSE;
10698 si->override_level_music = FALSE;
10700 si->volume_simple = 100; // percent
10701 si->volume_loops = 100; // percent
10702 si->volume_music = 100; // percent
10704 si->network_mode = FALSE;
10705 si->network_player_nr = 0; // first player
10706 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10708 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10709 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10710 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10711 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10712 si->touch.draw_outlined = TRUE;
10713 si->touch.draw_pressed = TRUE;
10715 for (i = 0; i < 2; i++)
10717 char *default_grid_button[6][2] =
10723 { "111222", " vv " },
10724 { "111222", " vv " }
10726 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10727 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10728 int min_xsize = MIN(6, grid_xsize);
10729 int min_ysize = MIN(6, grid_ysize);
10730 int startx = grid_xsize - min_xsize;
10731 int starty = grid_ysize - min_ysize;
10734 // virtual buttons grid can only be set to defaults if video is initialized
10735 // (this will be repeated if virtual buttons are not loaded from setup file)
10736 if (video.initialized)
10738 si->touch.grid_xsize[i] = grid_xsize;
10739 si->touch.grid_ysize[i] = grid_ysize;
10743 si->touch.grid_xsize[i] = -1;
10744 si->touch.grid_ysize[i] = -1;
10747 for (x = 0; x < MAX_GRID_XSIZE; x++)
10748 for (y = 0; y < MAX_GRID_YSIZE; y++)
10749 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10751 for (x = 0; x < min_xsize; x++)
10752 for (y = 0; y < min_ysize; y++)
10753 si->touch.grid_button[i][x][starty + y] =
10754 default_grid_button[y][0][x];
10756 for (x = 0; x < min_xsize; x++)
10757 for (y = 0; y < min_ysize; y++)
10758 si->touch.grid_button[i][startx + x][starty + y] =
10759 default_grid_button[y][1][x];
10762 si->touch.grid_initialized = video.initialized;
10764 si->touch.overlay_buttons = FALSE;
10766 si->editor.el_boulderdash = TRUE;
10767 si->editor.el_boulderdash_native = TRUE;
10768 si->editor.el_emerald_mine = TRUE;
10769 si->editor.el_emerald_mine_club = TRUE;
10770 si->editor.el_more = TRUE;
10771 si->editor.el_sokoban = TRUE;
10772 si->editor.el_supaplex = TRUE;
10773 si->editor.el_diamond_caves = TRUE;
10774 si->editor.el_dx_boulderdash = TRUE;
10776 si->editor.el_mirror_magic = TRUE;
10777 si->editor.el_deflektor = TRUE;
10779 si->editor.el_chars = TRUE;
10780 si->editor.el_steel_chars = TRUE;
10782 si->editor.el_classic = TRUE;
10783 si->editor.el_custom = TRUE;
10785 si->editor.el_user_defined = FALSE;
10786 si->editor.el_dynamic = TRUE;
10788 si->editor.el_headlines = TRUE;
10790 si->editor.show_element_token = FALSE;
10792 si->editor.show_read_only_warning = TRUE;
10794 si->editor.use_template_for_new_levels = TRUE;
10796 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10797 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10798 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10799 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10800 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10802 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10803 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10804 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10805 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10806 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10808 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10809 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10810 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10811 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10812 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10813 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10815 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10816 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10817 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10819 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10820 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10821 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10822 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10824 for (i = 0; i < MAX_PLAYERS; i++)
10826 si->input[i].use_joystick = FALSE;
10827 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10828 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10829 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10830 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10831 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10832 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10833 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10834 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10835 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10836 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10837 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10838 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10839 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10840 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10841 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10844 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10845 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10846 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10847 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10849 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10850 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10851 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10852 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10853 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10854 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10855 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10857 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10859 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10860 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10861 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10863 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10864 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10865 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10867 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10868 si->internal.choose_from_top_leveldir = FALSE;
10869 si->internal.show_scaling_in_title = TRUE;
10870 si->internal.create_user_levelset = TRUE;
10871 si->internal.info_screens_from_main = FALSE;
10873 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10874 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10876 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10877 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10878 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10879 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10880 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10881 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10882 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10883 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10884 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10885 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10887 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10888 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10889 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10890 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10891 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10892 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10893 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10894 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10895 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10896 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10898 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10899 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10901 si->debug.show_frames_per_second = FALSE;
10903 si->debug.xsn_mode = AUTO;
10904 si->debug.xsn_percent = 0;
10906 si->options.verbose = FALSE;
10907 si->options.debug = FALSE;
10908 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10910 #if defined(PLATFORM_ANDROID)
10911 si->fullscreen = TRUE;
10912 si->touch.overlay_buttons = TRUE;
10915 setHideSetupEntry(&setup.debug.xsn_mode);
10918 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10920 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10923 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10925 si->player_uuid = NULL; // (will be set later)
10926 si->player_version = 1; // (will be set later)
10928 si->use_api_server = TRUE;
10929 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10930 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10931 si->ask_for_uploading_tapes = TRUE;
10932 si->ask_for_remaining_tapes = FALSE;
10933 si->provide_uploading_tapes = TRUE;
10934 si->ask_for_using_api_server = TRUE;
10935 si->has_remaining_tapes = FALSE;
10938 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10940 si->editor_cascade.el_bd = TRUE;
10941 si->editor_cascade.el_bd_native = TRUE;
10942 si->editor_cascade.el_em = TRUE;
10943 si->editor_cascade.el_emc = TRUE;
10944 si->editor_cascade.el_rnd = TRUE;
10945 si->editor_cascade.el_sb = TRUE;
10946 si->editor_cascade.el_sp = TRUE;
10947 si->editor_cascade.el_dc = TRUE;
10948 si->editor_cascade.el_dx = TRUE;
10950 si->editor_cascade.el_mm = TRUE;
10951 si->editor_cascade.el_df = TRUE;
10953 si->editor_cascade.el_chars = FALSE;
10954 si->editor_cascade.el_steel_chars = FALSE;
10955 si->editor_cascade.el_ce = FALSE;
10956 si->editor_cascade.el_ge = FALSE;
10957 si->editor_cascade.el_es = FALSE;
10958 si->editor_cascade.el_ref = FALSE;
10959 si->editor_cascade.el_user = FALSE;
10960 si->editor_cascade.el_dynamic = FALSE;
10963 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10965 static char *getHideSetupToken(void *setup_value)
10967 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10969 if (setup_value != NULL)
10970 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10972 return hide_setup_token;
10975 void setHideSetupEntry(void *setup_value)
10977 char *hide_setup_token = getHideSetupToken(setup_value);
10979 if (hide_setup_hash == NULL)
10980 hide_setup_hash = newSetupFileHash();
10982 if (setup_value != NULL)
10983 setHashEntry(hide_setup_hash, hide_setup_token, "");
10986 void removeHideSetupEntry(void *setup_value)
10988 char *hide_setup_token = getHideSetupToken(setup_value);
10990 if (setup_value != NULL)
10991 removeHashEntry(hide_setup_hash, hide_setup_token);
10994 boolean hideSetupEntry(void *setup_value)
10996 char *hide_setup_token = getHideSetupToken(setup_value);
10998 return (setup_value != NULL &&
10999 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11002 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11003 struct TokenInfo *token_info,
11004 int token_nr, char *token_text)
11006 char *token_hide_text = getStringCat2(token_text, ".hide");
11007 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11009 // set the value of this setup option in the setup option structure
11010 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11012 // check if this setup option should be hidden in the setup menu
11013 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11014 setHideSetupEntry(token_info[token_nr].value);
11016 free(token_hide_text);
11019 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11020 struct TokenInfo *token_info,
11023 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11024 token_info[token_nr].text);
11027 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11031 if (!setup_file_hash)
11034 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11035 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11037 setup.touch.grid_initialized = TRUE;
11038 for (i = 0; i < 2; i++)
11040 int grid_xsize = setup.touch.grid_xsize[i];
11041 int grid_ysize = setup.touch.grid_ysize[i];
11044 // if virtual buttons are not loaded from setup file, repeat initializing
11045 // virtual buttons grid with default values later when video is initialized
11046 if (grid_xsize == -1 ||
11049 setup.touch.grid_initialized = FALSE;
11054 for (y = 0; y < grid_ysize; y++)
11056 char token_string[MAX_LINE_LEN];
11058 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11060 char *value_string = getHashEntry(setup_file_hash, token_string);
11062 if (value_string == NULL)
11065 for (x = 0; x < grid_xsize; x++)
11067 char c = value_string[x];
11069 setup.touch.grid_button[i][x][y] =
11070 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11075 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11076 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11078 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11079 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11081 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11085 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11087 setup_input = setup.input[pnr];
11088 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11090 char full_token[100];
11092 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11093 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11096 setup.input[pnr] = setup_input;
11099 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11100 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11102 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11103 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11105 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11106 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11108 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11109 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11111 setHideRelatedSetupEntries();
11114 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11118 if (!setup_file_hash)
11121 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11122 setSetupInfo(auto_setup_tokens, i,
11123 getHashEntry(setup_file_hash,
11124 auto_setup_tokens[i].text));
11127 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11131 if (!setup_file_hash)
11134 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11135 setSetupInfo(server_setup_tokens, i,
11136 getHashEntry(setup_file_hash,
11137 server_setup_tokens[i].text));
11140 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11144 if (!setup_file_hash)
11147 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11148 setSetupInfo(editor_cascade_setup_tokens, i,
11149 getHashEntry(setup_file_hash,
11150 editor_cascade_setup_tokens[i].text));
11153 void LoadUserNames(void)
11155 int last_user_nr = user.nr;
11158 if (global.user_names != NULL)
11160 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11161 checked_free(global.user_names[i]);
11163 checked_free(global.user_names);
11166 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11168 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11172 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11174 if (setup_file_hash)
11176 char *player_name = getHashEntry(setup_file_hash, "player_name");
11178 global.user_names[i] = getFixedUserName(player_name);
11180 freeSetupFileHash(setup_file_hash);
11183 if (global.user_names[i] == NULL)
11184 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11187 user.nr = last_user_nr;
11190 void LoadSetupFromFilename(char *filename)
11192 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11194 if (setup_file_hash)
11196 decodeSetupFileHash_Default(setup_file_hash);
11198 freeSetupFileHash(setup_file_hash);
11202 Debug("setup", "using default setup values");
11206 static void LoadSetup_SpecialPostProcessing(void)
11208 char *player_name_new;
11210 // needed to work around problems with fixed length strings
11211 player_name_new = getFixedUserName(setup.player_name);
11212 free(setup.player_name);
11213 setup.player_name = player_name_new;
11215 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11216 if (setup.scroll_delay == FALSE)
11218 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11219 setup.scroll_delay = TRUE; // now always "on"
11222 // make sure that scroll delay value stays inside valid range
11223 setup.scroll_delay_value =
11224 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11227 void LoadSetup_Default(void)
11231 // always start with reliable default values
11232 setSetupInfoToDefaults(&setup);
11234 // try to load setup values from default setup file
11235 filename = getDefaultSetupFilename();
11237 if (fileExists(filename))
11238 LoadSetupFromFilename(filename);
11240 // try to load setup values from platform setup file
11241 filename = getPlatformSetupFilename();
11243 if (fileExists(filename))
11244 LoadSetupFromFilename(filename);
11246 // try to load setup values from user setup file
11247 filename = getSetupFilename();
11249 LoadSetupFromFilename(filename);
11251 LoadSetup_SpecialPostProcessing();
11254 void LoadSetup_AutoSetup(void)
11256 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11257 SetupFileHash *setup_file_hash = NULL;
11259 // always start with reliable default values
11260 setSetupInfoToDefaults_AutoSetup(&setup);
11262 setup_file_hash = loadSetupFileHash(filename);
11264 if (setup_file_hash)
11266 decodeSetupFileHash_AutoSetup(setup_file_hash);
11268 freeSetupFileHash(setup_file_hash);
11274 void LoadSetup_ServerSetup(void)
11276 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11277 SetupFileHash *setup_file_hash = NULL;
11279 // always start with reliable default values
11280 setSetupInfoToDefaults_ServerSetup(&setup);
11282 setup_file_hash = loadSetupFileHash(filename);
11284 if (setup_file_hash)
11286 decodeSetupFileHash_ServerSetup(setup_file_hash);
11288 freeSetupFileHash(setup_file_hash);
11293 if (setup.player_uuid == NULL)
11295 // player UUID does not yet exist in setup file
11296 setup.player_uuid = getStringCopy(getUUID());
11297 setup.player_version = 2;
11299 SaveSetup_ServerSetup();
11303 void LoadSetup_EditorCascade(void)
11305 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11306 SetupFileHash *setup_file_hash = NULL;
11308 // always start with reliable default values
11309 setSetupInfoToDefaults_EditorCascade(&setup);
11311 setup_file_hash = loadSetupFileHash(filename);
11313 if (setup_file_hash)
11315 decodeSetupFileHash_EditorCascade(setup_file_hash);
11317 freeSetupFileHash(setup_file_hash);
11323 void LoadSetup(void)
11325 LoadSetup_Default();
11326 LoadSetup_AutoSetup();
11327 LoadSetup_ServerSetup();
11328 LoadSetup_EditorCascade();
11331 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11332 char *mapping_line)
11334 char mapping_guid[MAX_LINE_LEN];
11335 char *mapping_start, *mapping_end;
11337 // get GUID from game controller mapping line: copy complete line
11338 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11339 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11341 // get GUID from game controller mapping line: cut after GUID part
11342 mapping_start = strchr(mapping_guid, ',');
11343 if (mapping_start != NULL)
11344 *mapping_start = '\0';
11346 // cut newline from game controller mapping line
11347 mapping_end = strchr(mapping_line, '\n');
11348 if (mapping_end != NULL)
11349 *mapping_end = '\0';
11351 // add mapping entry to game controller mappings hash
11352 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11355 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11360 if (!(file = fopen(filename, MODE_READ)))
11362 Warn("cannot read game controller mappings file '%s'", filename);
11367 while (!feof(file))
11369 char line[MAX_LINE_LEN];
11371 if (!fgets(line, MAX_LINE_LEN, file))
11374 addGameControllerMappingToHash(mappings_hash, line);
11380 void SaveSetup_Default(void)
11382 char *filename = getSetupFilename();
11386 InitUserDataDirectory();
11388 if (!(file = fopen(filename, MODE_WRITE)))
11390 Warn("cannot write setup file '%s'", filename);
11395 fprintFileHeader(file, SETUP_FILENAME);
11397 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11399 // just to make things nicer :)
11400 if (global_setup_tokens[i].value == &setup.multiple_users ||
11401 global_setup_tokens[i].value == &setup.sound ||
11402 global_setup_tokens[i].value == &setup.graphics_set ||
11403 global_setup_tokens[i].value == &setup.volume_simple ||
11404 global_setup_tokens[i].value == &setup.network_mode ||
11405 global_setup_tokens[i].value == &setup.touch.control_type ||
11406 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11407 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11408 fprintf(file, "\n");
11410 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11413 for (i = 0; i < 2; i++)
11415 int grid_xsize = setup.touch.grid_xsize[i];
11416 int grid_ysize = setup.touch.grid_ysize[i];
11419 fprintf(file, "\n");
11421 for (y = 0; y < grid_ysize; y++)
11423 char token_string[MAX_LINE_LEN];
11424 char value_string[MAX_LINE_LEN];
11426 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11428 for (x = 0; x < grid_xsize; x++)
11430 char c = setup.touch.grid_button[i][x][y];
11432 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11435 value_string[grid_xsize] = '\0';
11437 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11441 fprintf(file, "\n");
11442 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11443 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11445 fprintf(file, "\n");
11446 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11447 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11449 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11453 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11454 fprintf(file, "\n");
11456 setup_input = setup.input[pnr];
11457 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11458 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11461 fprintf(file, "\n");
11462 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11463 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11465 // (internal setup values not saved to user setup file)
11467 fprintf(file, "\n");
11468 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11469 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11470 setup.debug.xsn_mode != AUTO)
11471 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11473 fprintf(file, "\n");
11474 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11475 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11479 SetFilePermissions(filename, PERMS_PRIVATE);
11482 void SaveSetup_AutoSetup(void)
11484 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11488 InitUserDataDirectory();
11490 if (!(file = fopen(filename, MODE_WRITE)))
11492 Warn("cannot write auto setup file '%s'", filename);
11499 fprintFileHeader(file, AUTOSETUP_FILENAME);
11501 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11502 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11506 SetFilePermissions(filename, PERMS_PRIVATE);
11511 void SaveSetup_ServerSetup(void)
11513 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11517 InitUserDataDirectory();
11519 if (!(file = fopen(filename, MODE_WRITE)))
11521 Warn("cannot write server setup file '%s'", filename);
11528 fprintFileHeader(file, SERVERSETUP_FILENAME);
11530 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11532 // just to make things nicer :)
11533 if (server_setup_tokens[i].value == &setup.use_api_server)
11534 fprintf(file, "\n");
11536 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11541 SetFilePermissions(filename, PERMS_PRIVATE);
11546 void SaveSetup_EditorCascade(void)
11548 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11552 InitUserDataDirectory();
11554 if (!(file = fopen(filename, MODE_WRITE)))
11556 Warn("cannot write editor cascade state file '%s'", filename);
11563 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11565 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11566 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11570 SetFilePermissions(filename, PERMS_PRIVATE);
11575 void SaveSetup(void)
11577 SaveSetup_Default();
11578 SaveSetup_AutoSetup();
11579 SaveSetup_ServerSetup();
11580 SaveSetup_EditorCascade();
11583 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11588 if (!(file = fopen(filename, MODE_WRITE)))
11590 Warn("cannot write game controller mappings file '%s'", filename);
11595 BEGIN_HASH_ITERATION(mappings_hash, itr)
11597 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11599 END_HASH_ITERATION(mappings_hash, itr)
11604 void SaveSetup_AddGameControllerMapping(char *mapping)
11606 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11607 SetupFileHash *mappings_hash = newSetupFileHash();
11609 InitUserDataDirectory();
11611 // load existing personal game controller mappings
11612 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11614 // add new mapping to personal game controller mappings
11615 addGameControllerMappingToHash(mappings_hash, mapping);
11617 // save updated personal game controller mappings
11618 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11620 freeSetupFileHash(mappings_hash);
11624 void LoadCustomElementDescriptions(void)
11626 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11627 SetupFileHash *setup_file_hash;
11630 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11632 if (element_info[i].custom_description != NULL)
11634 free(element_info[i].custom_description);
11635 element_info[i].custom_description = NULL;
11639 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11642 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11644 char *token = getStringCat2(element_info[i].token_name, ".name");
11645 char *value = getHashEntry(setup_file_hash, token);
11648 element_info[i].custom_description = getStringCopy(value);
11653 freeSetupFileHash(setup_file_hash);
11656 static int getElementFromToken(char *token)
11658 char *value = getHashEntry(element_token_hash, token);
11661 return atoi(value);
11663 Warn("unknown element token '%s'", token);
11665 return EL_UNDEFINED;
11668 void FreeGlobalAnimEventInfo(void)
11670 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11672 if (gaei->event_list == NULL)
11677 for (i = 0; i < gaei->num_event_lists; i++)
11679 checked_free(gaei->event_list[i]->event_value);
11680 checked_free(gaei->event_list[i]);
11683 checked_free(gaei->event_list);
11685 gaei->event_list = NULL;
11686 gaei->num_event_lists = 0;
11689 static int AddGlobalAnimEventList(void)
11691 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11692 int list_pos = gaei->num_event_lists++;
11694 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11695 sizeof(struct GlobalAnimEventListInfo *));
11697 gaei->event_list[list_pos] =
11698 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11700 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11702 gaeli->event_value = NULL;
11703 gaeli->num_event_values = 0;
11708 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11710 // do not add empty global animation events
11711 if (event_value == ANIM_EVENT_NONE)
11714 // if list position is undefined, create new list
11715 if (list_pos == ANIM_EVENT_UNDEFINED)
11716 list_pos = AddGlobalAnimEventList();
11718 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11719 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11720 int value_pos = gaeli->num_event_values++;
11722 gaeli->event_value = checked_realloc(gaeli->event_value,
11723 gaeli->num_event_values * sizeof(int *));
11725 gaeli->event_value[value_pos] = event_value;
11730 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11732 if (list_pos == ANIM_EVENT_UNDEFINED)
11733 return ANIM_EVENT_NONE;
11735 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11736 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11738 return gaeli->event_value[value_pos];
11741 int GetGlobalAnimEventValueCount(int list_pos)
11743 if (list_pos == ANIM_EVENT_UNDEFINED)
11746 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11747 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11749 return gaeli->num_event_values;
11752 // This function checks if a string <s> of the format "string1, string2, ..."
11753 // exactly contains a string <s_contained>.
11755 static boolean string_has_parameter(char *s, char *s_contained)
11759 if (s == NULL || s_contained == NULL)
11762 if (strlen(s_contained) > strlen(s))
11765 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11767 char next_char = s[strlen(s_contained)];
11769 // check if next character is delimiter or whitespace
11770 if (next_char == ',' || next_char == '\0' ||
11771 next_char == ' ' || next_char == '\t')
11775 // check if string contains another parameter string after a comma
11776 substring = strchr(s, ',');
11777 if (substring == NULL) // string does not contain a comma
11780 // advance string pointer to next character after the comma
11783 // skip potential whitespaces after the comma
11784 while (*substring == ' ' || *substring == '\t')
11787 return string_has_parameter(substring, s_contained);
11790 static int get_anim_parameter_value_ce(char *s)
11793 char *pattern_1 = "ce_change:custom_";
11794 char *pattern_2 = ".page_";
11795 int pattern_1_len = strlen(pattern_1);
11796 char *matching_char = strstr(s_ptr, pattern_1);
11797 int result = ANIM_EVENT_NONE;
11799 if (matching_char == NULL)
11800 return ANIM_EVENT_NONE;
11802 result = ANIM_EVENT_CE_CHANGE;
11804 s_ptr = matching_char + pattern_1_len;
11806 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11807 if (*s_ptr >= '0' && *s_ptr <= '9')
11809 int gic_ce_nr = (*s_ptr++ - '0');
11811 if (*s_ptr >= '0' && *s_ptr <= '9')
11813 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11815 if (*s_ptr >= '0' && *s_ptr <= '9')
11816 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11819 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11820 return ANIM_EVENT_NONE;
11822 // custom element stored as 0 to 255
11825 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11829 // invalid custom element number specified
11831 return ANIM_EVENT_NONE;
11834 // check for change page number ("page_X" or "page_XX") (optional)
11835 if (strPrefix(s_ptr, pattern_2))
11837 s_ptr += strlen(pattern_2);
11839 if (*s_ptr >= '0' && *s_ptr <= '9')
11841 int gic_page_nr = (*s_ptr++ - '0');
11843 if (*s_ptr >= '0' && *s_ptr <= '9')
11844 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11846 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11847 return ANIM_EVENT_NONE;
11849 // change page stored as 1 to 32 (0 means "all change pages")
11851 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11855 // invalid animation part number specified
11857 return ANIM_EVENT_NONE;
11861 // discard result if next character is neither delimiter nor whitespace
11862 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11863 *s_ptr == ' ' || *s_ptr == '\t'))
11864 return ANIM_EVENT_NONE;
11869 static int get_anim_parameter_value(char *s)
11871 int event_value[] =
11879 char *pattern_1[] =
11887 char *pattern_2 = ".part_";
11888 char *matching_char = NULL;
11890 int pattern_1_len = 0;
11891 int result = ANIM_EVENT_NONE;
11894 result = get_anim_parameter_value_ce(s);
11896 if (result != ANIM_EVENT_NONE)
11899 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11901 matching_char = strstr(s_ptr, pattern_1[i]);
11902 pattern_1_len = strlen(pattern_1[i]);
11903 result = event_value[i];
11905 if (matching_char != NULL)
11909 if (matching_char == NULL)
11910 return ANIM_EVENT_NONE;
11912 s_ptr = matching_char + pattern_1_len;
11914 // check for main animation number ("anim_X" or "anim_XX")
11915 if (*s_ptr >= '0' && *s_ptr <= '9')
11917 int gic_anim_nr = (*s_ptr++ - '0');
11919 if (*s_ptr >= '0' && *s_ptr <= '9')
11920 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11922 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11923 return ANIM_EVENT_NONE;
11925 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11929 // invalid main animation number specified
11931 return ANIM_EVENT_NONE;
11934 // check for animation part number ("part_X" or "part_XX") (optional)
11935 if (strPrefix(s_ptr, pattern_2))
11937 s_ptr += strlen(pattern_2);
11939 if (*s_ptr >= '0' && *s_ptr <= '9')
11941 int gic_part_nr = (*s_ptr++ - '0');
11943 if (*s_ptr >= '0' && *s_ptr <= '9')
11944 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11946 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11947 return ANIM_EVENT_NONE;
11949 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11953 // invalid animation part number specified
11955 return ANIM_EVENT_NONE;
11959 // discard result if next character is neither delimiter nor whitespace
11960 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11961 *s_ptr == ' ' || *s_ptr == '\t'))
11962 return ANIM_EVENT_NONE;
11967 static int get_anim_parameter_values(char *s)
11969 int list_pos = ANIM_EVENT_UNDEFINED;
11970 int event_value = ANIM_EVENT_DEFAULT;
11972 if (string_has_parameter(s, "any"))
11973 event_value |= ANIM_EVENT_ANY;
11975 if (string_has_parameter(s, "click:self") ||
11976 string_has_parameter(s, "click") ||
11977 string_has_parameter(s, "self"))
11978 event_value |= ANIM_EVENT_SELF;
11980 if (string_has_parameter(s, "unclick:any"))
11981 event_value |= ANIM_EVENT_UNCLICK_ANY;
11983 // if animation event found, add it to global animation event list
11984 if (event_value != ANIM_EVENT_NONE)
11985 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11989 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11990 event_value = get_anim_parameter_value(s);
11992 // if animation event found, add it to global animation event list
11993 if (event_value != ANIM_EVENT_NONE)
11994 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11996 // continue with next part of the string, starting with next comma
11997 s = strchr(s + 1, ',');
12003 static int get_anim_action_parameter_value(char *token)
12005 // check most common default case first to massively speed things up
12006 if (strEqual(token, ARG_UNDEFINED))
12007 return ANIM_EVENT_ACTION_NONE;
12009 int result = getImageIDFromToken(token);
12013 char *gfx_token = getStringCat2("gfx.", token);
12015 result = getImageIDFromToken(gfx_token);
12017 checked_free(gfx_token);
12022 Key key = getKeyFromX11KeyName(token);
12024 if (key != KSYM_UNDEFINED)
12025 result = -(int)key;
12032 result = get_hash_from_string(token); // unsigned int => int
12033 result = ABS(result); // may be negative now
12034 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12036 setHashEntry(anim_url_hash, int2str(result, 0), token);
12041 result = ANIM_EVENT_ACTION_NONE;
12046 int get_parameter_value(char *value_raw, char *suffix, int type)
12048 char *value = getStringToLower(value_raw);
12049 int result = 0; // probably a save default value
12051 if (strEqual(suffix, ".direction"))
12053 result = (strEqual(value, "left") ? MV_LEFT :
12054 strEqual(value, "right") ? MV_RIGHT :
12055 strEqual(value, "up") ? MV_UP :
12056 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12058 else if (strEqual(suffix, ".position"))
12060 result = (strEqual(value, "left") ? POS_LEFT :
12061 strEqual(value, "right") ? POS_RIGHT :
12062 strEqual(value, "top") ? POS_TOP :
12063 strEqual(value, "upper") ? POS_UPPER :
12064 strEqual(value, "middle") ? POS_MIDDLE :
12065 strEqual(value, "lower") ? POS_LOWER :
12066 strEqual(value, "bottom") ? POS_BOTTOM :
12067 strEqual(value, "any") ? POS_ANY :
12068 strEqual(value, "ce") ? POS_CE :
12069 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12070 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12072 else if (strEqual(suffix, ".align"))
12074 result = (strEqual(value, "left") ? ALIGN_LEFT :
12075 strEqual(value, "right") ? ALIGN_RIGHT :
12076 strEqual(value, "center") ? ALIGN_CENTER :
12077 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12079 else if (strEqual(suffix, ".valign"))
12081 result = (strEqual(value, "top") ? VALIGN_TOP :
12082 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12083 strEqual(value, "middle") ? VALIGN_MIDDLE :
12084 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12086 else if (strEqual(suffix, ".anim_mode"))
12088 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12089 string_has_parameter(value, "loop") ? ANIM_LOOP :
12090 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12091 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12092 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12093 string_has_parameter(value, "random") ? ANIM_RANDOM :
12094 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12095 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12096 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12097 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12098 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12099 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12100 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12101 string_has_parameter(value, "all") ? ANIM_ALL :
12102 string_has_parameter(value, "tiled") ? ANIM_TILED :
12103 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12106 if (string_has_parameter(value, "once"))
12107 result |= ANIM_ONCE;
12109 if (string_has_parameter(value, "reverse"))
12110 result |= ANIM_REVERSE;
12112 if (string_has_parameter(value, "opaque_player"))
12113 result |= ANIM_OPAQUE_PLAYER;
12115 if (string_has_parameter(value, "static_panel"))
12116 result |= ANIM_STATIC_PANEL;
12118 else if (strEqual(suffix, ".init_event") ||
12119 strEqual(suffix, ".anim_event"))
12121 result = get_anim_parameter_values(value);
12123 else if (strEqual(suffix, ".init_delay_action") ||
12124 strEqual(suffix, ".anim_delay_action") ||
12125 strEqual(suffix, ".post_delay_action") ||
12126 strEqual(suffix, ".init_event_action") ||
12127 strEqual(suffix, ".anim_event_action"))
12129 result = get_anim_action_parameter_value(value_raw);
12131 else if (strEqual(suffix, ".class"))
12133 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12134 get_hash_from_string(value));
12136 else if (strEqual(suffix, ".style"))
12138 result = STYLE_DEFAULT;
12140 if (string_has_parameter(value, "accurate_borders"))
12141 result |= STYLE_ACCURATE_BORDERS;
12143 if (string_has_parameter(value, "inner_corners"))
12144 result |= STYLE_INNER_CORNERS;
12146 if (string_has_parameter(value, "reverse"))
12147 result |= STYLE_REVERSE;
12149 if (string_has_parameter(value, "leftmost_position"))
12150 result |= STYLE_LEFTMOST_POSITION;
12152 if (string_has_parameter(value, "block_clicks"))
12153 result |= STYLE_BLOCK;
12155 if (string_has_parameter(value, "passthrough_clicks"))
12156 result |= STYLE_PASSTHROUGH;
12158 if (string_has_parameter(value, "multiple_actions"))
12159 result |= STYLE_MULTIPLE_ACTIONS;
12161 if (string_has_parameter(value, "consume_ce_event"))
12162 result |= STYLE_CONSUME_CE_EVENT;
12164 else if (strEqual(suffix, ".fade_mode"))
12166 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12167 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12168 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12169 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12170 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12171 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12172 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12173 FADE_MODE_DEFAULT);
12175 else if (strEqual(suffix, ".auto_delay_unit"))
12177 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12178 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12179 AUTO_DELAY_UNIT_DEFAULT);
12181 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12183 result = gfx.get_font_from_token_function(value);
12185 else // generic parameter of type integer or boolean
12187 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12188 type == TYPE_INTEGER ? get_integer_from_string(value) :
12189 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12190 ARG_UNDEFINED_VALUE);
12198 static int get_token_parameter_value(char *token, char *value_raw)
12202 if (token == NULL || value_raw == NULL)
12203 return ARG_UNDEFINED_VALUE;
12205 suffix = strrchr(token, '.');
12206 if (suffix == NULL)
12209 if (strEqual(suffix, ".element"))
12210 return getElementFromToken(value_raw);
12212 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12213 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12216 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12217 boolean ignore_defaults)
12221 for (i = 0; image_config_vars[i].token != NULL; i++)
12223 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12225 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12226 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12230 *image_config_vars[i].value =
12231 get_token_parameter_value(image_config_vars[i].token, value);
12235 void InitMenuDesignSettings_Static(void)
12237 // always start with reliable default values from static default config
12238 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12241 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12245 // the following initializes hierarchical values from static configuration
12247 // special case: initialize "ARG_DEFAULT" values in static default config
12248 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12249 titlescreen_initial_first_default.fade_mode =
12250 title_initial_first_default.fade_mode;
12251 titlescreen_initial_first_default.fade_delay =
12252 title_initial_first_default.fade_delay;
12253 titlescreen_initial_first_default.post_delay =
12254 title_initial_first_default.post_delay;
12255 titlescreen_initial_first_default.auto_delay =
12256 title_initial_first_default.auto_delay;
12257 titlescreen_initial_first_default.auto_delay_unit =
12258 title_initial_first_default.auto_delay_unit;
12259 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12260 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12261 titlescreen_first_default.post_delay = title_first_default.post_delay;
12262 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12263 titlescreen_first_default.auto_delay_unit =
12264 title_first_default.auto_delay_unit;
12265 titlemessage_initial_first_default.fade_mode =
12266 title_initial_first_default.fade_mode;
12267 titlemessage_initial_first_default.fade_delay =
12268 title_initial_first_default.fade_delay;
12269 titlemessage_initial_first_default.post_delay =
12270 title_initial_first_default.post_delay;
12271 titlemessage_initial_first_default.auto_delay =
12272 title_initial_first_default.auto_delay;
12273 titlemessage_initial_first_default.auto_delay_unit =
12274 title_initial_first_default.auto_delay_unit;
12275 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12276 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12277 titlemessage_first_default.post_delay = title_first_default.post_delay;
12278 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12279 titlemessage_first_default.auto_delay_unit =
12280 title_first_default.auto_delay_unit;
12282 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12283 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12284 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12285 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12286 titlescreen_initial_default.auto_delay_unit =
12287 title_initial_default.auto_delay_unit;
12288 titlescreen_default.fade_mode = title_default.fade_mode;
12289 titlescreen_default.fade_delay = title_default.fade_delay;
12290 titlescreen_default.post_delay = title_default.post_delay;
12291 titlescreen_default.auto_delay = title_default.auto_delay;
12292 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12293 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12294 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12295 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12296 titlemessage_initial_default.auto_delay_unit =
12297 title_initial_default.auto_delay_unit;
12298 titlemessage_default.fade_mode = title_default.fade_mode;
12299 titlemessage_default.fade_delay = title_default.fade_delay;
12300 titlemessage_default.post_delay = title_default.post_delay;
12301 titlemessage_default.auto_delay = title_default.auto_delay;
12302 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12304 // special case: initialize "ARG_DEFAULT" values in static default config
12305 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12306 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12308 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12309 titlescreen_first[i] = titlescreen_first_default;
12310 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12311 titlemessage_first[i] = titlemessage_first_default;
12313 titlescreen_initial[i] = titlescreen_initial_default;
12314 titlescreen[i] = titlescreen_default;
12315 titlemessage_initial[i] = titlemessage_initial_default;
12316 titlemessage[i] = titlemessage_default;
12319 // special case: initialize "ARG_DEFAULT" values in static default config
12320 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12321 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12323 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12326 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12327 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12328 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12331 // special case: initialize "ARG_DEFAULT" values in static default config
12332 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12333 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12335 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12336 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12337 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12339 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12342 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12346 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12350 struct XY *dst, *src;
12352 game_buttons_xy[] =
12354 { &game.button.save, &game.button.stop },
12355 { &game.button.pause2, &game.button.pause },
12356 { &game.button.load, &game.button.play },
12357 { &game.button.undo, &game.button.stop },
12358 { &game.button.redo, &game.button.play },
12364 // special case: initialize later added SETUP list size from LEVELS value
12365 if (menu.list_size[GAME_MODE_SETUP] == -1)
12366 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12368 // set default position for snapshot buttons to stop/pause/play buttons
12369 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12370 if ((*game_buttons_xy[i].dst).x == -1 &&
12371 (*game_buttons_xy[i].dst).y == -1)
12372 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12374 // --------------------------------------------------------------------------
12375 // dynamic viewports (including playfield margins, borders and alignments)
12376 // --------------------------------------------------------------------------
12378 // dynamic viewports currently only supported for landscape mode
12379 int display_width = MAX(video.display_width, video.display_height);
12380 int display_height = MIN(video.display_width, video.display_height);
12382 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12384 struct RectWithBorder *vp_window = &viewport.window[i];
12385 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12386 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12387 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12388 boolean dynamic_window_width = (vp_window->min_width != -1);
12389 boolean dynamic_window_height = (vp_window->min_height != -1);
12390 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12391 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12393 // adjust window size if min/max width/height is specified
12395 if (vp_window->min_width != -1)
12397 int window_width = display_width;
12399 // when using static window height, use aspect ratio of display
12400 if (vp_window->min_height == -1)
12401 window_width = vp_window->height * display_width / display_height;
12403 vp_window->width = MAX(vp_window->min_width, window_width);
12406 if (vp_window->min_height != -1)
12408 int window_height = display_height;
12410 // when using static window width, use aspect ratio of display
12411 if (vp_window->min_width == -1)
12412 window_height = vp_window->width * display_height / display_width;
12414 vp_window->height = MAX(vp_window->min_height, window_height);
12417 if (vp_window->max_width != -1)
12418 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12420 if (vp_window->max_height != -1)
12421 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12423 int playfield_width = vp_window->width;
12424 int playfield_height = vp_window->height;
12426 // adjust playfield size and position according to specified margins
12428 playfield_width -= vp_playfield->margin_left;
12429 playfield_width -= vp_playfield->margin_right;
12431 playfield_height -= vp_playfield->margin_top;
12432 playfield_height -= vp_playfield->margin_bottom;
12434 // adjust playfield size if min/max width/height is specified
12436 if (vp_playfield->min_width != -1)
12437 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12439 if (vp_playfield->min_height != -1)
12440 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12442 if (vp_playfield->max_width != -1)
12443 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12445 if (vp_playfield->max_height != -1)
12446 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12448 // adjust playfield position according to specified alignment
12450 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12451 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12452 else if (vp_playfield->align == ALIGN_CENTER)
12453 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12454 else if (vp_playfield->align == ALIGN_RIGHT)
12455 vp_playfield->x += playfield_width - vp_playfield->width;
12457 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12458 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12459 else if (vp_playfield->valign == VALIGN_MIDDLE)
12460 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12461 else if (vp_playfield->valign == VALIGN_BOTTOM)
12462 vp_playfield->y += playfield_height - vp_playfield->height;
12464 vp_playfield->x += vp_playfield->margin_left;
12465 vp_playfield->y += vp_playfield->margin_top;
12467 // adjust individual playfield borders if only default border is specified
12469 if (vp_playfield->border_left == -1)
12470 vp_playfield->border_left = vp_playfield->border_size;
12471 if (vp_playfield->border_right == -1)
12472 vp_playfield->border_right = vp_playfield->border_size;
12473 if (vp_playfield->border_top == -1)
12474 vp_playfield->border_top = vp_playfield->border_size;
12475 if (vp_playfield->border_bottom == -1)
12476 vp_playfield->border_bottom = vp_playfield->border_size;
12478 // set dynamic playfield borders if borders are specified as undefined
12479 // (but only if window size was dynamic and playfield size was static)
12481 if (dynamic_window_width && !dynamic_playfield_width)
12483 if (vp_playfield->border_left == -1)
12485 vp_playfield->border_left = (vp_playfield->x -
12486 vp_playfield->margin_left);
12487 vp_playfield->x -= vp_playfield->border_left;
12488 vp_playfield->width += vp_playfield->border_left;
12491 if (vp_playfield->border_right == -1)
12493 vp_playfield->border_right = (vp_window->width -
12495 vp_playfield->width -
12496 vp_playfield->margin_right);
12497 vp_playfield->width += vp_playfield->border_right;
12501 if (dynamic_window_height && !dynamic_playfield_height)
12503 if (vp_playfield->border_top == -1)
12505 vp_playfield->border_top = (vp_playfield->y -
12506 vp_playfield->margin_top);
12507 vp_playfield->y -= vp_playfield->border_top;
12508 vp_playfield->height += vp_playfield->border_top;
12511 if (vp_playfield->border_bottom == -1)
12513 vp_playfield->border_bottom = (vp_window->height -
12515 vp_playfield->height -
12516 vp_playfield->margin_bottom);
12517 vp_playfield->height += vp_playfield->border_bottom;
12521 // adjust playfield size to be a multiple of a defined alignment tile size
12523 int align_size = vp_playfield->align_size;
12524 int playfield_xtiles = vp_playfield->width / align_size;
12525 int playfield_ytiles = vp_playfield->height / align_size;
12526 int playfield_width_corrected = playfield_xtiles * align_size;
12527 int playfield_height_corrected = playfield_ytiles * align_size;
12528 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12529 i == GFX_SPECIAL_ARG_EDITOR);
12531 if (is_playfield_mode &&
12532 dynamic_playfield_width &&
12533 vp_playfield->width != playfield_width_corrected)
12535 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12537 vp_playfield->width = playfield_width_corrected;
12539 if (vp_playfield->align == ALIGN_LEFT)
12541 vp_playfield->border_left += playfield_xdiff;
12543 else if (vp_playfield->align == ALIGN_RIGHT)
12545 vp_playfield->border_right += playfield_xdiff;
12547 else if (vp_playfield->align == ALIGN_CENTER)
12549 int border_left_diff = playfield_xdiff / 2;
12550 int border_right_diff = playfield_xdiff - border_left_diff;
12552 vp_playfield->border_left += border_left_diff;
12553 vp_playfield->border_right += border_right_diff;
12557 if (is_playfield_mode &&
12558 dynamic_playfield_height &&
12559 vp_playfield->height != playfield_height_corrected)
12561 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12563 vp_playfield->height = playfield_height_corrected;
12565 if (vp_playfield->valign == VALIGN_TOP)
12567 vp_playfield->border_top += playfield_ydiff;
12569 else if (vp_playfield->align == VALIGN_BOTTOM)
12571 vp_playfield->border_right += playfield_ydiff;
12573 else if (vp_playfield->align == VALIGN_MIDDLE)
12575 int border_top_diff = playfield_ydiff / 2;
12576 int border_bottom_diff = playfield_ydiff - border_top_diff;
12578 vp_playfield->border_top += border_top_diff;
12579 vp_playfield->border_bottom += border_bottom_diff;
12583 // adjust door positions according to specified alignment
12585 for (j = 0; j < 2; j++)
12587 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12589 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12590 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12591 else if (vp_door->align == ALIGN_CENTER)
12592 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12593 else if (vp_door->align == ALIGN_RIGHT)
12594 vp_door->x += vp_window->width - vp_door->width;
12596 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12597 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12598 else if (vp_door->valign == VALIGN_MIDDLE)
12599 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12600 else if (vp_door->valign == VALIGN_BOTTOM)
12601 vp_door->y += vp_window->height - vp_door->height;
12606 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12610 struct XYTileSize *dst, *src;
12613 editor_buttons_xy[] =
12616 &editor.button.element_left, &editor.palette.element_left,
12617 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12620 &editor.button.element_middle, &editor.palette.element_middle,
12621 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12624 &editor.button.element_right, &editor.palette.element_right,
12625 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12632 // set default position for element buttons to element graphics
12633 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12635 if ((*editor_buttons_xy[i].dst).x == -1 &&
12636 (*editor_buttons_xy[i].dst).y == -1)
12638 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12640 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12642 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12646 // adjust editor palette rows and columns if specified to be dynamic
12648 if (editor.palette.cols == -1)
12650 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12651 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12652 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12654 editor.palette.cols = (vp_width - sc_width) / bt_width;
12656 if (editor.palette.x == -1)
12658 int palette_width = editor.palette.cols * bt_width + sc_width;
12660 editor.palette.x = (vp_width - palette_width) / 2;
12664 if (editor.palette.rows == -1)
12666 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12667 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12668 int tx_height = getFontHeight(FONT_TEXT_2);
12670 editor.palette.rows = (vp_height - tx_height) / bt_height;
12672 if (editor.palette.y == -1)
12674 int palette_height = editor.palette.rows * bt_height + tx_height;
12676 editor.palette.y = (vp_height - palette_height) / 2;
12681 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12682 boolean initialize)
12684 // special case: check if network and preview player positions are redefined,
12685 // to compare this later against the main menu level preview being redefined
12686 struct TokenIntPtrInfo menu_config_players[] =
12688 { "main.network_players.x", &menu.main.network_players.redefined },
12689 { "main.network_players.y", &menu.main.network_players.redefined },
12690 { "main.preview_players.x", &menu.main.preview_players.redefined },
12691 { "main.preview_players.y", &menu.main.preview_players.redefined },
12692 { "preview.x", &preview.redefined },
12693 { "preview.y", &preview.redefined }
12699 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12700 *menu_config_players[i].value = FALSE;
12704 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12705 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12706 *menu_config_players[i].value = TRUE;
12710 static void InitMenuDesignSettings_PreviewPlayers(void)
12712 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12715 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12717 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12720 static void LoadMenuDesignSettingsFromFilename(char *filename)
12722 static struct TitleFadingInfo tfi;
12723 static struct TitleMessageInfo tmi;
12724 static struct TokenInfo title_tokens[] =
12726 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12727 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12728 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12729 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12730 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12734 static struct TokenInfo titlemessage_tokens[] =
12736 { TYPE_INTEGER, &tmi.x, ".x" },
12737 { TYPE_INTEGER, &tmi.y, ".y" },
12738 { TYPE_INTEGER, &tmi.width, ".width" },
12739 { TYPE_INTEGER, &tmi.height, ".height" },
12740 { TYPE_INTEGER, &tmi.chars, ".chars" },
12741 { TYPE_INTEGER, &tmi.lines, ".lines" },
12742 { TYPE_INTEGER, &tmi.align, ".align" },
12743 { TYPE_INTEGER, &tmi.valign, ".valign" },
12744 { TYPE_INTEGER, &tmi.font, ".font" },
12745 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12746 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12747 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12748 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12749 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12750 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12751 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12752 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12753 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12759 struct TitleFadingInfo *info;
12764 // initialize first titles from "enter screen" definitions, if defined
12765 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12766 { &title_first_default, "menu.enter_screen.TITLE" },
12768 // initialize title screens from "next screen" definitions, if defined
12769 { &title_initial_default, "menu.next_screen.TITLE" },
12770 { &title_default, "menu.next_screen.TITLE" },
12776 struct TitleMessageInfo *array;
12779 titlemessage_arrays[] =
12781 // initialize first titles from "enter screen" definitions, if defined
12782 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12783 { titlescreen_first, "menu.enter_screen.TITLE" },
12784 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12785 { titlemessage_first, "menu.enter_screen.TITLE" },
12787 // initialize titles from "next screen" definitions, if defined
12788 { titlescreen_initial, "menu.next_screen.TITLE" },
12789 { titlescreen, "menu.next_screen.TITLE" },
12790 { titlemessage_initial, "menu.next_screen.TITLE" },
12791 { titlemessage, "menu.next_screen.TITLE" },
12793 // overwrite titles with title definitions, if defined
12794 { titlescreen_initial_first, "[title_initial]" },
12795 { titlescreen_first, "[title]" },
12796 { titlemessage_initial_first, "[title_initial]" },
12797 { titlemessage_first, "[title]" },
12799 { titlescreen_initial, "[title_initial]" },
12800 { titlescreen, "[title]" },
12801 { titlemessage_initial, "[title_initial]" },
12802 { titlemessage, "[title]" },
12804 // overwrite titles with title screen/message definitions, if defined
12805 { titlescreen_initial_first, "[titlescreen_initial]" },
12806 { titlescreen_first, "[titlescreen]" },
12807 { titlemessage_initial_first, "[titlemessage_initial]" },
12808 { titlemessage_first, "[titlemessage]" },
12810 { titlescreen_initial, "[titlescreen_initial]" },
12811 { titlescreen, "[titlescreen]" },
12812 { titlemessage_initial, "[titlemessage_initial]" },
12813 { titlemessage, "[titlemessage]" },
12817 SetupFileHash *setup_file_hash;
12820 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12823 // the following initializes hierarchical values from dynamic configuration
12825 // special case: initialize with default values that may be overwritten
12826 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12827 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12829 struct TokenIntPtrInfo menu_config[] =
12831 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12832 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12833 { "menu.list_size", &menu.list_size[i] }
12836 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12838 char *token = menu_config[j].token;
12839 char *value = getHashEntry(setup_file_hash, token);
12842 *menu_config[j].value = get_integer_from_string(value);
12846 // special case: initialize with default values that may be overwritten
12847 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12848 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12850 struct TokenIntPtrInfo menu_config[] =
12852 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12853 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12854 { "menu.list_size.INFO", &menu.list_size_info[i] },
12855 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12856 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12859 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12861 char *token = menu_config[j].token;
12862 char *value = getHashEntry(setup_file_hash, token);
12865 *menu_config[j].value = get_integer_from_string(value);
12869 // special case: initialize with default values that may be overwritten
12870 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12871 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12873 struct TokenIntPtrInfo menu_config[] =
12875 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12876 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12879 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12881 char *token = menu_config[j].token;
12882 char *value = getHashEntry(setup_file_hash, token);
12885 *menu_config[j].value = get_integer_from_string(value);
12889 // special case: initialize with default values that may be overwritten
12890 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12891 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12893 struct TokenIntPtrInfo menu_config[] =
12895 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12896 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12897 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12898 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12899 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12900 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12901 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12902 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12903 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12904 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12907 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12909 char *token = menu_config[j].token;
12910 char *value = getHashEntry(setup_file_hash, token);
12913 *menu_config[j].value = get_integer_from_string(value);
12917 // special case: initialize with default values that may be overwritten
12918 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12919 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12921 struct TokenIntPtrInfo menu_config[] =
12923 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12924 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12925 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12926 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12927 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12928 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12929 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12930 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12931 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12934 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12936 char *token = menu_config[j].token;
12937 char *value = getHashEntry(setup_file_hash, token);
12940 *menu_config[j].value = get_token_parameter_value(token, value);
12944 // special case: initialize with default values that may be overwritten
12945 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12946 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12950 char *token_prefix;
12951 struct RectWithBorder *struct_ptr;
12955 { "viewport.window", &viewport.window[i] },
12956 { "viewport.playfield", &viewport.playfield[i] },
12957 { "viewport.door_1", &viewport.door_1[i] },
12958 { "viewport.door_2", &viewport.door_2[i] }
12961 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12963 struct TokenIntPtrInfo vp_config[] =
12965 { ".x", &vp_struct[j].struct_ptr->x },
12966 { ".y", &vp_struct[j].struct_ptr->y },
12967 { ".width", &vp_struct[j].struct_ptr->width },
12968 { ".height", &vp_struct[j].struct_ptr->height },
12969 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12970 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12971 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12972 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12973 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12974 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12975 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12976 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12977 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12978 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12979 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12980 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12981 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12982 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12983 { ".align", &vp_struct[j].struct_ptr->align },
12984 { ".valign", &vp_struct[j].struct_ptr->valign }
12987 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12989 char *token = getStringCat2(vp_struct[j].token_prefix,
12990 vp_config[k].token);
12991 char *value = getHashEntry(setup_file_hash, token);
12994 *vp_config[k].value = get_token_parameter_value(token, value);
13001 // special case: initialize with default values that may be overwritten
13002 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13003 for (i = 0; title_info[i].info != NULL; i++)
13005 struct TitleFadingInfo *info = title_info[i].info;
13006 char *base_token = title_info[i].text;
13008 for (j = 0; title_tokens[j].type != -1; j++)
13010 char *token = getStringCat2(base_token, title_tokens[j].text);
13011 char *value = getHashEntry(setup_file_hash, token);
13015 int parameter_value = get_token_parameter_value(token, value);
13019 *(int *)title_tokens[j].value = (int)parameter_value;
13028 // special case: initialize with default values that may be overwritten
13029 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13030 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13032 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13033 char *base_token = titlemessage_arrays[i].text;
13035 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13037 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13038 char *value = getHashEntry(setup_file_hash, token);
13042 int parameter_value = get_token_parameter_value(token, value);
13044 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13048 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13049 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13051 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13061 // read (and overwrite with) values that may be specified in config file
13062 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13064 // special case: check if network and preview player positions are redefined
13065 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13067 freeSetupFileHash(setup_file_hash);
13070 void LoadMenuDesignSettings(void)
13072 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13074 InitMenuDesignSettings_Static();
13075 InitMenuDesignSettings_SpecialPreProcessing();
13076 InitMenuDesignSettings_PreviewPlayers();
13078 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13080 // first look for special settings configured in level series config
13081 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13083 if (fileExists(filename_base))
13084 LoadMenuDesignSettingsFromFilename(filename_base);
13087 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13089 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13090 LoadMenuDesignSettingsFromFilename(filename_local);
13092 InitMenuDesignSettings_SpecialPostProcessing();
13095 void LoadMenuDesignSettings_AfterGraphics(void)
13097 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13100 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13101 boolean ignore_defaults)
13105 for (i = 0; sound_config_vars[i].token != NULL; i++)
13107 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13109 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13110 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13114 *sound_config_vars[i].value =
13115 get_token_parameter_value(sound_config_vars[i].token, value);
13119 void InitSoundSettings_Static(void)
13121 // always start with reliable default values from static default config
13122 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13125 static void LoadSoundSettingsFromFilename(char *filename)
13127 SetupFileHash *setup_file_hash;
13129 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13132 // read (and overwrite with) values that may be specified in config file
13133 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13135 freeSetupFileHash(setup_file_hash);
13138 void LoadSoundSettings(void)
13140 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13142 InitSoundSettings_Static();
13144 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13146 // first look for special settings configured in level series config
13147 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13149 if (fileExists(filename_base))
13150 LoadSoundSettingsFromFilename(filename_base);
13153 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13155 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13156 LoadSoundSettingsFromFilename(filename_local);
13159 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13161 char *filename = getEditorSetupFilename();
13162 SetupFileList *setup_file_list, *list;
13163 SetupFileHash *element_hash;
13164 int num_unknown_tokens = 0;
13167 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13170 element_hash = newSetupFileHash();
13172 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13173 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13175 // determined size may be larger than needed (due to unknown elements)
13177 for (list = setup_file_list; list != NULL; list = list->next)
13180 // add space for up to 3 more elements for padding that may be needed
13181 *num_elements += 3;
13183 // free memory for old list of elements, if needed
13184 checked_free(*elements);
13186 // allocate memory for new list of elements
13187 *elements = checked_malloc(*num_elements * sizeof(int));
13190 for (list = setup_file_list; list != NULL; list = list->next)
13192 char *value = getHashEntry(element_hash, list->token);
13194 if (value == NULL) // try to find obsolete token mapping
13196 char *mapped_token = get_mapped_token(list->token);
13198 if (mapped_token != NULL)
13200 value = getHashEntry(element_hash, mapped_token);
13202 free(mapped_token);
13208 (*elements)[(*num_elements)++] = atoi(value);
13212 if (num_unknown_tokens == 0)
13215 Warn("unknown token(s) found in config file:");
13216 Warn("- config file: '%s'", filename);
13218 num_unknown_tokens++;
13221 Warn("- token: '%s'", list->token);
13225 if (num_unknown_tokens > 0)
13228 while (*num_elements % 4) // pad with empty elements, if needed
13229 (*elements)[(*num_elements)++] = EL_EMPTY;
13231 freeSetupFileList(setup_file_list);
13232 freeSetupFileHash(element_hash);
13235 for (i = 0; i < *num_elements; i++)
13236 Debug("editor", "element '%s' [%d]\n",
13237 element_info[(*elements)[i]].token_name, (*elements)[i]);
13241 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13244 SetupFileHash *setup_file_hash = NULL;
13245 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13246 char *filename_music, *filename_prefix, *filename_info;
13252 token_to_value_ptr[] =
13254 { "title_header", &tmp_music_file_info.title_header },
13255 { "artist_header", &tmp_music_file_info.artist_header },
13256 { "album_header", &tmp_music_file_info.album_header },
13257 { "year_header", &tmp_music_file_info.year_header },
13258 { "played_header", &tmp_music_file_info.played_header },
13260 { "title", &tmp_music_file_info.title },
13261 { "artist", &tmp_music_file_info.artist },
13262 { "album", &tmp_music_file_info.album },
13263 { "year", &tmp_music_file_info.year },
13264 { "played", &tmp_music_file_info.played },
13270 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13271 getCustomMusicFilename(basename));
13273 if (filename_music == NULL)
13276 // ---------- try to replace file extension ----------
13278 filename_prefix = getStringCopy(filename_music);
13279 if (strrchr(filename_prefix, '.') != NULL)
13280 *strrchr(filename_prefix, '.') = '\0';
13281 filename_info = getStringCat2(filename_prefix, ".txt");
13283 if (fileExists(filename_info))
13284 setup_file_hash = loadSetupFileHash(filename_info);
13286 free(filename_prefix);
13287 free(filename_info);
13289 if (setup_file_hash == NULL)
13291 // ---------- try to add file extension ----------
13293 filename_prefix = getStringCopy(filename_music);
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);
13303 if (setup_file_hash == NULL)
13306 // ---------- music file info found ----------
13308 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13310 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13312 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13314 *token_to_value_ptr[i].value_ptr =
13315 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13318 tmp_music_file_info.basename = getStringCopy(basename);
13319 tmp_music_file_info.music = music;
13320 tmp_music_file_info.is_sound = is_sound;
13322 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13323 *new_music_file_info = tmp_music_file_info;
13325 return new_music_file_info;
13328 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13330 return get_music_file_info_ext(basename, music, FALSE);
13333 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13335 return get_music_file_info_ext(basename, sound, TRUE);
13338 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13339 char *basename, boolean is_sound)
13341 for (; list != NULL; list = list->next)
13342 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13348 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13350 return music_info_listed_ext(list, basename, FALSE);
13353 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13355 return music_info_listed_ext(list, basename, TRUE);
13358 void LoadMusicInfo(void)
13360 int num_music_noconf = getMusicListSize_NoConf();
13361 int num_music = getMusicListSize();
13362 int num_sounds = getSoundListSize();
13363 struct FileInfo *music, *sound;
13364 struct MusicFileInfo *next, **new;
13368 while (music_file_info != NULL)
13370 next = music_file_info->next;
13372 checked_free(music_file_info->basename);
13374 checked_free(music_file_info->title_header);
13375 checked_free(music_file_info->artist_header);
13376 checked_free(music_file_info->album_header);
13377 checked_free(music_file_info->year_header);
13378 checked_free(music_file_info->played_header);
13380 checked_free(music_file_info->title);
13381 checked_free(music_file_info->artist);
13382 checked_free(music_file_info->album);
13383 checked_free(music_file_info->year);
13384 checked_free(music_file_info->played);
13386 free(music_file_info);
13388 music_file_info = next;
13391 new = &music_file_info;
13393 // get (configured or unconfigured) music file info for all levels
13394 for (i = leveldir_current->first_level;
13395 i <= leveldir_current->last_level; i++)
13399 if (levelset.music[i] != MUS_UNDEFINED)
13401 // get music file info for configured level music
13402 music_nr = levelset.music[i];
13404 else if (num_music_noconf > 0)
13406 // get music file info for unconfigured level music
13407 int level_pos = i - leveldir_current->first_level;
13409 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13416 char *basename = getMusicInfoEntryFilename(music_nr);
13418 if (basename == NULL)
13421 if (!music_info_listed(music_file_info, basename))
13423 *new = get_music_file_info(basename, music_nr);
13426 new = &(*new)->next;
13430 // get music file info for all remaining configured music files
13431 for (i = 0; i < num_music; i++)
13433 music = getMusicListEntry(i);
13435 if (music->filename == NULL)
13438 if (strEqual(music->filename, UNDEFINED_FILENAME))
13441 // a configured file may be not recognized as music
13442 if (!FileIsMusic(music->filename))
13445 if (!music_info_listed(music_file_info, music->filename))
13447 *new = get_music_file_info(music->filename, i);
13450 new = &(*new)->next;
13454 // get sound file info for all configured sound files
13455 for (i = 0; i < num_sounds; i++)
13457 sound = getSoundListEntry(i);
13459 if (sound->filename == NULL)
13462 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13465 // a configured file may be not recognized as sound
13466 if (!FileIsSound(sound->filename))
13469 if (!sound_info_listed(music_file_info, sound->filename))
13471 *new = get_sound_file_info(sound->filename, i);
13473 new = &(*new)->next;
13477 // add pointers to previous list nodes
13479 struct MusicFileInfo *node = music_file_info;
13481 while (node != NULL)
13484 node->next->prev = node;
13490 static void add_helpanim_entry(int element, int action, int direction,
13491 int delay, int *num_list_entries)
13493 struct HelpAnimInfo *new_list_entry;
13494 (*num_list_entries)++;
13497 checked_realloc(helpanim_info,
13498 *num_list_entries * sizeof(struct HelpAnimInfo));
13499 new_list_entry = &helpanim_info[*num_list_entries - 1];
13501 new_list_entry->element = element;
13502 new_list_entry->action = action;
13503 new_list_entry->direction = direction;
13504 new_list_entry->delay = delay;
13507 static void print_unknown_token(char *filename, char *token, int token_nr)
13512 Warn("unknown token(s) found in config file:");
13513 Warn("- config file: '%s'", filename);
13516 Warn("- token: '%s'", token);
13519 static void print_unknown_token_end(int token_nr)
13525 void LoadHelpAnimInfo(void)
13527 char *filename = getHelpAnimFilename();
13528 SetupFileList *setup_file_list = NULL, *list;
13529 SetupFileHash *element_hash, *action_hash, *direction_hash;
13530 int num_list_entries = 0;
13531 int num_unknown_tokens = 0;
13534 if (fileExists(filename))
13535 setup_file_list = loadSetupFileList(filename);
13537 if (setup_file_list == NULL)
13539 // use reliable default values from static configuration
13540 SetupFileList *insert_ptr;
13542 insert_ptr = setup_file_list =
13543 newSetupFileList(helpanim_config[0].token,
13544 helpanim_config[0].value);
13546 for (i = 1; helpanim_config[i].token; i++)
13547 insert_ptr = addListEntry(insert_ptr,
13548 helpanim_config[i].token,
13549 helpanim_config[i].value);
13552 element_hash = newSetupFileHash();
13553 action_hash = newSetupFileHash();
13554 direction_hash = newSetupFileHash();
13556 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13557 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13559 for (i = 0; i < NUM_ACTIONS; i++)
13560 setHashEntry(action_hash, element_action_info[i].suffix,
13561 i_to_a(element_action_info[i].value));
13563 // do not store direction index (bit) here, but direction value!
13564 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13565 setHashEntry(direction_hash, element_direction_info[i].suffix,
13566 i_to_a(1 << element_direction_info[i].value));
13568 for (list = setup_file_list; list != NULL; list = list->next)
13570 char *element_token, *action_token, *direction_token;
13571 char *element_value, *action_value, *direction_value;
13572 int delay = atoi(list->value);
13574 if (strEqual(list->token, "end"))
13576 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13581 /* first try to break element into element/action/direction parts;
13582 if this does not work, also accept combined "element[.act][.dir]"
13583 elements (like "dynamite.active"), which are unique elements */
13585 if (strchr(list->token, '.') == NULL) // token contains no '.'
13587 element_value = getHashEntry(element_hash, list->token);
13588 if (element_value != NULL) // element found
13589 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13590 &num_list_entries);
13593 // no further suffixes found -- this is not an element
13594 print_unknown_token(filename, list->token, num_unknown_tokens++);
13600 // token has format "<prefix>.<something>"
13602 action_token = strchr(list->token, '.'); // suffix may be action ...
13603 direction_token = action_token; // ... or direction
13605 element_token = getStringCopy(list->token);
13606 *strchr(element_token, '.') = '\0';
13608 element_value = getHashEntry(element_hash, element_token);
13610 if (element_value == NULL) // this is no element
13612 element_value = getHashEntry(element_hash, list->token);
13613 if (element_value != NULL) // combined element found
13614 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13615 &num_list_entries);
13617 print_unknown_token(filename, list->token, num_unknown_tokens++);
13619 free(element_token);
13624 action_value = getHashEntry(action_hash, action_token);
13626 if (action_value != NULL) // action found
13628 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13629 &num_list_entries);
13631 free(element_token);
13636 direction_value = getHashEntry(direction_hash, direction_token);
13638 if (direction_value != NULL) // direction found
13640 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13641 &num_list_entries);
13643 free(element_token);
13648 if (strchr(action_token + 1, '.') == NULL)
13650 // no further suffixes found -- this is not an action nor direction
13652 element_value = getHashEntry(element_hash, list->token);
13653 if (element_value != NULL) // combined element found
13654 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13655 &num_list_entries);
13657 print_unknown_token(filename, list->token, num_unknown_tokens++);
13659 free(element_token);
13664 // token has format "<prefix>.<suffix>.<something>"
13666 direction_token = strchr(action_token + 1, '.');
13668 action_token = getStringCopy(action_token);
13669 *strchr(action_token + 1, '.') = '\0';
13671 action_value = getHashEntry(action_hash, action_token);
13673 if (action_value == NULL) // this is no action
13675 element_value = getHashEntry(element_hash, list->token);
13676 if (element_value != NULL) // combined element found
13677 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13678 &num_list_entries);
13680 print_unknown_token(filename, list->token, num_unknown_tokens++);
13682 free(element_token);
13683 free(action_token);
13688 direction_value = getHashEntry(direction_hash, direction_token);
13690 if (direction_value != NULL) // direction found
13692 add_helpanim_entry(atoi(element_value), atoi(action_value),
13693 atoi(direction_value), delay, &num_list_entries);
13695 free(element_token);
13696 free(action_token);
13701 // this is no direction
13703 element_value = getHashEntry(element_hash, list->token);
13704 if (element_value != NULL) // combined element found
13705 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13706 &num_list_entries);
13708 print_unknown_token(filename, list->token, num_unknown_tokens++);
13710 free(element_token);
13711 free(action_token);
13714 print_unknown_token_end(num_unknown_tokens);
13716 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13717 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13719 freeSetupFileList(setup_file_list);
13720 freeSetupFileHash(element_hash);
13721 freeSetupFileHash(action_hash);
13722 freeSetupFileHash(direction_hash);
13725 for (i = 0; i < num_list_entries; i++)
13726 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13727 EL_NAME(helpanim_info[i].element),
13728 helpanim_info[i].element,
13729 helpanim_info[i].action,
13730 helpanim_info[i].direction,
13731 helpanim_info[i].delay);
13735 void LoadHelpTextInfo(void)
13737 char *filename = getHelpTextFilename();
13740 if (helptext_info != NULL)
13742 freeSetupFileHash(helptext_info);
13743 helptext_info = NULL;
13746 if (fileExists(filename))
13747 helptext_info = loadSetupFileHash(filename);
13749 if (helptext_info == NULL)
13751 // use reliable default values from static configuration
13752 helptext_info = newSetupFileHash();
13754 for (i = 0; helptext_config[i].token; i++)
13755 setHashEntry(helptext_info,
13756 helptext_config[i].token,
13757 helptext_config[i].value);
13761 BEGIN_HASH_ITERATION(helptext_info, itr)
13763 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13764 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13766 END_HASH_ITERATION(hash, itr)
13771 // ----------------------------------------------------------------------------
13773 // ----------------------------------------------------------------------------
13775 #define MAX_NUM_CONVERT_LEVELS 1000
13777 void ConvertLevels(void)
13779 static LevelDirTree *convert_leveldir = NULL;
13780 static int convert_level_nr = -1;
13781 static int num_levels_handled = 0;
13782 static int num_levels_converted = 0;
13783 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13786 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13787 global.convert_leveldir);
13789 if (convert_leveldir == NULL)
13790 Fail("no such level identifier: '%s'", global.convert_leveldir);
13792 leveldir_current = convert_leveldir;
13794 if (global.convert_level_nr != -1)
13796 convert_leveldir->first_level = global.convert_level_nr;
13797 convert_leveldir->last_level = global.convert_level_nr;
13800 convert_level_nr = convert_leveldir->first_level;
13802 PrintLine("=", 79);
13803 Print("Converting levels\n");
13804 PrintLine("-", 79);
13805 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13806 Print("Level series name: '%s'\n", convert_leveldir->name);
13807 Print("Level series author: '%s'\n", convert_leveldir->author);
13808 Print("Number of levels: %d\n", convert_leveldir->levels);
13809 PrintLine("=", 79);
13812 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13813 levels_failed[i] = FALSE;
13815 while (convert_level_nr <= convert_leveldir->last_level)
13817 char *level_filename;
13820 level_nr = convert_level_nr++;
13822 Print("Level %03d: ", level_nr);
13824 LoadLevel(level_nr);
13825 if (level.no_level_file || level.no_valid_file)
13827 Print("(no level)\n");
13831 Print("converting level ... ");
13834 // special case: conversion of some EMC levels as requested by ACME
13835 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13838 level_filename = getDefaultLevelFilename(level_nr);
13839 new_level = !fileExists(level_filename);
13843 SaveLevel(level_nr);
13845 num_levels_converted++;
13847 Print("converted.\n");
13851 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13852 levels_failed[level_nr] = TRUE;
13854 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13857 num_levels_handled++;
13861 PrintLine("=", 79);
13862 Print("Number of levels handled: %d\n", num_levels_handled);
13863 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13864 (num_levels_handled ?
13865 num_levels_converted * 100 / num_levels_handled : 0));
13866 PrintLine("-", 79);
13867 Print("Summary (for automatic parsing by scripts):\n");
13868 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13869 convert_leveldir->identifier, num_levels_converted,
13870 num_levels_handled,
13871 (num_levels_handled ?
13872 num_levels_converted * 100 / num_levels_handled : 0));
13874 if (num_levels_handled != num_levels_converted)
13876 Print(", FAILED:");
13877 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13878 if (levels_failed[i])
13883 PrintLine("=", 79);
13885 CloseAllAndExit(0);
13889 // ----------------------------------------------------------------------------
13890 // create and save images for use in level sketches (raw BMP format)
13891 // ----------------------------------------------------------------------------
13893 void CreateLevelSketchImages(void)
13899 InitElementPropertiesGfxElement();
13901 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13902 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13904 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13906 int element = getMappedElement(i);
13907 char basename1[16];
13908 char basename2[16];
13912 sprintf(basename1, "%04d.bmp", i);
13913 sprintf(basename2, "%04ds.bmp", i);
13915 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13916 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13918 DrawSizedElement(0, 0, element, TILESIZE);
13919 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13921 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13922 Fail("cannot save level sketch image file '%s'", filename1);
13924 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13925 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13927 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13928 Fail("cannot save level sketch image file '%s'", filename2);
13933 // create corresponding SQL statements (for normal and small images)
13936 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13937 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13940 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13941 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13943 // optional: create content for forum level sketch demonstration post
13945 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13948 FreeBitmap(bitmap1);
13949 FreeBitmap(bitmap2);
13952 fprintf(stderr, "\n");
13954 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13956 CloseAllAndExit(0);
13960 // ----------------------------------------------------------------------------
13961 // create and save images for element collecting animations (raw BMP format)
13962 // ----------------------------------------------------------------------------
13964 static boolean createCollectImage(int element)
13966 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13969 void CreateCollectElementImages(void)
13973 int anim_frames = num_steps - 1;
13974 int tile_size = TILESIZE;
13975 int anim_width = tile_size * anim_frames;
13976 int anim_height = tile_size;
13977 int num_collect_images = 0;
13978 int pos_collect_images = 0;
13980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13981 if (createCollectImage(i))
13982 num_collect_images++;
13984 Info("Creating %d element collecting animation images ...",
13985 num_collect_images);
13987 int dst_width = anim_width * 2;
13988 int dst_height = anim_height * num_collect_images / 2;
13989 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13990 char *basename_bmp = "RocksCollect.bmp";
13991 char *basename_png = "RocksCollect.png";
13992 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13993 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13994 int len_filename_bmp = strlen(filename_bmp);
13995 int len_filename_png = strlen(filename_png);
13996 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13997 char cmd_convert[max_command_len];
13999 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14003 // force using RGBA surface for destination bitmap
14004 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14005 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14007 dst_bitmap->surface =
14008 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14010 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14012 if (!createCollectImage(i))
14015 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14016 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14017 int graphic = el2img(i);
14018 char *token_name = element_info[i].token_name;
14019 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14020 Bitmap *src_bitmap;
14023 Info("- creating collecting image for '%s' ...", token_name);
14025 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14027 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14028 tile_size, tile_size, 0, 0);
14030 // force using RGBA surface for temporary bitmap (using transparent black)
14031 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14032 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14034 tmp_bitmap->surface =
14035 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14037 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14039 for (j = 0; j < anim_frames; j++)
14041 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14042 int frame_size = frame_size_final * num_steps;
14043 int offset = (tile_size - frame_size_final) / 2;
14044 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14046 while (frame_size > frame_size_final)
14050 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14052 FreeBitmap(frame_bitmap);
14054 frame_bitmap = half_bitmap;
14057 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14058 frame_size_final, frame_size_final,
14059 dst_x + j * tile_size + offset, dst_y + offset);
14061 FreeBitmap(frame_bitmap);
14064 tmp_bitmap->surface_masked = NULL;
14066 FreeBitmap(tmp_bitmap);
14068 pos_collect_images++;
14071 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14072 Fail("cannot save element collecting image file '%s'", filename_bmp);
14074 FreeBitmap(dst_bitmap);
14076 Info("Converting image file from BMP to PNG ...");
14078 if (system(cmd_convert) != 0)
14079 Fail("converting image file failed");
14081 unlink(filename_bmp);
14085 CloseAllAndExit(0);
14089 // ----------------------------------------------------------------------------
14090 // create and save images for custom and group elements (raw BMP format)
14091 // ----------------------------------------------------------------------------
14093 void CreateCustomElementImages(char *directory)
14095 char *src_basename = "RocksCE-template.ilbm";
14096 char *dst_basename = "RocksCE.bmp";
14097 char *src_filename = getPath2(directory, src_basename);
14098 char *dst_filename = getPath2(directory, dst_basename);
14099 Bitmap *src_bitmap;
14101 int yoffset_ce = 0;
14102 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14105 InitVideoDefaults();
14107 ReCreateBitmap(&backbuffer, video.width, video.height);
14109 src_bitmap = LoadImage(src_filename);
14111 bitmap = CreateBitmap(TILEX * 16 * 2,
14112 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14115 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14122 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14123 TILEX * x, TILEY * y + yoffset_ce);
14125 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14127 TILEX * x + TILEX * 16,
14128 TILEY * y + yoffset_ce);
14130 for (j = 2; j >= 0; j--)
14134 BlitBitmap(src_bitmap, bitmap,
14135 TILEX + c * 7, 0, 6, 10,
14136 TILEX * x + 6 + j * 7,
14137 TILEY * y + 11 + yoffset_ce);
14139 BlitBitmap(src_bitmap, bitmap,
14140 TILEX + c * 8, TILEY, 6, 10,
14141 TILEX * 16 + TILEX * x + 6 + j * 8,
14142 TILEY * y + 10 + yoffset_ce);
14148 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14155 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14156 TILEX * x, TILEY * y + yoffset_ge);
14158 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14160 TILEX * x + TILEX * 16,
14161 TILEY * y + yoffset_ge);
14163 for (j = 1; j >= 0; j--)
14167 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14168 TILEX * x + 6 + j * 10,
14169 TILEY * y + 11 + yoffset_ge);
14171 BlitBitmap(src_bitmap, bitmap,
14172 TILEX + c * 8, TILEY + 12, 6, 10,
14173 TILEX * 16 + TILEX * x + 10 + j * 8,
14174 TILEY * y + 10 + yoffset_ge);
14180 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14181 Fail("cannot save CE graphics file '%s'", dst_filename);
14183 FreeBitmap(bitmap);
14185 CloseAllAndExit(0);