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"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
55 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
57 // (nothing at all if unchanged)
58 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
60 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
61 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
62 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
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),
909 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
913 EL_MM_STEEL_BLOCK, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 &li.mm_time_block, 75
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
920 &li.score[SC_ELEM_BONUS], 10
923 // ---------- unused values -------------------------------------------------
926 EL_UNKNOWN, SAVE_CONF_NEVER,
927 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
928 &li.score[SC_UNKNOWN_15], 10
938 static struct LevelFileConfigInfo chunk_config_NOTE[] =
942 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
943 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
947 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
948 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
953 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
954 &xx_envelope.autowrap, FALSE
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
959 &xx_envelope.centered, FALSE
964 TYPE_STRING, CONF_VALUE_BYTES(1),
965 &xx_envelope.text, -1, NULL,
966 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
967 &xx_default_string_empty[0]
977 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
981 TYPE_STRING, CONF_VALUE_BYTES(1),
982 &xx_ei.description[0], -1,
983 &yy_ei.description[0],
984 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
985 &xx_default_description[0]
990 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
991 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
992 &yy_ei.properties[EP_BITFIELD_BASE_NR]
994 #if ENABLE_RESERVED_CODE
995 // (reserved for later use)
998 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
999 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1000 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1007 &xx_ei.use_gfx_element, FALSE,
1008 &yy_ei.use_gfx_element
1012 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1013 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1014 &yy_ei.gfx_element_initial
1019 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1020 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1021 &yy_ei.access_direction
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1027 &xx_ei.collect_score_initial, 10,
1028 &yy_ei.collect_score_initial
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1033 &xx_ei.collect_count_initial, 1,
1034 &yy_ei.collect_count_initial
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1040 &xx_ei.ce_value_fixed_initial, 0,
1041 &yy_ei.ce_value_fixed_initial
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1046 &xx_ei.ce_value_random_initial, 0,
1047 &yy_ei.ce_value_random_initial
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1052 &xx_ei.use_last_ce_value, FALSE,
1053 &yy_ei.use_last_ce_value
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1059 &xx_ei.push_delay_fixed, 8,
1060 &yy_ei.push_delay_fixed
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1065 &xx_ei.push_delay_random, 8,
1066 &yy_ei.push_delay_random
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1071 &xx_ei.drop_delay_fixed, 0,
1072 &yy_ei.drop_delay_fixed
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1077 &xx_ei.drop_delay_random, 0,
1078 &yy_ei.drop_delay_random
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1083 &xx_ei.move_delay_fixed, 0,
1084 &yy_ei.move_delay_fixed
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1089 &xx_ei.move_delay_random, 0,
1090 &yy_ei.move_delay_random
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1095 &xx_ei.step_delay_fixed, 0,
1096 &yy_ei.step_delay_fixed
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1101 &xx_ei.step_delay_random, 0,
1102 &yy_ei.step_delay_random
1107 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1108 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1113 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1114 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1115 &yy_ei.move_direction_initial
1119 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1120 &xx_ei.move_stepsize, TILEX / 8,
1121 &yy_ei.move_stepsize
1126 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1127 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1128 &yy_ei.move_enter_element
1132 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1133 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1134 &yy_ei.move_leave_element
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1139 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1140 &yy_ei.move_leave_type
1145 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1146 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1147 &yy_ei.slippery_type
1152 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1153 &xx_ei.explosion_type, EXPLODES_3X3,
1154 &yy_ei.explosion_type
1158 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1159 &xx_ei.explosion_delay, 16,
1160 &yy_ei.explosion_delay
1164 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1165 &xx_ei.ignition_delay, 8,
1166 &yy_ei.ignition_delay
1171 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1172 &xx_ei.content, EL_EMPTY_SPACE,
1174 &xx_num_contents, 1, 1
1177 // ---------- "num_change_pages" must be the last entry ---------------------
1180 -1, SAVE_CONF_ALWAYS,
1181 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1182 &xx_ei.num_change_pages, 1,
1183 &yy_ei.num_change_pages
1194 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1196 // ---------- "current_change_page" must be the first entry -----------------
1199 -1, SAVE_CONF_ALWAYS,
1200 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1201 &xx_current_change_page, -1
1204 // ---------- (the remaining entries can be in any order) -------------------
1208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1209 &xx_change.can_change, FALSE
1214 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1215 &xx_event_bits[0], 0
1219 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1220 &xx_event_bits[1], 0
1225 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1226 &xx_change.trigger_player, CH_PLAYER_ANY
1230 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1231 &xx_change.trigger_side, CH_SIDE_ANY
1235 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1236 &xx_change.trigger_page, CH_PAGE_ANY
1241 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1242 &xx_change.target_element, EL_EMPTY_SPACE
1247 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 &xx_change.delay_fixed, 0
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1253 &xx_change.delay_random, 0
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1258 &xx_change.delay_frames, FRAMES_PER_SECOND
1263 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1264 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1269 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1270 &xx_change.explode, FALSE
1274 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1275 &xx_change.use_target_content, FALSE
1279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1280 &xx_change.only_if_complete, FALSE
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1285 &xx_change.use_random_replace, FALSE
1289 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1290 &xx_change.random_percentage, 100
1294 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1295 &xx_change.replace_when, CP_WHEN_EMPTY
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1301 &xx_change.has_action, FALSE
1305 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1306 &xx_change.action_type, CA_NO_ACTION
1310 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1311 &xx_change.action_mode, CA_MODE_UNDEFINED
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1316 &xx_change.action_arg, CA_ARG_UNDEFINED
1321 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1322 &xx_change.action_element, EL_EMPTY_SPACE
1327 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1328 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1329 &xx_num_contents, 1, 1
1339 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1343 TYPE_STRING, CONF_VALUE_BYTES(1),
1344 &xx_ei.description[0], -1, NULL,
1345 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1346 &xx_default_description[0]
1351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1352 &xx_ei.use_gfx_element, FALSE
1356 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1357 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1362 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1363 &xx_group.choice_mode, ANIM_RANDOM
1368 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1369 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1370 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1380 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1385 &xx_ei.use_gfx_element, FALSE
1389 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1390 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1400 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1405 &li.block_snap_field, TRUE
1409 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1410 &li.continuous_snapping, TRUE
1414 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1420 &li.use_start_element[0], FALSE
1424 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1425 &li.start_element[0], EL_PLAYER_1
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1430 &li.use_artwork_element[0], FALSE
1434 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1435 &li.artwork_element[0], EL_PLAYER_1
1439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1440 &li.use_explosion_element[0], FALSE
1444 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1445 &li.explosion_element[0], EL_PLAYER_1
1460 filetype_id_list[] =
1462 { LEVEL_FILE_TYPE_RND, "RND" },
1463 { LEVEL_FILE_TYPE_BD, "BD" },
1464 { LEVEL_FILE_TYPE_EM, "EM" },
1465 { LEVEL_FILE_TYPE_SP, "SP" },
1466 { LEVEL_FILE_TYPE_DX, "DX" },
1467 { LEVEL_FILE_TYPE_SB, "SB" },
1468 { LEVEL_FILE_TYPE_DC, "DC" },
1469 { LEVEL_FILE_TYPE_MM, "MM" },
1470 { LEVEL_FILE_TYPE_MM, "DF" },
1475 // ============================================================================
1476 // level file functions
1477 // ============================================================================
1479 static boolean check_special_flags(char *flag)
1481 if (strEqual(options.special_flags, flag) ||
1482 strEqual(leveldir_current->special_flags, flag))
1488 static struct DateInfo getCurrentDate(void)
1490 time_t epoch_seconds = time(NULL);
1491 struct tm *now = localtime(&epoch_seconds);
1492 struct DateInfo date;
1494 date.year = now->tm_year + 1900;
1495 date.month = now->tm_mon + 1;
1496 date.day = now->tm_mday;
1498 date.src = DATE_SRC_CLOCK;
1503 static void resetEventFlags(struct ElementChangeInfo *change)
1507 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1508 change->has_event[i] = FALSE;
1511 static void resetEventBits(void)
1515 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1516 xx_event_bits[i] = 0;
1519 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1523 /* important: only change event flag if corresponding event bit is set
1524 (this is because all xx_event_bits[] values are loaded separately,
1525 and all xx_event_bits[] values are set back to zero before loading
1526 another value xx_event_bits[x] (each value representing 32 flags)) */
1528 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1529 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1530 change->has_event[i] = TRUE;
1533 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1537 /* in contrast to the above function setEventFlagsFromEventBits(), it
1538 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1539 depending on the corresponding change->has_event[i] values here, as
1540 all xx_event_bits[] values are reset in resetEventBits() before */
1542 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1543 if (change->has_event[i])
1544 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1547 static char *getDefaultElementDescription(struct ElementInfo *ei)
1549 static char description[MAX_ELEMENT_NAME_LEN + 1];
1550 char *default_description = (ei->custom_description != NULL ?
1551 ei->custom_description :
1552 ei->editor_description);
1555 // always start with reliable default values
1556 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1557 description[i] = '\0';
1559 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1560 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1562 return &description[0];
1565 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1567 char *default_description = getDefaultElementDescription(ei);
1570 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1571 ei->description[i] = default_description[i];
1574 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1578 for (i = 0; conf[i].data_type != -1; i++)
1580 int default_value = conf[i].default_value;
1581 int data_type = conf[i].data_type;
1582 int conf_type = conf[i].conf_type;
1583 int byte_mask = conf_type & CONF_MASK_BYTES;
1585 if (byte_mask == CONF_MASK_MULTI_BYTES)
1587 int default_num_entities = conf[i].default_num_entities;
1588 int max_num_entities = conf[i].max_num_entities;
1590 *(int *)(conf[i].num_entities) = default_num_entities;
1592 if (data_type == TYPE_STRING)
1594 char *default_string = conf[i].default_string;
1595 char *string = (char *)(conf[i].value);
1597 strncpy(string, default_string, max_num_entities);
1599 else if (data_type == TYPE_ELEMENT_LIST)
1601 int *element_array = (int *)(conf[i].value);
1604 for (j = 0; j < max_num_entities; j++)
1605 element_array[j] = default_value;
1607 else if (data_type == TYPE_CONTENT_LIST)
1609 struct Content *content = (struct Content *)(conf[i].value);
1612 for (c = 0; c < max_num_entities; c++)
1613 for (y = 0; y < 3; y++)
1614 for (x = 0; x < 3; x++)
1615 content[c].e[x][y] = default_value;
1618 else // constant size configuration data (1, 2 or 4 bytes)
1620 if (data_type == TYPE_BOOLEAN)
1621 *(boolean *)(conf[i].value) = default_value;
1623 *(int *) (conf[i].value) = default_value;
1628 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1632 for (i = 0; conf[i].data_type != -1; i++)
1634 int data_type = conf[i].data_type;
1635 int conf_type = conf[i].conf_type;
1636 int byte_mask = conf_type & CONF_MASK_BYTES;
1638 if (byte_mask == CONF_MASK_MULTI_BYTES)
1640 int max_num_entities = conf[i].max_num_entities;
1642 if (data_type == TYPE_STRING)
1644 char *string = (char *)(conf[i].value);
1645 char *string_copy = (char *)(conf[i].value_copy);
1647 strncpy(string_copy, string, max_num_entities);
1649 else if (data_type == TYPE_ELEMENT_LIST)
1651 int *element_array = (int *)(conf[i].value);
1652 int *element_array_copy = (int *)(conf[i].value_copy);
1655 for (j = 0; j < max_num_entities; j++)
1656 element_array_copy[j] = element_array[j];
1658 else if (data_type == TYPE_CONTENT_LIST)
1660 struct Content *content = (struct Content *)(conf[i].value);
1661 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1664 for (c = 0; c < max_num_entities; c++)
1665 for (y = 0; y < 3; y++)
1666 for (x = 0; x < 3; x++)
1667 content_copy[c].e[x][y] = content[c].e[x][y];
1670 else // constant size configuration data (1, 2 or 4 bytes)
1672 if (data_type == TYPE_BOOLEAN)
1673 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1675 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1680 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1684 xx_ei = *ei_from; // copy element data into temporary buffer
1685 yy_ei = *ei_to; // copy element data into temporary buffer
1687 copyConfigFromConfigList(chunk_config_CUSX_base);
1692 // ---------- reinitialize and copy change pages ----------
1694 ei_to->num_change_pages = ei_from->num_change_pages;
1695 ei_to->current_change_page = ei_from->current_change_page;
1697 setElementChangePages(ei_to, ei_to->num_change_pages);
1699 for (i = 0; i < ei_to->num_change_pages; i++)
1700 ei_to->change_page[i] = ei_from->change_page[i];
1702 // ---------- copy group element info ----------
1703 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1704 *ei_to->group = *ei_from->group;
1706 // mark this custom element as modified
1707 ei_to->modified_settings = TRUE;
1710 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1712 int change_page_size = sizeof(struct ElementChangeInfo);
1714 ei->num_change_pages = MAX(1, change_pages);
1717 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1719 if (ei->current_change_page >= ei->num_change_pages)
1720 ei->current_change_page = ei->num_change_pages - 1;
1722 ei->change = &ei->change_page[ei->current_change_page];
1725 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1727 xx_change = *change; // copy change data into temporary buffer
1729 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1731 *change = xx_change;
1733 resetEventFlags(change);
1735 change->direct_action = 0;
1736 change->other_action = 0;
1738 change->pre_change_function = NULL;
1739 change->change_function = NULL;
1740 change->post_change_function = NULL;
1743 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1747 li = *level; // copy level data into temporary buffer
1748 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1749 *level = li; // copy temporary buffer back to level data
1751 setLevelInfoToDefaults_EM();
1752 setLevelInfoToDefaults_SP();
1753 setLevelInfoToDefaults_MM();
1755 level->native_em_level = &native_em_level;
1756 level->native_sp_level = &native_sp_level;
1757 level->native_mm_level = &native_mm_level;
1759 level->file_version = FILE_VERSION_ACTUAL;
1760 level->game_version = GAME_VERSION_ACTUAL;
1762 level->creation_date = getCurrentDate();
1764 level->encoding_16bit_field = TRUE;
1765 level->encoding_16bit_yamyam = TRUE;
1766 level->encoding_16bit_amoeba = TRUE;
1768 // clear level name and level author string buffers
1769 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1770 level->name[i] = '\0';
1771 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1772 level->author[i] = '\0';
1774 // set level name and level author to default values
1775 strcpy(level->name, NAMELESS_LEVEL_NAME);
1776 strcpy(level->author, ANONYMOUS_NAME);
1778 // set level playfield to playable default level with player and exit
1779 for (x = 0; x < MAX_LEV_FIELDX; x++)
1780 for (y = 0; y < MAX_LEV_FIELDY; y++)
1781 level->field[x][y] = EL_SAND;
1783 level->field[0][0] = EL_PLAYER_1;
1784 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1786 BorderElement = EL_STEELWALL;
1788 // detect custom elements when loading them
1789 level->file_has_custom_elements = FALSE;
1791 // set all bug compatibility flags to "false" => do not emulate this bug
1792 level->use_action_after_change_bug = FALSE;
1794 if (leveldir_current)
1796 // try to determine better author name than 'anonymous'
1797 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1799 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1800 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1804 switch (LEVELCLASS(leveldir_current))
1806 case LEVELCLASS_TUTORIAL:
1807 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1810 case LEVELCLASS_CONTRIB:
1811 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1812 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1815 case LEVELCLASS_PRIVATE:
1816 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1817 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1821 // keep default value
1828 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1830 static boolean clipboard_elements_initialized = FALSE;
1833 InitElementPropertiesStatic();
1835 li = *level; // copy level data into temporary buffer
1836 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1837 *level = li; // copy temporary buffer back to level data
1839 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1842 struct ElementInfo *ei = &element_info[element];
1844 // never initialize clipboard elements after the very first time
1845 // (to be able to use clipboard elements between several levels)
1846 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1849 if (IS_ENVELOPE(element))
1851 int envelope_nr = element - EL_ENVELOPE_1;
1853 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1855 level->envelope[envelope_nr] = xx_envelope;
1858 if (IS_CUSTOM_ELEMENT(element) ||
1859 IS_GROUP_ELEMENT(element) ||
1860 IS_INTERNAL_ELEMENT(element))
1862 xx_ei = *ei; // copy element data into temporary buffer
1864 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1869 setElementChangePages(ei, 1);
1870 setElementChangeInfoToDefaults(ei->change);
1872 if (IS_CUSTOM_ELEMENT(element) ||
1873 IS_GROUP_ELEMENT(element) ||
1874 IS_INTERNAL_ELEMENT(element))
1876 setElementDescriptionToDefault(ei);
1878 ei->modified_settings = FALSE;
1881 if (IS_CUSTOM_ELEMENT(element) ||
1882 IS_INTERNAL_ELEMENT(element))
1884 // internal values used in level editor
1886 ei->access_type = 0;
1887 ei->access_layer = 0;
1888 ei->access_protected = 0;
1889 ei->walk_to_action = 0;
1890 ei->smash_targets = 0;
1893 ei->can_explode_by_fire = FALSE;
1894 ei->can_explode_smashed = FALSE;
1895 ei->can_explode_impact = FALSE;
1897 ei->current_change_page = 0;
1900 if (IS_GROUP_ELEMENT(element) ||
1901 IS_INTERNAL_ELEMENT(element))
1903 struct ElementGroupInfo *group;
1905 // initialize memory for list of elements in group
1906 if (ei->group == NULL)
1907 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1911 xx_group = *group; // copy group data into temporary buffer
1913 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1918 if (IS_EMPTY_ELEMENT(element) ||
1919 IS_INTERNAL_ELEMENT(element))
1921 xx_ei = *ei; // copy element data into temporary buffer
1923 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1929 clipboard_elements_initialized = TRUE;
1932 static void setLevelInfoToDefaults(struct LevelInfo *level,
1933 boolean level_info_only,
1934 boolean reset_file_status)
1936 setLevelInfoToDefaults_Level(level);
1938 if (!level_info_only)
1939 setLevelInfoToDefaults_Elements(level);
1941 if (reset_file_status)
1943 level->no_valid_file = FALSE;
1944 level->no_level_file = FALSE;
1947 level->changed = FALSE;
1950 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1952 level_file_info->nr = 0;
1953 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1954 level_file_info->packed = FALSE;
1956 setString(&level_file_info->basename, NULL);
1957 setString(&level_file_info->filename, NULL);
1960 int getMappedElement_SB(int, boolean);
1962 static void ActivateLevelTemplate(void)
1966 if (check_special_flags("load_xsb_to_ces"))
1968 // fill smaller playfields with padding "beyond border wall" elements
1969 if (level.fieldx < level_template.fieldx ||
1970 level.fieldy < level_template.fieldy)
1972 short field[level.fieldx][level.fieldy];
1973 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1974 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1975 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1976 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1978 // copy old playfield (which is smaller than the visible area)
1979 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1980 field[x][y] = level.field[x][y];
1982 // fill new, larger playfield with "beyond border wall" elements
1983 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1984 level.field[x][y] = getMappedElement_SB('_', TRUE);
1986 // copy the old playfield to the middle of the new playfield
1987 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1988 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1990 level.fieldx = new_fieldx;
1991 level.fieldy = new_fieldy;
1995 // Currently there is no special action needed to activate the template
1996 // data, because 'element_info' property settings overwrite the original
1997 // level data, while all other variables do not change.
1999 // Exception: 'from_level_template' elements in the original level playfield
2000 // are overwritten with the corresponding elements at the same position in
2001 // playfield from the level template.
2003 for (x = 0; x < level.fieldx; x++)
2004 for (y = 0; y < level.fieldy; y++)
2005 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2006 level.field[x][y] = level_template.field[x][y];
2008 if (check_special_flags("load_xsb_to_ces"))
2010 struct LevelInfo level_backup = level;
2012 // overwrite all individual level settings from template level settings
2013 level = level_template;
2015 // restore level file info
2016 level.file_info = level_backup.file_info;
2018 // restore playfield size
2019 level.fieldx = level_backup.fieldx;
2020 level.fieldy = level_backup.fieldy;
2022 // restore playfield content
2023 for (x = 0; x < level.fieldx; x++)
2024 for (y = 0; y < level.fieldy; y++)
2025 level.field[x][y] = level_backup.field[x][y];
2027 // restore name and author from individual level
2028 strcpy(level.name, level_backup.name);
2029 strcpy(level.author, level_backup.author);
2031 // restore flag "use_custom_template"
2032 level.use_custom_template = level_backup.use_custom_template;
2036 static char *getLevelFilenameFromBasename(char *basename)
2038 static char *filename = NULL;
2040 checked_free(filename);
2042 filename = getPath2(getCurrentLevelDir(), basename);
2047 static int getFileTypeFromBasename(char *basename)
2049 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2051 static char *filename = NULL;
2052 struct stat file_status;
2054 // ---------- try to determine file type from filename ----------
2056 // check for typical filename of a Supaplex level package file
2057 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2058 return LEVEL_FILE_TYPE_SP;
2060 // check for typical filename of a Diamond Caves II level package file
2061 if (strSuffixLower(basename, ".dc") ||
2062 strSuffixLower(basename, ".dc2"))
2063 return LEVEL_FILE_TYPE_DC;
2065 // check for typical filename of a Sokoban level package file
2066 if (strSuffixLower(basename, ".xsb") &&
2067 strchr(basename, '%') == NULL)
2068 return LEVEL_FILE_TYPE_SB;
2070 // ---------- try to determine file type from filesize ----------
2072 checked_free(filename);
2073 filename = getPath2(getCurrentLevelDir(), basename);
2075 if (stat(filename, &file_status) == 0)
2077 // check for typical filesize of a Supaplex level package file
2078 if (file_status.st_size == 170496)
2079 return LEVEL_FILE_TYPE_SP;
2082 return LEVEL_FILE_TYPE_UNKNOWN;
2085 static int getFileTypeFromMagicBytes(char *filename, int type)
2089 if ((file = openFile(filename, MODE_READ)))
2091 char chunk_name[CHUNK_ID_LEN + 1];
2093 getFileChunkBE(file, chunk_name, NULL);
2095 if (strEqual(chunk_name, "MMII") ||
2096 strEqual(chunk_name, "MIRR"))
2097 type = LEVEL_FILE_TYPE_MM;
2105 static boolean checkForPackageFromBasename(char *basename)
2107 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2108 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2110 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2113 static char *getSingleLevelBasenameExt(int nr, char *extension)
2115 static char basename[MAX_FILENAME_LEN];
2118 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2120 sprintf(basename, "%03d.%s", nr, extension);
2125 static char *getSingleLevelBasename(int nr)
2127 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2130 static char *getPackedLevelBasename(int type)
2132 static char basename[MAX_FILENAME_LEN];
2133 char *directory = getCurrentLevelDir();
2135 DirectoryEntry *dir_entry;
2137 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2139 if ((dir = openDirectory(directory)) == NULL)
2141 Warn("cannot read current level directory '%s'", directory);
2146 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2148 char *entry_basename = dir_entry->basename;
2149 int entry_type = getFileTypeFromBasename(entry_basename);
2151 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2153 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2156 strcpy(basename, entry_basename);
2163 closeDirectory(dir);
2168 static char *getSingleLevelFilename(int nr)
2170 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2173 #if ENABLE_UNUSED_CODE
2174 static char *getPackedLevelFilename(int type)
2176 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2180 char *getDefaultLevelFilename(int nr)
2182 return getSingleLevelFilename(nr);
2185 #if ENABLE_UNUSED_CODE
2186 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2190 lfi->packed = FALSE;
2192 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2193 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2197 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2198 int type, char *format, ...)
2200 static char basename[MAX_FILENAME_LEN];
2203 va_start(ap, format);
2204 vsprintf(basename, format, ap);
2208 lfi->packed = FALSE;
2210 setString(&lfi->basename, basename);
2211 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2214 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2220 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2221 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2224 static int getFiletypeFromID(char *filetype_id)
2226 char *filetype_id_lower;
2227 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2230 if (filetype_id == NULL)
2231 return LEVEL_FILE_TYPE_UNKNOWN;
2233 filetype_id_lower = getStringToLower(filetype_id);
2235 for (i = 0; filetype_id_list[i].id != NULL; i++)
2237 char *id_lower = getStringToLower(filetype_id_list[i].id);
2239 if (strEqual(filetype_id_lower, id_lower))
2240 filetype = filetype_id_list[i].filetype;
2244 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2248 free(filetype_id_lower);
2253 char *getLocalLevelTemplateFilename(void)
2255 return getDefaultLevelFilename(-1);
2258 char *getGlobalLevelTemplateFilename(void)
2260 // global variable "leveldir_current" must be modified in the loop below
2261 LevelDirTree *leveldir_current_last = leveldir_current;
2262 char *filename = NULL;
2264 // check for template level in path from current to topmost tree node
2266 while (leveldir_current != NULL)
2268 filename = getDefaultLevelFilename(-1);
2270 if (fileExists(filename))
2273 leveldir_current = leveldir_current->node_parent;
2276 // restore global variable "leveldir_current" modified in above loop
2277 leveldir_current = leveldir_current_last;
2282 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2286 // special case: level number is negative => check for level template file
2289 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2290 getSingleLevelBasename(-1));
2292 // replace local level template filename with global template filename
2293 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2295 // no fallback if template file not existing
2299 // special case: check for file name/pattern specified in "levelinfo.conf"
2300 if (leveldir_current->level_filename != NULL)
2302 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2304 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2305 leveldir_current->level_filename, nr);
2307 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2309 if (fileExists(lfi->filename))
2312 else if (leveldir_current->level_filetype != NULL)
2314 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2316 // check for specified native level file with standard file name
2317 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2318 "%03d.%s", nr, LEVELFILE_EXTENSION);
2319 if (fileExists(lfi->filename))
2323 // check for native Rocks'n'Diamonds level file
2324 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2325 "%03d.%s", nr, LEVELFILE_EXTENSION);
2326 if (fileExists(lfi->filename))
2329 // check for Emerald Mine level file (V1)
2330 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2331 'a' + (nr / 10) % 26, '0' + nr % 10);
2332 if (fileExists(lfi->filename))
2334 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2335 'A' + (nr / 10) % 26, '0' + nr % 10);
2336 if (fileExists(lfi->filename))
2339 // check for Emerald Mine level file (V2 to V5)
2340 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2341 if (fileExists(lfi->filename))
2344 // check for Emerald Mine level file (V6 / single mode)
2345 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2346 if (fileExists(lfi->filename))
2348 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2349 if (fileExists(lfi->filename))
2352 // check for Emerald Mine level file (V6 / teamwork mode)
2353 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2354 if (fileExists(lfi->filename))
2356 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2357 if (fileExists(lfi->filename))
2360 // check for various packed level file formats
2361 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2362 if (fileExists(lfi->filename))
2365 // no known level file found -- use default values (and fail later)
2366 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2367 "%03d.%s", nr, LEVELFILE_EXTENSION);
2370 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2372 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2373 lfi->type = getFileTypeFromBasename(lfi->basename);
2375 if (lfi->type == LEVEL_FILE_TYPE_RND)
2376 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2379 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2381 // always start with reliable default values
2382 setFileInfoToDefaults(level_file_info);
2384 level_file_info->nr = nr; // set requested level number
2386 determineLevelFileInfo_Filename(level_file_info);
2387 determineLevelFileInfo_Filetype(level_file_info);
2390 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2391 struct LevelFileInfo *lfi_to)
2393 lfi_to->nr = lfi_from->nr;
2394 lfi_to->type = lfi_from->type;
2395 lfi_to->packed = lfi_from->packed;
2397 setString(&lfi_to->basename, lfi_from->basename);
2398 setString(&lfi_to->filename, lfi_from->filename);
2401 // ----------------------------------------------------------------------------
2402 // functions for loading R'n'D level
2403 // ----------------------------------------------------------------------------
2405 int getMappedElement(int element)
2407 // remap some (historic, now obsolete) elements
2411 case EL_PLAYER_OBSOLETE:
2412 element = EL_PLAYER_1;
2415 case EL_KEY_OBSOLETE:
2419 case EL_EM_KEY_1_FILE_OBSOLETE:
2420 element = EL_EM_KEY_1;
2423 case EL_EM_KEY_2_FILE_OBSOLETE:
2424 element = EL_EM_KEY_2;
2427 case EL_EM_KEY_3_FILE_OBSOLETE:
2428 element = EL_EM_KEY_3;
2431 case EL_EM_KEY_4_FILE_OBSOLETE:
2432 element = EL_EM_KEY_4;
2435 case EL_ENVELOPE_OBSOLETE:
2436 element = EL_ENVELOPE_1;
2444 if (element >= NUM_FILE_ELEMENTS)
2446 Warn("invalid level element %d", element);
2448 element = EL_UNKNOWN;
2456 static int getMappedElementByVersion(int element, int game_version)
2458 // remap some elements due to certain game version
2460 if (game_version <= VERSION_IDENT(2,2,0,0))
2462 // map game font elements
2463 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2464 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2465 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2466 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2469 if (game_version < VERSION_IDENT(3,0,0,0))
2471 // map Supaplex gravity tube elements
2472 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2473 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2474 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2475 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2482 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2484 level->file_version = getFileVersion(file);
2485 level->game_version = getFileVersion(file);
2490 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2492 level->creation_date.year = getFile16BitBE(file);
2493 level->creation_date.month = getFile8Bit(file);
2494 level->creation_date.day = getFile8Bit(file);
2496 level->creation_date.src = DATE_SRC_LEVELFILE;
2501 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2503 int initial_player_stepsize;
2504 int initial_player_gravity;
2507 level->fieldx = getFile8Bit(file);
2508 level->fieldy = getFile8Bit(file);
2510 level->time = getFile16BitBE(file);
2511 level->gems_needed = getFile16BitBE(file);
2513 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2514 level->name[i] = getFile8Bit(file);
2515 level->name[MAX_LEVEL_NAME_LEN] = 0;
2517 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2518 level->score[i] = getFile8Bit(file);
2520 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2521 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2522 for (y = 0; y < 3; y++)
2523 for (x = 0; x < 3; x++)
2524 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2526 level->amoeba_speed = getFile8Bit(file);
2527 level->time_magic_wall = getFile8Bit(file);
2528 level->time_wheel = getFile8Bit(file);
2529 level->amoeba_content = getMappedElement(getFile8Bit(file));
2531 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2534 for (i = 0; i < MAX_PLAYERS; i++)
2535 level->initial_player_stepsize[i] = initial_player_stepsize;
2537 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2539 for (i = 0; i < MAX_PLAYERS; i++)
2540 level->initial_player_gravity[i] = initial_player_gravity;
2542 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2543 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2545 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2547 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2548 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2549 level->can_move_into_acid_bits = getFile32BitBE(file);
2550 level->dont_collide_with_bits = getFile8Bit(file);
2552 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2553 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2555 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2556 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2557 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2559 level->game_engine_type = getFile8Bit(file);
2561 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2566 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2570 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2571 level->name[i] = getFile8Bit(file);
2572 level->name[MAX_LEVEL_NAME_LEN] = 0;
2577 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2581 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2582 level->author[i] = getFile8Bit(file);
2583 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2588 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2591 int chunk_size_expected = level->fieldx * level->fieldy;
2593 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2594 stored with 16-bit encoding (and should be twice as big then).
2595 Even worse, playfield data was stored 16-bit when only yamyam content
2596 contained 16-bit elements and vice versa. */
2598 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2599 chunk_size_expected *= 2;
2601 if (chunk_size_expected != chunk_size)
2603 ReadUnusedBytesFromFile(file, chunk_size);
2604 return chunk_size_expected;
2607 for (y = 0; y < level->fieldy; y++)
2608 for (x = 0; x < level->fieldx; x++)
2609 level->field[x][y] =
2610 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2615 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2618 int header_size = 4;
2619 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2620 int chunk_size_expected = header_size + content_size;
2622 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2623 stored with 16-bit encoding (and should be twice as big then).
2624 Even worse, playfield data was stored 16-bit when only yamyam content
2625 contained 16-bit elements and vice versa. */
2627 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2628 chunk_size_expected += content_size;
2630 if (chunk_size_expected != chunk_size)
2632 ReadUnusedBytesFromFile(file, chunk_size);
2633 return chunk_size_expected;
2637 level->num_yamyam_contents = getFile8Bit(file);
2641 // correct invalid number of content fields -- should never happen
2642 if (level->num_yamyam_contents < 1 ||
2643 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2644 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2646 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2647 for (y = 0; y < 3; y++)
2648 for (x = 0; x < 3; x++)
2649 level->yamyam_content[i].e[x][y] =
2650 getMappedElement(level->encoding_16bit_field ?
2651 getFile16BitBE(file) : getFile8Bit(file));
2655 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2660 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2662 element = getMappedElement(getFile16BitBE(file));
2663 num_contents = getFile8Bit(file);
2665 getFile8Bit(file); // content x size (unused)
2666 getFile8Bit(file); // content y size (unused)
2668 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2671 for (y = 0; y < 3; y++)
2672 for (x = 0; x < 3; x++)
2673 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2675 // correct invalid number of content fields -- should never happen
2676 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2677 num_contents = STD_ELEMENT_CONTENTS;
2679 if (element == EL_YAMYAM)
2681 level->num_yamyam_contents = num_contents;
2683 for (i = 0; i < num_contents; i++)
2684 for (y = 0; y < 3; y++)
2685 for (x = 0; x < 3; x++)
2686 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2688 else if (element == EL_BD_AMOEBA)
2690 level->amoeba_content = content_array[0][0][0];
2694 Warn("cannot load content for element '%d'", element);
2700 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2706 int chunk_size_expected;
2708 element = getMappedElement(getFile16BitBE(file));
2709 if (!IS_ENVELOPE(element))
2710 element = EL_ENVELOPE_1;
2712 envelope_nr = element - EL_ENVELOPE_1;
2714 envelope_len = getFile16BitBE(file);
2716 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2717 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2719 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2721 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2722 if (chunk_size_expected != chunk_size)
2724 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2725 return chunk_size_expected;
2728 for (i = 0; i < envelope_len; i++)
2729 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2734 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2736 int num_changed_custom_elements = getFile16BitBE(file);
2737 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2740 if (chunk_size_expected != chunk_size)
2742 ReadUnusedBytesFromFile(file, chunk_size - 2);
2743 return chunk_size_expected;
2746 for (i = 0; i < num_changed_custom_elements; i++)
2748 int element = getMappedElement(getFile16BitBE(file));
2749 int properties = getFile32BitBE(file);
2751 if (IS_CUSTOM_ELEMENT(element))
2752 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2754 Warn("invalid custom element number %d", element);
2756 // older game versions that wrote level files with CUS1 chunks used
2757 // different default push delay values (not yet stored in level file)
2758 element_info[element].push_delay_fixed = 2;
2759 element_info[element].push_delay_random = 8;
2762 level->file_has_custom_elements = TRUE;
2767 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2769 int num_changed_custom_elements = getFile16BitBE(file);
2770 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2773 if (chunk_size_expected != chunk_size)
2775 ReadUnusedBytesFromFile(file, chunk_size - 2);
2776 return chunk_size_expected;
2779 for (i = 0; i < num_changed_custom_elements; i++)
2781 int element = getMappedElement(getFile16BitBE(file));
2782 int custom_target_element = getMappedElement(getFile16BitBE(file));
2784 if (IS_CUSTOM_ELEMENT(element))
2785 element_info[element].change->target_element = custom_target_element;
2787 Warn("invalid custom element number %d", element);
2790 level->file_has_custom_elements = TRUE;
2795 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2797 int num_changed_custom_elements = getFile16BitBE(file);
2798 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2801 if (chunk_size_expected != chunk_size)
2803 ReadUnusedBytesFromFile(file, chunk_size - 2);
2804 return chunk_size_expected;
2807 for (i = 0; i < num_changed_custom_elements; i++)
2809 int element = getMappedElement(getFile16BitBE(file));
2810 struct ElementInfo *ei = &element_info[element];
2811 unsigned int event_bits;
2813 if (!IS_CUSTOM_ELEMENT(element))
2815 Warn("invalid custom element number %d", element);
2817 element = EL_INTERNAL_DUMMY;
2820 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2821 ei->description[j] = getFile8Bit(file);
2822 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2824 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2826 // some free bytes for future properties and padding
2827 ReadUnusedBytesFromFile(file, 7);
2829 ei->use_gfx_element = getFile8Bit(file);
2830 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2832 ei->collect_score_initial = getFile8Bit(file);
2833 ei->collect_count_initial = getFile8Bit(file);
2835 ei->push_delay_fixed = getFile16BitBE(file);
2836 ei->push_delay_random = getFile16BitBE(file);
2837 ei->move_delay_fixed = getFile16BitBE(file);
2838 ei->move_delay_random = getFile16BitBE(file);
2840 ei->move_pattern = getFile16BitBE(file);
2841 ei->move_direction_initial = getFile8Bit(file);
2842 ei->move_stepsize = getFile8Bit(file);
2844 for (y = 0; y < 3; y++)
2845 for (x = 0; x < 3; x++)
2846 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2848 // bits 0 - 31 of "has_event[]"
2849 event_bits = getFile32BitBE(file);
2850 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2851 if (event_bits & (1 << j))
2852 ei->change->has_event[j] = TRUE;
2854 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2856 ei->change->delay_fixed = getFile16BitBE(file);
2857 ei->change->delay_random = getFile16BitBE(file);
2858 ei->change->delay_frames = getFile16BitBE(file);
2860 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2862 ei->change->explode = getFile8Bit(file);
2863 ei->change->use_target_content = getFile8Bit(file);
2864 ei->change->only_if_complete = getFile8Bit(file);
2865 ei->change->use_random_replace = getFile8Bit(file);
2867 ei->change->random_percentage = getFile8Bit(file);
2868 ei->change->replace_when = getFile8Bit(file);
2870 for (y = 0; y < 3; y++)
2871 for (x = 0; x < 3; x++)
2872 ei->change->target_content.e[x][y] =
2873 getMappedElement(getFile16BitBE(file));
2875 ei->slippery_type = getFile8Bit(file);
2877 // some free bytes for future properties and padding
2878 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2880 // mark that this custom element has been modified
2881 ei->modified_settings = TRUE;
2884 level->file_has_custom_elements = TRUE;
2889 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2891 struct ElementInfo *ei;
2892 int chunk_size_expected;
2896 // ---------- custom element base property values (96 bytes) ----------------
2898 element = getMappedElement(getFile16BitBE(file));
2900 if (!IS_CUSTOM_ELEMENT(element))
2902 Warn("invalid custom element number %d", element);
2904 ReadUnusedBytesFromFile(file, chunk_size - 2);
2909 ei = &element_info[element];
2911 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2912 ei->description[i] = getFile8Bit(file);
2913 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2915 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2917 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2919 ei->num_change_pages = getFile8Bit(file);
2921 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2922 if (chunk_size_expected != chunk_size)
2924 ReadUnusedBytesFromFile(file, chunk_size - 43);
2925 return chunk_size_expected;
2928 ei->ce_value_fixed_initial = getFile16BitBE(file);
2929 ei->ce_value_random_initial = getFile16BitBE(file);
2930 ei->use_last_ce_value = getFile8Bit(file);
2932 ei->use_gfx_element = getFile8Bit(file);
2933 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2935 ei->collect_score_initial = getFile8Bit(file);
2936 ei->collect_count_initial = getFile8Bit(file);
2938 ei->drop_delay_fixed = getFile8Bit(file);
2939 ei->push_delay_fixed = getFile8Bit(file);
2940 ei->drop_delay_random = getFile8Bit(file);
2941 ei->push_delay_random = getFile8Bit(file);
2942 ei->move_delay_fixed = getFile16BitBE(file);
2943 ei->move_delay_random = getFile16BitBE(file);
2945 // bits 0 - 15 of "move_pattern" ...
2946 ei->move_pattern = getFile16BitBE(file);
2947 ei->move_direction_initial = getFile8Bit(file);
2948 ei->move_stepsize = getFile8Bit(file);
2950 ei->slippery_type = getFile8Bit(file);
2952 for (y = 0; y < 3; y++)
2953 for (x = 0; x < 3; x++)
2954 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2956 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2957 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2958 ei->move_leave_type = getFile8Bit(file);
2960 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2961 ei->move_pattern |= (getFile16BitBE(file) << 16);
2963 ei->access_direction = getFile8Bit(file);
2965 ei->explosion_delay = getFile8Bit(file);
2966 ei->ignition_delay = getFile8Bit(file);
2967 ei->explosion_type = getFile8Bit(file);
2969 // some free bytes for future custom property values and padding
2970 ReadUnusedBytesFromFile(file, 1);
2972 // ---------- change page property values (48 bytes) ------------------------
2974 setElementChangePages(ei, ei->num_change_pages);
2976 for (i = 0; i < ei->num_change_pages; i++)
2978 struct ElementChangeInfo *change = &ei->change_page[i];
2979 unsigned int event_bits;
2981 // always start with reliable default values
2982 setElementChangeInfoToDefaults(change);
2984 // bits 0 - 31 of "has_event[]" ...
2985 event_bits = getFile32BitBE(file);
2986 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2987 if (event_bits & (1 << j))
2988 change->has_event[j] = TRUE;
2990 change->target_element = getMappedElement(getFile16BitBE(file));
2992 change->delay_fixed = getFile16BitBE(file);
2993 change->delay_random = getFile16BitBE(file);
2994 change->delay_frames = getFile16BitBE(file);
2996 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2998 change->explode = getFile8Bit(file);
2999 change->use_target_content = getFile8Bit(file);
3000 change->only_if_complete = getFile8Bit(file);
3001 change->use_random_replace = getFile8Bit(file);
3003 change->random_percentage = getFile8Bit(file);
3004 change->replace_when = getFile8Bit(file);
3006 for (y = 0; y < 3; y++)
3007 for (x = 0; x < 3; x++)
3008 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3010 change->can_change = getFile8Bit(file);
3012 change->trigger_side = getFile8Bit(file);
3014 change->trigger_player = getFile8Bit(file);
3015 change->trigger_page = getFile8Bit(file);
3017 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3018 CH_PAGE_ANY : (1 << change->trigger_page));
3020 change->has_action = getFile8Bit(file);
3021 change->action_type = getFile8Bit(file);
3022 change->action_mode = getFile8Bit(file);
3023 change->action_arg = getFile16BitBE(file);
3025 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3026 event_bits = getFile8Bit(file);
3027 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3028 if (event_bits & (1 << (j - 32)))
3029 change->has_event[j] = TRUE;
3032 // mark this custom element as modified
3033 ei->modified_settings = TRUE;
3035 level->file_has_custom_elements = TRUE;
3040 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3042 struct ElementInfo *ei;
3043 struct ElementGroupInfo *group;
3047 element = getMappedElement(getFile16BitBE(file));
3049 if (!IS_GROUP_ELEMENT(element))
3051 Warn("invalid group element number %d", element);
3053 ReadUnusedBytesFromFile(file, chunk_size - 2);
3058 ei = &element_info[element];
3060 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3061 ei->description[i] = getFile8Bit(file);
3062 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3064 group = element_info[element].group;
3066 group->num_elements = getFile8Bit(file);
3068 ei->use_gfx_element = getFile8Bit(file);
3069 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3071 group->choice_mode = getFile8Bit(file);
3073 // some free bytes for future values and padding
3074 ReadUnusedBytesFromFile(file, 3);
3076 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3077 group->element[i] = getMappedElement(getFile16BitBE(file));
3079 // mark this group element as modified
3080 element_info[element].modified_settings = TRUE;
3082 level->file_has_custom_elements = TRUE;
3087 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3088 int element, int real_element)
3090 int micro_chunk_size = 0;
3091 int conf_type = getFile8Bit(file);
3092 int byte_mask = conf_type & CONF_MASK_BYTES;
3093 boolean element_found = FALSE;
3096 micro_chunk_size += 1;
3098 if (byte_mask == CONF_MASK_MULTI_BYTES)
3100 int num_bytes = getFile16BitBE(file);
3101 byte *buffer = checked_malloc(num_bytes);
3103 ReadBytesFromFile(file, buffer, num_bytes);
3105 for (i = 0; conf[i].data_type != -1; i++)
3107 if (conf[i].element == element &&
3108 conf[i].conf_type == conf_type)
3110 int data_type = conf[i].data_type;
3111 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3112 int max_num_entities = conf[i].max_num_entities;
3114 if (num_entities > max_num_entities)
3116 Warn("truncating number of entities for element %d from %d to %d",
3117 element, num_entities, max_num_entities);
3119 num_entities = max_num_entities;
3122 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3123 data_type == TYPE_CONTENT_LIST))
3125 // for element and content lists, zero entities are not allowed
3126 Warn("found empty list of entities for element %d", element);
3128 // do not set "num_entities" here to prevent reading behind buffer
3130 *(int *)(conf[i].num_entities) = 1; // at least one is required
3134 *(int *)(conf[i].num_entities) = num_entities;
3137 element_found = TRUE;
3139 if (data_type == TYPE_STRING)
3141 char *string = (char *)(conf[i].value);
3144 for (j = 0; j < max_num_entities; j++)
3145 string[j] = (j < num_entities ? buffer[j] : '\0');
3147 else if (data_type == TYPE_ELEMENT_LIST)
3149 int *element_array = (int *)(conf[i].value);
3152 for (j = 0; j < num_entities; j++)
3154 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3156 else if (data_type == TYPE_CONTENT_LIST)
3158 struct Content *content= (struct Content *)(conf[i].value);
3161 for (c = 0; c < num_entities; c++)
3162 for (y = 0; y < 3; y++)
3163 for (x = 0; x < 3; x++)
3164 content[c].e[x][y] =
3165 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3168 element_found = FALSE;
3174 checked_free(buffer);
3176 micro_chunk_size += 2 + num_bytes;
3178 else // constant size configuration data (1, 2 or 4 bytes)
3180 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3181 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3182 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3184 for (i = 0; conf[i].data_type != -1; i++)
3186 if (conf[i].element == element &&
3187 conf[i].conf_type == conf_type)
3189 int data_type = conf[i].data_type;
3191 if (data_type == TYPE_ELEMENT)
3192 value = getMappedElement(value);
3194 if (data_type == TYPE_BOOLEAN)
3195 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3197 *(int *) (conf[i].value) = value;
3199 element_found = TRUE;
3205 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3210 char *error_conf_chunk_bytes =
3211 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3212 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3213 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3214 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3215 int error_element = real_element;
3217 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3218 error_conf_chunk_bytes, error_conf_chunk_token,
3219 error_element, EL_NAME(error_element));
3222 return micro_chunk_size;
3225 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3227 int real_chunk_size = 0;
3229 li = *level; // copy level data into temporary buffer
3231 while (!checkEndOfFile(file))
3233 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3235 if (real_chunk_size >= chunk_size)
3239 *level = li; // copy temporary buffer back to level data
3241 return real_chunk_size;
3244 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3246 int real_chunk_size = 0;
3248 li = *level; // copy level data into temporary buffer
3250 while (!checkEndOfFile(file))
3252 int element = getMappedElement(getFile16BitBE(file));
3254 real_chunk_size += 2;
3255 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3257 if (real_chunk_size >= chunk_size)
3261 *level = li; // copy temporary buffer back to level data
3263 return real_chunk_size;
3266 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3268 int real_chunk_size = 0;
3270 li = *level; // copy level data into temporary buffer
3272 while (!checkEndOfFile(file))
3274 int element = getMappedElement(getFile16BitBE(file));
3276 real_chunk_size += 2;
3277 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3279 if (real_chunk_size >= chunk_size)
3283 *level = li; // copy temporary buffer back to level data
3285 return real_chunk_size;
3288 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3290 int element = getMappedElement(getFile16BitBE(file));
3291 int envelope_nr = element - EL_ENVELOPE_1;
3292 int real_chunk_size = 2;
3294 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3296 while (!checkEndOfFile(file))
3298 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3301 if (real_chunk_size >= chunk_size)
3305 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3307 return real_chunk_size;
3310 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3312 int element = getMappedElement(getFile16BitBE(file));
3313 int real_chunk_size = 2;
3314 struct ElementInfo *ei = &element_info[element];
3317 xx_ei = *ei; // copy element data into temporary buffer
3319 xx_ei.num_change_pages = -1;
3321 while (!checkEndOfFile(file))
3323 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3325 if (xx_ei.num_change_pages != -1)
3328 if (real_chunk_size >= chunk_size)
3334 if (ei->num_change_pages == -1)
3336 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3339 ei->num_change_pages = 1;
3341 setElementChangePages(ei, 1);
3342 setElementChangeInfoToDefaults(ei->change);
3344 return real_chunk_size;
3347 // initialize number of change pages stored for this custom element
3348 setElementChangePages(ei, ei->num_change_pages);
3349 for (i = 0; i < ei->num_change_pages; i++)
3350 setElementChangeInfoToDefaults(&ei->change_page[i]);
3352 // start with reading properties for the first change page
3353 xx_current_change_page = 0;
3355 while (!checkEndOfFile(file))
3357 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3359 xx_change = *change; // copy change data into temporary buffer
3361 resetEventBits(); // reset bits; change page might have changed
3363 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3366 *change = xx_change;
3368 setEventFlagsFromEventBits(change);
3370 if (real_chunk_size >= chunk_size)
3374 level->file_has_custom_elements = TRUE;
3376 return real_chunk_size;
3379 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3381 int element = getMappedElement(getFile16BitBE(file));
3382 int real_chunk_size = 2;
3383 struct ElementInfo *ei = &element_info[element];
3384 struct ElementGroupInfo *group = ei->group;
3386 xx_ei = *ei; // copy element data into temporary buffer
3387 xx_group = *group; // copy group data into temporary buffer
3389 while (!checkEndOfFile(file))
3391 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3394 if (real_chunk_size >= chunk_size)
3401 level->file_has_custom_elements = TRUE;
3403 return real_chunk_size;
3406 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3408 int element = getMappedElement(getFile16BitBE(file));
3409 int real_chunk_size = 2;
3410 struct ElementInfo *ei = &element_info[element];
3412 xx_ei = *ei; // copy element data into temporary buffer
3414 while (!checkEndOfFile(file))
3416 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3419 if (real_chunk_size >= chunk_size)
3425 level->file_has_custom_elements = TRUE;
3427 return real_chunk_size;
3430 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3431 struct LevelFileInfo *level_file_info,
3432 boolean level_info_only)
3434 char *filename = level_file_info->filename;
3435 char cookie[MAX_LINE_LEN];
3436 char chunk_name[CHUNK_ID_LEN + 1];
3440 if (!(file = openFile(filename, MODE_READ)))
3442 level->no_valid_file = TRUE;
3443 level->no_level_file = TRUE;
3445 if (level_info_only)
3448 Warn("cannot read level '%s' -- using empty level", filename);
3450 if (!setup.editor.use_template_for_new_levels)
3453 // if level file not found, try to initialize level data from template
3454 filename = getGlobalLevelTemplateFilename();
3456 if (!(file = openFile(filename, MODE_READ)))
3459 // default: for empty levels, use level template for custom elements
3460 level->use_custom_template = TRUE;
3462 level->no_valid_file = FALSE;
3465 getFileChunkBE(file, chunk_name, NULL);
3466 if (strEqual(chunk_name, "RND1"))
3468 getFile32BitBE(file); // not used
3470 getFileChunkBE(file, chunk_name, NULL);
3471 if (!strEqual(chunk_name, "CAVE"))
3473 level->no_valid_file = TRUE;
3475 Warn("unknown format of level file '%s'", filename);
3482 else // check for pre-2.0 file format with cookie string
3484 strcpy(cookie, chunk_name);
3485 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3487 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3488 cookie[strlen(cookie) - 1] = '\0';
3490 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3492 level->no_valid_file = TRUE;
3494 Warn("unknown format of level file '%s'", filename);
3501 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3503 level->no_valid_file = TRUE;
3505 Warn("unsupported version of level file '%s'", filename);
3512 // pre-2.0 level files have no game version, so use file version here
3513 level->game_version = level->file_version;
3516 if (level->file_version < FILE_VERSION_1_2)
3518 // level files from versions before 1.2.0 without chunk structure
3519 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3520 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3528 int (*loader)(File *, int, struct LevelInfo *);
3532 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3533 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3534 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3535 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3536 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3537 { "INFO", -1, LoadLevel_INFO },
3538 { "BODY", -1, LoadLevel_BODY },
3539 { "CONT", -1, LoadLevel_CONT },
3540 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3541 { "CNT3", -1, LoadLevel_CNT3 },
3542 { "CUS1", -1, LoadLevel_CUS1 },
3543 { "CUS2", -1, LoadLevel_CUS2 },
3544 { "CUS3", -1, LoadLevel_CUS3 },
3545 { "CUS4", -1, LoadLevel_CUS4 },
3546 { "GRP1", -1, LoadLevel_GRP1 },
3547 { "CONF", -1, LoadLevel_CONF },
3548 { "ELEM", -1, LoadLevel_ELEM },
3549 { "NOTE", -1, LoadLevel_NOTE },
3550 { "CUSX", -1, LoadLevel_CUSX },
3551 { "GRPX", -1, LoadLevel_GRPX },
3552 { "EMPX", -1, LoadLevel_EMPX },
3557 while (getFileChunkBE(file, chunk_name, &chunk_size))
3561 while (chunk_info[i].name != NULL &&
3562 !strEqual(chunk_name, chunk_info[i].name))
3565 if (chunk_info[i].name == NULL)
3567 Warn("unknown chunk '%s' in level file '%s'",
3568 chunk_name, filename);
3570 ReadUnusedBytesFromFile(file, chunk_size);
3572 else if (chunk_info[i].size != -1 &&
3573 chunk_info[i].size != chunk_size)
3575 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3576 chunk_size, chunk_name, filename);
3578 ReadUnusedBytesFromFile(file, chunk_size);
3582 // call function to load this level chunk
3583 int chunk_size_expected =
3584 (chunk_info[i].loader)(file, chunk_size, level);
3586 // the size of some chunks cannot be checked before reading other
3587 // chunks first (like "HEAD" and "BODY") that contain some header
3588 // information, so check them here
3589 if (chunk_size_expected != chunk_size)
3591 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3592 chunk_size, chunk_name, filename);
3602 // ----------------------------------------------------------------------------
3603 // functions for loading EM level
3604 // ----------------------------------------------------------------------------
3606 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3608 static int ball_xy[8][2] =
3619 struct LevelInfo_EM *level_em = level->native_em_level;
3620 struct CAVE *cav = level_em->cav;
3623 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3624 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3626 cav->time_seconds = level->time;
3627 cav->gems_needed = level->gems_needed;
3629 cav->emerald_score = level->score[SC_EMERALD];
3630 cav->diamond_score = level->score[SC_DIAMOND];
3631 cav->alien_score = level->score[SC_ROBOT];
3632 cav->tank_score = level->score[SC_SPACESHIP];
3633 cav->bug_score = level->score[SC_BUG];
3634 cav->eater_score = level->score[SC_YAMYAM];
3635 cav->nut_score = level->score[SC_NUT];
3636 cav->dynamite_score = level->score[SC_DYNAMITE];
3637 cav->key_score = level->score[SC_KEY];
3638 cav->exit_score = level->score[SC_TIME_BONUS];
3640 cav->num_eater_arrays = level->num_yamyam_contents;
3642 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3643 for (y = 0; y < 3; y++)
3644 for (x = 0; x < 3; x++)
3645 cav->eater_array[i][y * 3 + x] =
3646 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3648 cav->amoeba_time = level->amoeba_speed;
3649 cav->wonderwall_time = level->time_magic_wall;
3650 cav->wheel_time = level->time_wheel;
3652 cav->android_move_time = level->android_move_time;
3653 cav->android_clone_time = level->android_clone_time;
3654 cav->ball_random = level->ball_random;
3655 cav->ball_active = level->ball_active_initial;
3656 cav->ball_time = level->ball_time;
3657 cav->num_ball_arrays = level->num_ball_contents;
3659 cav->lenses_score = level->lenses_score;
3660 cav->magnify_score = level->magnify_score;
3661 cav->slurp_score = level->slurp_score;
3663 cav->lenses_time = level->lenses_time;
3664 cav->magnify_time = level->magnify_time;
3666 cav->wind_direction =
3667 map_direction_RND_to_EM(level->wind_direction_initial);
3669 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3670 for (j = 0; j < 8; j++)
3671 cav->ball_array[i][j] =
3672 map_element_RND_to_EM_cave(level->ball_content[i].
3673 e[ball_xy[j][0]][ball_xy[j][1]]);
3675 map_android_clone_elements_RND_to_EM(level);
3677 // first fill the complete playfield with the empty space element
3678 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3679 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3680 cav->cave[x][y] = Cblank;
3682 // then copy the real level contents from level file into the playfield
3683 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3685 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3687 if (level->field[x][y] == EL_AMOEBA_DEAD)
3688 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3690 cav->cave[x][y] = new_element;
3693 for (i = 0; i < MAX_PLAYERS; i++)
3695 cav->player_x[i] = -1;
3696 cav->player_y[i] = -1;
3699 // initialize player positions and delete players from the playfield
3700 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3702 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3704 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3706 cav->player_x[player_nr] = x;
3707 cav->player_y[player_nr] = y;
3709 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3714 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3716 static int ball_xy[8][2] =
3727 struct LevelInfo_EM *level_em = level->native_em_level;
3728 struct CAVE *cav = level_em->cav;
3731 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3732 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3734 level->time = cav->time_seconds;
3735 level->gems_needed = cav->gems_needed;
3737 sprintf(level->name, "Level %d", level->file_info.nr);
3739 level->score[SC_EMERALD] = cav->emerald_score;
3740 level->score[SC_DIAMOND] = cav->diamond_score;
3741 level->score[SC_ROBOT] = cav->alien_score;
3742 level->score[SC_SPACESHIP] = cav->tank_score;
3743 level->score[SC_BUG] = cav->bug_score;
3744 level->score[SC_YAMYAM] = cav->eater_score;
3745 level->score[SC_NUT] = cav->nut_score;
3746 level->score[SC_DYNAMITE] = cav->dynamite_score;
3747 level->score[SC_KEY] = cav->key_score;
3748 level->score[SC_TIME_BONUS] = cav->exit_score;
3750 level->num_yamyam_contents = cav->num_eater_arrays;
3752 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3753 for (y = 0; y < 3; y++)
3754 for (x = 0; x < 3; x++)
3755 level->yamyam_content[i].e[x][y] =
3756 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3758 level->amoeba_speed = cav->amoeba_time;
3759 level->time_magic_wall = cav->wonderwall_time;
3760 level->time_wheel = cav->wheel_time;
3762 level->android_move_time = cav->android_move_time;
3763 level->android_clone_time = cav->android_clone_time;
3764 level->ball_random = cav->ball_random;
3765 level->ball_active_initial = cav->ball_active;
3766 level->ball_time = cav->ball_time;
3767 level->num_ball_contents = cav->num_ball_arrays;
3769 level->lenses_score = cav->lenses_score;
3770 level->magnify_score = cav->magnify_score;
3771 level->slurp_score = cav->slurp_score;
3773 level->lenses_time = cav->lenses_time;
3774 level->magnify_time = cav->magnify_time;
3776 level->wind_direction_initial =
3777 map_direction_EM_to_RND(cav->wind_direction);
3779 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3780 for (j = 0; j < 8; j++)
3781 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3782 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3784 map_android_clone_elements_EM_to_RND(level);
3786 // convert the playfield (some elements need special treatment)
3787 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3789 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3791 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3792 new_element = EL_AMOEBA_DEAD;
3794 level->field[x][y] = new_element;
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 // in case of all players set to the same field, use the first player
3800 int nr = MAX_PLAYERS - i - 1;
3801 int jx = cav->player_x[nr];
3802 int jy = cav->player_y[nr];
3804 if (jx != -1 && jy != -1)
3805 level->field[jx][jy] = EL_PLAYER_1 + nr;
3808 // time score is counted for each 10 seconds left in Emerald Mine levels
3809 level->time_score_base = 10;
3813 // ----------------------------------------------------------------------------
3814 // functions for loading SP level
3815 // ----------------------------------------------------------------------------
3817 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3819 struct LevelInfo_SP *level_sp = level->native_sp_level;
3820 LevelInfoType *header = &level_sp->header;
3823 level_sp->width = level->fieldx;
3824 level_sp->height = level->fieldy;
3826 for (x = 0; x < level->fieldx; x++)
3827 for (y = 0; y < level->fieldy; y++)
3828 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3830 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3832 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3833 header->LevelTitle[i] = level->name[i];
3834 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3836 header->InfotronsNeeded = level->gems_needed;
3838 header->SpecialPortCount = 0;
3840 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3842 boolean gravity_port_found = FALSE;
3843 boolean gravity_port_valid = FALSE;
3844 int gravity_port_flag;
3845 int gravity_port_base_element;
3846 int element = level->field[x][y];
3848 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3849 element <= EL_SP_GRAVITY_ON_PORT_UP)
3851 gravity_port_found = TRUE;
3852 gravity_port_valid = TRUE;
3853 gravity_port_flag = 1;
3854 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3856 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3857 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3859 gravity_port_found = TRUE;
3860 gravity_port_valid = TRUE;
3861 gravity_port_flag = 0;
3862 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3864 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3865 element <= EL_SP_GRAVITY_PORT_UP)
3867 // change R'n'D style gravity inverting special port to normal port
3868 // (there are no gravity inverting ports in native Supaplex engine)
3870 gravity_port_found = TRUE;
3871 gravity_port_valid = FALSE;
3872 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3875 if (gravity_port_found)
3877 if (gravity_port_valid &&
3878 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3880 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3882 port->PortLocation = (y * level->fieldx + x) * 2;
3883 port->Gravity = gravity_port_flag;
3885 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3887 header->SpecialPortCount++;
3891 // change special gravity port to normal port
3893 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3896 level_sp->playfield[x][y] = element - EL_SP_START;
3901 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3903 struct LevelInfo_SP *level_sp = level->native_sp_level;
3904 LevelInfoType *header = &level_sp->header;
3905 boolean num_invalid_elements = 0;
3908 level->fieldx = level_sp->width;
3909 level->fieldy = level_sp->height;
3911 for (x = 0; x < level->fieldx; x++)
3913 for (y = 0; y < level->fieldy; y++)
3915 int element_old = level_sp->playfield[x][y];
3916 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3918 if (element_new == EL_UNKNOWN)
3920 num_invalid_elements++;
3922 Debug("level:native:SP", "invalid element %d at position %d, %d",
3926 level->field[x][y] = element_new;
3930 if (num_invalid_elements > 0)
3931 Warn("found %d invalid elements%s", num_invalid_elements,
3932 (!options.debug ? " (use '--debug' for more details)" : ""));
3934 for (i = 0; i < MAX_PLAYERS; i++)
3935 level->initial_player_gravity[i] =
3936 (header->InitialGravity == 1 ? TRUE : FALSE);
3938 // skip leading spaces
3939 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3940 if (header->LevelTitle[i] != ' ')
3944 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3945 level->name[j] = header->LevelTitle[i];
3946 level->name[j] = '\0';
3948 // cut trailing spaces
3950 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3951 level->name[j - 1] = '\0';
3953 level->gems_needed = header->InfotronsNeeded;
3955 for (i = 0; i < header->SpecialPortCount; i++)
3957 SpecialPortType *port = &header->SpecialPort[i];
3958 int port_location = port->PortLocation;
3959 int gravity = port->Gravity;
3960 int port_x, port_y, port_element;
3962 port_x = (port_location / 2) % level->fieldx;
3963 port_y = (port_location / 2) / level->fieldx;
3965 if (port_x < 0 || port_x >= level->fieldx ||
3966 port_y < 0 || port_y >= level->fieldy)
3968 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3973 port_element = level->field[port_x][port_y];
3975 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3976 port_element > EL_SP_GRAVITY_PORT_UP)
3978 Warn("no special port at position (%d, %d)", port_x, port_y);
3983 // change previous (wrong) gravity inverting special port to either
3984 // gravity enabling special port or gravity disabling special port
3985 level->field[port_x][port_y] +=
3986 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3987 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3990 // change special gravity ports without database entries to normal ports
3991 for (x = 0; x < level->fieldx; x++)
3992 for (y = 0; y < level->fieldy; y++)
3993 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3994 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3995 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3997 level->time = 0; // no time limit
3998 level->amoeba_speed = 0;
3999 level->time_magic_wall = 0;
4000 level->time_wheel = 0;
4001 level->amoeba_content = EL_EMPTY;
4003 // original Supaplex does not use score values -- rate by playing time
4004 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4005 level->score[i] = 0;
4007 level->rate_time_over_score = TRUE;
4009 // there are no yamyams in supaplex levels
4010 for (i = 0; i < level->num_yamyam_contents; i++)
4011 for (x = 0; x < 3; x++)
4012 for (y = 0; y < 3; y++)
4013 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4016 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4018 struct LevelInfo_SP *level_sp = level->native_sp_level;
4019 struct DemoInfo_SP *demo = &level_sp->demo;
4022 // always start with reliable default values
4023 demo->is_available = FALSE;
4026 if (TAPE_IS_EMPTY(tape))
4029 demo->level_nr = tape.level_nr; // (currently not used)
4031 level_sp->header.DemoRandomSeed = tape.random_seed;
4035 for (i = 0; i < tape.length; i++)
4037 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4038 int demo_repeat = tape.pos[i].delay;
4039 int demo_entries = (demo_repeat + 15) / 16;
4041 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4043 Warn("tape truncated: size exceeds maximum SP demo size %d",
4049 for (j = 0; j < demo_repeat / 16; j++)
4050 demo->data[demo->length++] = 0xf0 | demo_action;
4052 if (demo_repeat % 16)
4053 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4056 demo->is_available = TRUE;
4059 static void setTapeInfoToDefaults(void);
4061 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4063 struct LevelInfo_SP *level_sp = level->native_sp_level;
4064 struct DemoInfo_SP *demo = &level_sp->demo;
4065 char *filename = level->file_info.filename;
4068 // always start with reliable default values
4069 setTapeInfoToDefaults();
4071 if (!demo->is_available)
4074 tape.level_nr = demo->level_nr; // (currently not used)
4075 tape.random_seed = level_sp->header.DemoRandomSeed;
4077 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4080 tape.pos[tape.counter].delay = 0;
4082 for (i = 0; i < demo->length; i++)
4084 int demo_action = demo->data[i] & 0x0f;
4085 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4086 int tape_action = map_key_SP_to_RND(demo_action);
4087 int tape_repeat = demo_repeat + 1;
4088 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4089 boolean success = 0;
4092 for (j = 0; j < tape_repeat; j++)
4093 success = TapeAddAction(action);
4097 Warn("SP demo truncated: size exceeds maximum tape size %d",
4104 TapeHaltRecording();
4108 // ----------------------------------------------------------------------------
4109 // functions for loading MM level
4110 // ----------------------------------------------------------------------------
4112 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4114 struct LevelInfo_MM *level_mm = level->native_mm_level;
4117 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4118 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4120 level_mm->time = level->time;
4121 level_mm->kettles_needed = level->gems_needed;
4122 level_mm->auto_count_kettles = level->auto_count_gems;
4124 level_mm->laser_red = level->mm_laser_red;
4125 level_mm->laser_green = level->mm_laser_green;
4126 level_mm->laser_blue = level->mm_laser_blue;
4128 strcpy(level_mm->name, level->name);
4129 strcpy(level_mm->author, level->author);
4131 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4132 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4133 level_mm->score[SC_KEY] = level->score[SC_KEY];
4134 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4135 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4137 level_mm->amoeba_speed = level->amoeba_speed;
4138 level_mm->time_fuse = level->mm_time_fuse;
4139 level_mm->time_bomb = level->mm_time_bomb;
4140 level_mm->time_ball = level->mm_time_ball;
4141 level_mm->time_block = level->mm_time_block;
4143 for (x = 0; x < level->fieldx; x++)
4144 for (y = 0; y < level->fieldy; y++)
4146 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4149 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4151 struct LevelInfo_MM *level_mm = level->native_mm_level;
4154 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4155 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4157 level->time = level_mm->time;
4158 level->gems_needed = level_mm->kettles_needed;
4159 level->auto_count_gems = level_mm->auto_count_kettles;
4161 level->mm_laser_red = level_mm->laser_red;
4162 level->mm_laser_green = level_mm->laser_green;
4163 level->mm_laser_blue = level_mm->laser_blue;
4165 strcpy(level->name, level_mm->name);
4167 // only overwrite author from 'levelinfo.conf' if author defined in level
4168 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4169 strcpy(level->author, level_mm->author);
4171 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4172 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4173 level->score[SC_KEY] = level_mm->score[SC_KEY];
4174 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4175 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4177 level->amoeba_speed = level_mm->amoeba_speed;
4178 level->mm_time_fuse = level_mm->time_fuse;
4179 level->mm_time_bomb = level_mm->time_bomb;
4180 level->mm_time_ball = level_mm->time_ball;
4181 level->mm_time_block = level_mm->time_block;
4183 for (x = 0; x < level->fieldx; x++)
4184 for (y = 0; y < level->fieldy; y++)
4185 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4189 // ----------------------------------------------------------------------------
4190 // functions for loading DC level
4191 // ----------------------------------------------------------------------------
4193 #define DC_LEVEL_HEADER_SIZE 344
4195 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4198 static int last_data_encoded;
4202 int diff_hi, diff_lo;
4203 int data_hi, data_lo;
4204 unsigned short data_decoded;
4208 last_data_encoded = 0;
4215 diff = data_encoded - last_data_encoded;
4216 diff_hi = diff & ~0xff;
4217 diff_lo = diff & 0xff;
4221 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4222 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4223 data_hi = data_hi & 0xff00;
4225 data_decoded = data_hi | data_lo;
4227 last_data_encoded = data_encoded;
4229 offset1 = (offset1 + 1) % 31;
4230 offset2 = offset2 & 0xff;
4232 return data_decoded;
4235 static int getMappedElement_DC(int element)
4243 // 0x0117 - 0x036e: (?)
4246 // 0x042d - 0x0684: (?)
4262 element = EL_CRYSTAL;
4265 case 0x0e77: // quicksand (boulder)
4266 element = EL_QUICKSAND_FAST_FULL;
4269 case 0x0e99: // slow quicksand (boulder)
4270 element = EL_QUICKSAND_FULL;
4274 element = EL_EM_EXIT_OPEN;
4278 element = EL_EM_EXIT_CLOSED;
4282 element = EL_EM_STEEL_EXIT_OPEN;
4286 element = EL_EM_STEEL_EXIT_CLOSED;
4289 case 0x0f4f: // dynamite (lit 1)
4290 element = EL_EM_DYNAMITE_ACTIVE;
4293 case 0x0f57: // dynamite (lit 2)
4294 element = EL_EM_DYNAMITE_ACTIVE;
4297 case 0x0f5f: // dynamite (lit 3)
4298 element = EL_EM_DYNAMITE_ACTIVE;
4301 case 0x0f67: // dynamite (lit 4)
4302 element = EL_EM_DYNAMITE_ACTIVE;
4309 element = EL_AMOEBA_WET;
4313 element = EL_AMOEBA_DROP;
4317 element = EL_DC_MAGIC_WALL;
4321 element = EL_SPACESHIP_UP;
4325 element = EL_SPACESHIP_DOWN;
4329 element = EL_SPACESHIP_LEFT;
4333 element = EL_SPACESHIP_RIGHT;
4337 element = EL_BUG_UP;
4341 element = EL_BUG_DOWN;
4345 element = EL_BUG_LEFT;
4349 element = EL_BUG_RIGHT;
4353 element = EL_MOLE_UP;
4357 element = EL_MOLE_DOWN;
4361 element = EL_MOLE_LEFT;
4365 element = EL_MOLE_RIGHT;
4373 element = EL_YAMYAM_UP;
4377 element = EL_SWITCHGATE_OPEN;
4381 element = EL_SWITCHGATE_CLOSED;
4385 element = EL_DC_SWITCHGATE_SWITCH_UP;
4389 element = EL_TIMEGATE_CLOSED;
4392 case 0x144c: // conveyor belt switch (green)
4393 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4396 case 0x144f: // conveyor belt switch (red)
4397 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4400 case 0x1452: // conveyor belt switch (blue)
4401 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4405 element = EL_CONVEYOR_BELT_3_MIDDLE;
4409 element = EL_CONVEYOR_BELT_3_LEFT;
4413 element = EL_CONVEYOR_BELT_3_RIGHT;
4417 element = EL_CONVEYOR_BELT_1_MIDDLE;
4421 element = EL_CONVEYOR_BELT_1_LEFT;
4425 element = EL_CONVEYOR_BELT_1_RIGHT;
4429 element = EL_CONVEYOR_BELT_4_MIDDLE;
4433 element = EL_CONVEYOR_BELT_4_LEFT;
4437 element = EL_CONVEYOR_BELT_4_RIGHT;
4441 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4445 element = EL_EXPANDABLE_WALL_VERTICAL;
4449 element = EL_EXPANDABLE_WALL_ANY;
4452 case 0x14ce: // growing steel wall (left/right)
4453 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4456 case 0x14df: // growing steel wall (up/down)
4457 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4460 case 0x14e8: // growing steel wall (up/down/left/right)
4461 element = EL_EXPANDABLE_STEELWALL_ANY;
4465 element = EL_SHIELD_DEADLY;
4469 element = EL_EXTRA_TIME;
4477 element = EL_EMPTY_SPACE;
4480 case 0x1578: // quicksand (empty)
4481 element = EL_QUICKSAND_FAST_EMPTY;
4484 case 0x1579: // slow quicksand (empty)
4485 element = EL_QUICKSAND_EMPTY;
4495 element = EL_EM_DYNAMITE;
4498 case 0x15a1: // key (red)
4499 element = EL_EM_KEY_1;
4502 case 0x15a2: // key (yellow)
4503 element = EL_EM_KEY_2;
4506 case 0x15a3: // key (blue)
4507 element = EL_EM_KEY_4;
4510 case 0x15a4: // key (green)
4511 element = EL_EM_KEY_3;
4514 case 0x15a5: // key (white)
4515 element = EL_DC_KEY_WHITE;
4519 element = EL_WALL_SLIPPERY;
4526 case 0x15a8: // wall (not round)
4530 case 0x15a9: // (blue)
4531 element = EL_CHAR_A;
4534 case 0x15aa: // (blue)
4535 element = EL_CHAR_B;
4538 case 0x15ab: // (blue)
4539 element = EL_CHAR_C;
4542 case 0x15ac: // (blue)
4543 element = EL_CHAR_D;
4546 case 0x15ad: // (blue)
4547 element = EL_CHAR_E;
4550 case 0x15ae: // (blue)
4551 element = EL_CHAR_F;
4554 case 0x15af: // (blue)
4555 element = EL_CHAR_G;
4558 case 0x15b0: // (blue)
4559 element = EL_CHAR_H;
4562 case 0x15b1: // (blue)
4563 element = EL_CHAR_I;
4566 case 0x15b2: // (blue)
4567 element = EL_CHAR_J;
4570 case 0x15b3: // (blue)
4571 element = EL_CHAR_K;
4574 case 0x15b4: // (blue)
4575 element = EL_CHAR_L;
4578 case 0x15b5: // (blue)
4579 element = EL_CHAR_M;
4582 case 0x15b6: // (blue)
4583 element = EL_CHAR_N;
4586 case 0x15b7: // (blue)
4587 element = EL_CHAR_O;
4590 case 0x15b8: // (blue)
4591 element = EL_CHAR_P;
4594 case 0x15b9: // (blue)
4595 element = EL_CHAR_Q;
4598 case 0x15ba: // (blue)
4599 element = EL_CHAR_R;
4602 case 0x15bb: // (blue)
4603 element = EL_CHAR_S;
4606 case 0x15bc: // (blue)
4607 element = EL_CHAR_T;
4610 case 0x15bd: // (blue)
4611 element = EL_CHAR_U;
4614 case 0x15be: // (blue)
4615 element = EL_CHAR_V;
4618 case 0x15bf: // (blue)
4619 element = EL_CHAR_W;
4622 case 0x15c0: // (blue)
4623 element = EL_CHAR_X;
4626 case 0x15c1: // (blue)
4627 element = EL_CHAR_Y;
4630 case 0x15c2: // (blue)
4631 element = EL_CHAR_Z;
4634 case 0x15c3: // (blue)
4635 element = EL_CHAR_AUMLAUT;
4638 case 0x15c4: // (blue)
4639 element = EL_CHAR_OUMLAUT;
4642 case 0x15c5: // (blue)
4643 element = EL_CHAR_UUMLAUT;
4646 case 0x15c6: // (blue)
4647 element = EL_CHAR_0;
4650 case 0x15c7: // (blue)
4651 element = EL_CHAR_1;
4654 case 0x15c8: // (blue)
4655 element = EL_CHAR_2;
4658 case 0x15c9: // (blue)
4659 element = EL_CHAR_3;
4662 case 0x15ca: // (blue)
4663 element = EL_CHAR_4;
4666 case 0x15cb: // (blue)
4667 element = EL_CHAR_5;
4670 case 0x15cc: // (blue)
4671 element = EL_CHAR_6;
4674 case 0x15cd: // (blue)
4675 element = EL_CHAR_7;
4678 case 0x15ce: // (blue)
4679 element = EL_CHAR_8;
4682 case 0x15cf: // (blue)
4683 element = EL_CHAR_9;
4686 case 0x15d0: // (blue)
4687 element = EL_CHAR_PERIOD;
4690 case 0x15d1: // (blue)
4691 element = EL_CHAR_EXCLAM;
4694 case 0x15d2: // (blue)
4695 element = EL_CHAR_COLON;
4698 case 0x15d3: // (blue)
4699 element = EL_CHAR_LESS;
4702 case 0x15d4: // (blue)
4703 element = EL_CHAR_GREATER;
4706 case 0x15d5: // (blue)
4707 element = EL_CHAR_QUESTION;
4710 case 0x15d6: // (blue)
4711 element = EL_CHAR_COPYRIGHT;
4714 case 0x15d7: // (blue)
4715 element = EL_CHAR_UP;
4718 case 0x15d8: // (blue)
4719 element = EL_CHAR_DOWN;
4722 case 0x15d9: // (blue)
4723 element = EL_CHAR_BUTTON;
4726 case 0x15da: // (blue)
4727 element = EL_CHAR_PLUS;
4730 case 0x15db: // (blue)
4731 element = EL_CHAR_MINUS;
4734 case 0x15dc: // (blue)
4735 element = EL_CHAR_APOSTROPHE;
4738 case 0x15dd: // (blue)
4739 element = EL_CHAR_PARENLEFT;
4742 case 0x15de: // (blue)
4743 element = EL_CHAR_PARENRIGHT;
4746 case 0x15df: // (green)
4747 element = EL_CHAR_A;
4750 case 0x15e0: // (green)
4751 element = EL_CHAR_B;
4754 case 0x15e1: // (green)
4755 element = EL_CHAR_C;
4758 case 0x15e2: // (green)
4759 element = EL_CHAR_D;
4762 case 0x15e3: // (green)
4763 element = EL_CHAR_E;
4766 case 0x15e4: // (green)
4767 element = EL_CHAR_F;
4770 case 0x15e5: // (green)
4771 element = EL_CHAR_G;
4774 case 0x15e6: // (green)
4775 element = EL_CHAR_H;
4778 case 0x15e7: // (green)
4779 element = EL_CHAR_I;
4782 case 0x15e8: // (green)
4783 element = EL_CHAR_J;
4786 case 0x15e9: // (green)
4787 element = EL_CHAR_K;
4790 case 0x15ea: // (green)
4791 element = EL_CHAR_L;
4794 case 0x15eb: // (green)
4795 element = EL_CHAR_M;
4798 case 0x15ec: // (green)
4799 element = EL_CHAR_N;
4802 case 0x15ed: // (green)
4803 element = EL_CHAR_O;
4806 case 0x15ee: // (green)
4807 element = EL_CHAR_P;
4810 case 0x15ef: // (green)
4811 element = EL_CHAR_Q;
4814 case 0x15f0: // (green)
4815 element = EL_CHAR_R;
4818 case 0x15f1: // (green)
4819 element = EL_CHAR_S;
4822 case 0x15f2: // (green)
4823 element = EL_CHAR_T;
4826 case 0x15f3: // (green)
4827 element = EL_CHAR_U;
4830 case 0x15f4: // (green)
4831 element = EL_CHAR_V;
4834 case 0x15f5: // (green)
4835 element = EL_CHAR_W;
4838 case 0x15f6: // (green)
4839 element = EL_CHAR_X;
4842 case 0x15f7: // (green)
4843 element = EL_CHAR_Y;
4846 case 0x15f8: // (green)
4847 element = EL_CHAR_Z;
4850 case 0x15f9: // (green)
4851 element = EL_CHAR_AUMLAUT;
4854 case 0x15fa: // (green)
4855 element = EL_CHAR_OUMLAUT;
4858 case 0x15fb: // (green)
4859 element = EL_CHAR_UUMLAUT;
4862 case 0x15fc: // (green)
4863 element = EL_CHAR_0;
4866 case 0x15fd: // (green)
4867 element = EL_CHAR_1;
4870 case 0x15fe: // (green)
4871 element = EL_CHAR_2;
4874 case 0x15ff: // (green)
4875 element = EL_CHAR_3;
4878 case 0x1600: // (green)
4879 element = EL_CHAR_4;
4882 case 0x1601: // (green)
4883 element = EL_CHAR_5;
4886 case 0x1602: // (green)
4887 element = EL_CHAR_6;
4890 case 0x1603: // (green)
4891 element = EL_CHAR_7;
4894 case 0x1604: // (green)
4895 element = EL_CHAR_8;
4898 case 0x1605: // (green)
4899 element = EL_CHAR_9;
4902 case 0x1606: // (green)
4903 element = EL_CHAR_PERIOD;
4906 case 0x1607: // (green)
4907 element = EL_CHAR_EXCLAM;
4910 case 0x1608: // (green)
4911 element = EL_CHAR_COLON;
4914 case 0x1609: // (green)
4915 element = EL_CHAR_LESS;
4918 case 0x160a: // (green)
4919 element = EL_CHAR_GREATER;
4922 case 0x160b: // (green)
4923 element = EL_CHAR_QUESTION;
4926 case 0x160c: // (green)
4927 element = EL_CHAR_COPYRIGHT;
4930 case 0x160d: // (green)
4931 element = EL_CHAR_UP;
4934 case 0x160e: // (green)
4935 element = EL_CHAR_DOWN;
4938 case 0x160f: // (green)
4939 element = EL_CHAR_BUTTON;
4942 case 0x1610: // (green)
4943 element = EL_CHAR_PLUS;
4946 case 0x1611: // (green)
4947 element = EL_CHAR_MINUS;
4950 case 0x1612: // (green)
4951 element = EL_CHAR_APOSTROPHE;
4954 case 0x1613: // (green)
4955 element = EL_CHAR_PARENLEFT;
4958 case 0x1614: // (green)
4959 element = EL_CHAR_PARENRIGHT;
4962 case 0x1615: // (blue steel)
4963 element = EL_STEEL_CHAR_A;
4966 case 0x1616: // (blue steel)
4967 element = EL_STEEL_CHAR_B;
4970 case 0x1617: // (blue steel)
4971 element = EL_STEEL_CHAR_C;
4974 case 0x1618: // (blue steel)
4975 element = EL_STEEL_CHAR_D;
4978 case 0x1619: // (blue steel)
4979 element = EL_STEEL_CHAR_E;
4982 case 0x161a: // (blue steel)
4983 element = EL_STEEL_CHAR_F;
4986 case 0x161b: // (blue steel)
4987 element = EL_STEEL_CHAR_G;
4990 case 0x161c: // (blue steel)
4991 element = EL_STEEL_CHAR_H;
4994 case 0x161d: // (blue steel)
4995 element = EL_STEEL_CHAR_I;
4998 case 0x161e: // (blue steel)
4999 element = EL_STEEL_CHAR_J;
5002 case 0x161f: // (blue steel)
5003 element = EL_STEEL_CHAR_K;
5006 case 0x1620: // (blue steel)
5007 element = EL_STEEL_CHAR_L;
5010 case 0x1621: // (blue steel)
5011 element = EL_STEEL_CHAR_M;
5014 case 0x1622: // (blue steel)
5015 element = EL_STEEL_CHAR_N;
5018 case 0x1623: // (blue steel)
5019 element = EL_STEEL_CHAR_O;
5022 case 0x1624: // (blue steel)
5023 element = EL_STEEL_CHAR_P;
5026 case 0x1625: // (blue steel)
5027 element = EL_STEEL_CHAR_Q;
5030 case 0x1626: // (blue steel)
5031 element = EL_STEEL_CHAR_R;
5034 case 0x1627: // (blue steel)
5035 element = EL_STEEL_CHAR_S;
5038 case 0x1628: // (blue steel)
5039 element = EL_STEEL_CHAR_T;
5042 case 0x1629: // (blue steel)
5043 element = EL_STEEL_CHAR_U;
5046 case 0x162a: // (blue steel)
5047 element = EL_STEEL_CHAR_V;
5050 case 0x162b: // (blue steel)
5051 element = EL_STEEL_CHAR_W;
5054 case 0x162c: // (blue steel)
5055 element = EL_STEEL_CHAR_X;
5058 case 0x162d: // (blue steel)
5059 element = EL_STEEL_CHAR_Y;
5062 case 0x162e: // (blue steel)
5063 element = EL_STEEL_CHAR_Z;
5066 case 0x162f: // (blue steel)
5067 element = EL_STEEL_CHAR_AUMLAUT;
5070 case 0x1630: // (blue steel)
5071 element = EL_STEEL_CHAR_OUMLAUT;
5074 case 0x1631: // (blue steel)
5075 element = EL_STEEL_CHAR_UUMLAUT;
5078 case 0x1632: // (blue steel)
5079 element = EL_STEEL_CHAR_0;
5082 case 0x1633: // (blue steel)
5083 element = EL_STEEL_CHAR_1;
5086 case 0x1634: // (blue steel)
5087 element = EL_STEEL_CHAR_2;
5090 case 0x1635: // (blue steel)
5091 element = EL_STEEL_CHAR_3;
5094 case 0x1636: // (blue steel)
5095 element = EL_STEEL_CHAR_4;
5098 case 0x1637: // (blue steel)
5099 element = EL_STEEL_CHAR_5;
5102 case 0x1638: // (blue steel)
5103 element = EL_STEEL_CHAR_6;
5106 case 0x1639: // (blue steel)
5107 element = EL_STEEL_CHAR_7;
5110 case 0x163a: // (blue steel)
5111 element = EL_STEEL_CHAR_8;
5114 case 0x163b: // (blue steel)
5115 element = EL_STEEL_CHAR_9;
5118 case 0x163c: // (blue steel)
5119 element = EL_STEEL_CHAR_PERIOD;
5122 case 0x163d: // (blue steel)
5123 element = EL_STEEL_CHAR_EXCLAM;
5126 case 0x163e: // (blue steel)
5127 element = EL_STEEL_CHAR_COLON;
5130 case 0x163f: // (blue steel)
5131 element = EL_STEEL_CHAR_LESS;
5134 case 0x1640: // (blue steel)
5135 element = EL_STEEL_CHAR_GREATER;
5138 case 0x1641: // (blue steel)
5139 element = EL_STEEL_CHAR_QUESTION;
5142 case 0x1642: // (blue steel)
5143 element = EL_STEEL_CHAR_COPYRIGHT;
5146 case 0x1643: // (blue steel)
5147 element = EL_STEEL_CHAR_UP;
5150 case 0x1644: // (blue steel)
5151 element = EL_STEEL_CHAR_DOWN;
5154 case 0x1645: // (blue steel)
5155 element = EL_STEEL_CHAR_BUTTON;
5158 case 0x1646: // (blue steel)
5159 element = EL_STEEL_CHAR_PLUS;
5162 case 0x1647: // (blue steel)
5163 element = EL_STEEL_CHAR_MINUS;
5166 case 0x1648: // (blue steel)
5167 element = EL_STEEL_CHAR_APOSTROPHE;
5170 case 0x1649: // (blue steel)
5171 element = EL_STEEL_CHAR_PARENLEFT;
5174 case 0x164a: // (blue steel)
5175 element = EL_STEEL_CHAR_PARENRIGHT;
5178 case 0x164b: // (green steel)
5179 element = EL_STEEL_CHAR_A;
5182 case 0x164c: // (green steel)
5183 element = EL_STEEL_CHAR_B;
5186 case 0x164d: // (green steel)
5187 element = EL_STEEL_CHAR_C;
5190 case 0x164e: // (green steel)
5191 element = EL_STEEL_CHAR_D;
5194 case 0x164f: // (green steel)
5195 element = EL_STEEL_CHAR_E;
5198 case 0x1650: // (green steel)
5199 element = EL_STEEL_CHAR_F;
5202 case 0x1651: // (green steel)
5203 element = EL_STEEL_CHAR_G;
5206 case 0x1652: // (green steel)
5207 element = EL_STEEL_CHAR_H;
5210 case 0x1653: // (green steel)
5211 element = EL_STEEL_CHAR_I;
5214 case 0x1654: // (green steel)
5215 element = EL_STEEL_CHAR_J;
5218 case 0x1655: // (green steel)
5219 element = EL_STEEL_CHAR_K;
5222 case 0x1656: // (green steel)
5223 element = EL_STEEL_CHAR_L;
5226 case 0x1657: // (green steel)
5227 element = EL_STEEL_CHAR_M;
5230 case 0x1658: // (green steel)
5231 element = EL_STEEL_CHAR_N;
5234 case 0x1659: // (green steel)
5235 element = EL_STEEL_CHAR_O;
5238 case 0x165a: // (green steel)
5239 element = EL_STEEL_CHAR_P;
5242 case 0x165b: // (green steel)
5243 element = EL_STEEL_CHAR_Q;
5246 case 0x165c: // (green steel)
5247 element = EL_STEEL_CHAR_R;
5250 case 0x165d: // (green steel)
5251 element = EL_STEEL_CHAR_S;
5254 case 0x165e: // (green steel)
5255 element = EL_STEEL_CHAR_T;
5258 case 0x165f: // (green steel)
5259 element = EL_STEEL_CHAR_U;
5262 case 0x1660: // (green steel)
5263 element = EL_STEEL_CHAR_V;
5266 case 0x1661: // (green steel)
5267 element = EL_STEEL_CHAR_W;
5270 case 0x1662: // (green steel)
5271 element = EL_STEEL_CHAR_X;
5274 case 0x1663: // (green steel)
5275 element = EL_STEEL_CHAR_Y;
5278 case 0x1664: // (green steel)
5279 element = EL_STEEL_CHAR_Z;
5282 case 0x1665: // (green steel)
5283 element = EL_STEEL_CHAR_AUMLAUT;
5286 case 0x1666: // (green steel)
5287 element = EL_STEEL_CHAR_OUMLAUT;
5290 case 0x1667: // (green steel)
5291 element = EL_STEEL_CHAR_UUMLAUT;
5294 case 0x1668: // (green steel)
5295 element = EL_STEEL_CHAR_0;
5298 case 0x1669: // (green steel)
5299 element = EL_STEEL_CHAR_1;
5302 case 0x166a: // (green steel)
5303 element = EL_STEEL_CHAR_2;
5306 case 0x166b: // (green steel)
5307 element = EL_STEEL_CHAR_3;
5310 case 0x166c: // (green steel)
5311 element = EL_STEEL_CHAR_4;
5314 case 0x166d: // (green steel)
5315 element = EL_STEEL_CHAR_5;
5318 case 0x166e: // (green steel)
5319 element = EL_STEEL_CHAR_6;
5322 case 0x166f: // (green steel)
5323 element = EL_STEEL_CHAR_7;
5326 case 0x1670: // (green steel)
5327 element = EL_STEEL_CHAR_8;
5330 case 0x1671: // (green steel)
5331 element = EL_STEEL_CHAR_9;
5334 case 0x1672: // (green steel)
5335 element = EL_STEEL_CHAR_PERIOD;
5338 case 0x1673: // (green steel)
5339 element = EL_STEEL_CHAR_EXCLAM;
5342 case 0x1674: // (green steel)
5343 element = EL_STEEL_CHAR_COLON;
5346 case 0x1675: // (green steel)
5347 element = EL_STEEL_CHAR_LESS;
5350 case 0x1676: // (green steel)
5351 element = EL_STEEL_CHAR_GREATER;
5354 case 0x1677: // (green steel)
5355 element = EL_STEEL_CHAR_QUESTION;
5358 case 0x1678: // (green steel)
5359 element = EL_STEEL_CHAR_COPYRIGHT;
5362 case 0x1679: // (green steel)
5363 element = EL_STEEL_CHAR_UP;
5366 case 0x167a: // (green steel)
5367 element = EL_STEEL_CHAR_DOWN;
5370 case 0x167b: // (green steel)
5371 element = EL_STEEL_CHAR_BUTTON;
5374 case 0x167c: // (green steel)
5375 element = EL_STEEL_CHAR_PLUS;
5378 case 0x167d: // (green steel)
5379 element = EL_STEEL_CHAR_MINUS;
5382 case 0x167e: // (green steel)
5383 element = EL_STEEL_CHAR_APOSTROPHE;
5386 case 0x167f: // (green steel)
5387 element = EL_STEEL_CHAR_PARENLEFT;
5390 case 0x1680: // (green steel)
5391 element = EL_STEEL_CHAR_PARENRIGHT;
5394 case 0x1681: // gate (red)
5395 element = EL_EM_GATE_1;
5398 case 0x1682: // secret gate (red)
5399 element = EL_EM_GATE_1_GRAY;
5402 case 0x1683: // gate (yellow)
5403 element = EL_EM_GATE_2;
5406 case 0x1684: // secret gate (yellow)
5407 element = EL_EM_GATE_2_GRAY;
5410 case 0x1685: // gate (blue)
5411 element = EL_EM_GATE_4;
5414 case 0x1686: // secret gate (blue)
5415 element = EL_EM_GATE_4_GRAY;
5418 case 0x1687: // gate (green)
5419 element = EL_EM_GATE_3;
5422 case 0x1688: // secret gate (green)
5423 element = EL_EM_GATE_3_GRAY;
5426 case 0x1689: // gate (white)
5427 element = EL_DC_GATE_WHITE;
5430 case 0x168a: // secret gate (white)
5431 element = EL_DC_GATE_WHITE_GRAY;
5434 case 0x168b: // secret gate (no key)
5435 element = EL_DC_GATE_FAKE_GRAY;
5439 element = EL_ROBOT_WHEEL;
5443 element = EL_DC_TIMEGATE_SWITCH;
5447 element = EL_ACID_POOL_BOTTOM;
5451 element = EL_ACID_POOL_TOPLEFT;
5455 element = EL_ACID_POOL_TOPRIGHT;
5459 element = EL_ACID_POOL_BOTTOMLEFT;
5463 element = EL_ACID_POOL_BOTTOMRIGHT;
5467 element = EL_STEELWALL;
5471 element = EL_STEELWALL_SLIPPERY;
5474 case 0x1695: // steel wall (not round)
5475 element = EL_STEELWALL;
5478 case 0x1696: // steel wall (left)
5479 element = EL_DC_STEELWALL_1_LEFT;
5482 case 0x1697: // steel wall (bottom)
5483 element = EL_DC_STEELWALL_1_BOTTOM;
5486 case 0x1698: // steel wall (right)
5487 element = EL_DC_STEELWALL_1_RIGHT;
5490 case 0x1699: // steel wall (top)
5491 element = EL_DC_STEELWALL_1_TOP;
5494 case 0x169a: // steel wall (left/bottom)
5495 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5498 case 0x169b: // steel wall (right/bottom)
5499 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5502 case 0x169c: // steel wall (right/top)
5503 element = EL_DC_STEELWALL_1_TOPRIGHT;
5506 case 0x169d: // steel wall (left/top)
5507 element = EL_DC_STEELWALL_1_TOPLEFT;
5510 case 0x169e: // steel wall (right/bottom small)
5511 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5514 case 0x169f: // steel wall (left/bottom small)
5515 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5518 case 0x16a0: // steel wall (right/top small)
5519 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5522 case 0x16a1: // steel wall (left/top small)
5523 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5526 case 0x16a2: // steel wall (left/right)
5527 element = EL_DC_STEELWALL_1_VERTICAL;
5530 case 0x16a3: // steel wall (top/bottom)
5531 element = EL_DC_STEELWALL_1_HORIZONTAL;
5534 case 0x16a4: // steel wall 2 (left end)
5535 element = EL_DC_STEELWALL_2_LEFT;
5538 case 0x16a5: // steel wall 2 (right end)
5539 element = EL_DC_STEELWALL_2_RIGHT;
5542 case 0x16a6: // steel wall 2 (top end)
5543 element = EL_DC_STEELWALL_2_TOP;
5546 case 0x16a7: // steel wall 2 (bottom end)
5547 element = EL_DC_STEELWALL_2_BOTTOM;
5550 case 0x16a8: // steel wall 2 (left/right)
5551 element = EL_DC_STEELWALL_2_HORIZONTAL;
5554 case 0x16a9: // steel wall 2 (up/down)
5555 element = EL_DC_STEELWALL_2_VERTICAL;
5558 case 0x16aa: // steel wall 2 (mid)
5559 element = EL_DC_STEELWALL_2_MIDDLE;
5563 element = EL_SIGN_EXCLAMATION;
5567 element = EL_SIGN_RADIOACTIVITY;
5571 element = EL_SIGN_STOP;
5575 element = EL_SIGN_WHEELCHAIR;
5579 element = EL_SIGN_PARKING;
5583 element = EL_SIGN_NO_ENTRY;
5587 element = EL_SIGN_HEART;
5591 element = EL_SIGN_GIVE_WAY;
5595 element = EL_SIGN_ENTRY_FORBIDDEN;
5599 element = EL_SIGN_EMERGENCY_EXIT;
5603 element = EL_SIGN_YIN_YANG;
5607 element = EL_WALL_EMERALD;
5611 element = EL_WALL_DIAMOND;
5615 element = EL_WALL_PEARL;
5619 element = EL_WALL_CRYSTAL;
5623 element = EL_INVISIBLE_WALL;
5627 element = EL_INVISIBLE_STEELWALL;
5631 // EL_INVISIBLE_SAND
5634 element = EL_LIGHT_SWITCH;
5638 element = EL_ENVELOPE_1;
5642 if (element >= 0x0117 && element <= 0x036e) // (?)
5643 element = EL_DIAMOND;
5644 else if (element >= 0x042d && element <= 0x0684) // (?)
5645 element = EL_EMERALD;
5646 else if (element >= 0x157c && element <= 0x158b)
5648 else if (element >= 0x1590 && element <= 0x159f)
5649 element = EL_DC_LANDMINE;
5650 else if (element >= 0x16bc && element <= 0x16cb)
5651 element = EL_INVISIBLE_SAND;
5654 Warn("unknown Diamond Caves element 0x%04x", element);
5656 element = EL_UNKNOWN;
5661 return getMappedElement(element);
5664 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5667 byte header[DC_LEVEL_HEADER_SIZE];
5669 int envelope_header_pos = 62;
5670 int envelope_content_pos = 94;
5671 int level_name_pos = 251;
5672 int level_author_pos = 292;
5673 int envelope_header_len;
5674 int envelope_content_len;
5676 int level_author_len;
5678 int num_yamyam_contents;
5681 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5683 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5685 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5687 header[i * 2 + 0] = header_word >> 8;
5688 header[i * 2 + 1] = header_word & 0xff;
5691 // read some values from level header to check level decoding integrity
5692 fieldx = header[6] | (header[7] << 8);
5693 fieldy = header[8] | (header[9] << 8);
5694 num_yamyam_contents = header[60] | (header[61] << 8);
5696 // do some simple sanity checks to ensure that level was correctly decoded
5697 if (fieldx < 1 || fieldx > 256 ||
5698 fieldy < 1 || fieldy > 256 ||
5699 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5701 level->no_valid_file = TRUE;
5703 Warn("cannot decode level from stream -- using empty level");
5708 // maximum envelope header size is 31 bytes
5709 envelope_header_len = header[envelope_header_pos];
5710 // maximum envelope content size is 110 (156?) bytes
5711 envelope_content_len = header[envelope_content_pos];
5713 // maximum level title size is 40 bytes
5714 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5715 // maximum level author size is 30 (51?) bytes
5716 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5720 for (i = 0; i < envelope_header_len; i++)
5721 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5722 level->envelope[0].text[envelope_size++] =
5723 header[envelope_header_pos + 1 + i];
5725 if (envelope_header_len > 0 && envelope_content_len > 0)
5727 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5728 level->envelope[0].text[envelope_size++] = '\n';
5729 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5730 level->envelope[0].text[envelope_size++] = '\n';
5733 for (i = 0; i < envelope_content_len; i++)
5734 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5735 level->envelope[0].text[envelope_size++] =
5736 header[envelope_content_pos + 1 + i];
5738 level->envelope[0].text[envelope_size] = '\0';
5740 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5741 level->envelope[0].ysize = 10;
5742 level->envelope[0].autowrap = TRUE;
5743 level->envelope[0].centered = TRUE;
5745 for (i = 0; i < level_name_len; i++)
5746 level->name[i] = header[level_name_pos + 1 + i];
5747 level->name[level_name_len] = '\0';
5749 for (i = 0; i < level_author_len; i++)
5750 level->author[i] = header[level_author_pos + 1 + i];
5751 level->author[level_author_len] = '\0';
5753 num_yamyam_contents = header[60] | (header[61] << 8);
5754 level->num_yamyam_contents =
5755 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5757 for (i = 0; i < num_yamyam_contents; i++)
5759 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5761 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5762 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5764 if (i < MAX_ELEMENT_CONTENTS)
5765 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5769 fieldx = header[6] | (header[7] << 8);
5770 fieldy = header[8] | (header[9] << 8);
5771 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5772 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5774 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5776 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5777 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5779 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5780 level->field[x][y] = getMappedElement_DC(element_dc);
5783 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5784 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5785 level->field[x][y] = EL_PLAYER_1;
5787 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5788 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5789 level->field[x][y] = EL_PLAYER_2;
5791 level->gems_needed = header[18] | (header[19] << 8);
5793 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5794 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5795 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5796 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5797 level->score[SC_NUT] = header[28] | (header[29] << 8);
5798 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5799 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5800 level->score[SC_BUG] = header[34] | (header[35] << 8);
5801 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5802 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5803 level->score[SC_KEY] = header[40] | (header[41] << 8);
5804 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5806 level->time = header[44] | (header[45] << 8);
5808 level->amoeba_speed = header[46] | (header[47] << 8);
5809 level->time_light = header[48] | (header[49] << 8);
5810 level->time_timegate = header[50] | (header[51] << 8);
5811 level->time_wheel = header[52] | (header[53] << 8);
5812 level->time_magic_wall = header[54] | (header[55] << 8);
5813 level->extra_time = header[56] | (header[57] << 8);
5814 level->shield_normal_time = header[58] | (header[59] << 8);
5816 // shield and extra time elements do not have a score
5817 level->score[SC_SHIELD] = 0;
5818 level->extra_time_score = 0;
5820 // set time for normal and deadly shields to the same value
5821 level->shield_deadly_time = level->shield_normal_time;
5823 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5824 // can slip down from flat walls, like normal walls and steel walls
5825 level->em_slippery_gems = TRUE;
5827 // time score is counted for each 10 seconds left in Diamond Caves levels
5828 level->time_score_base = 10;
5831 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5832 struct LevelFileInfo *level_file_info,
5833 boolean level_info_only)
5835 char *filename = level_file_info->filename;
5837 int num_magic_bytes = 8;
5838 char magic_bytes[num_magic_bytes + 1];
5839 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5841 if (!(file = openFile(filename, MODE_READ)))
5843 level->no_valid_file = TRUE;
5845 if (!level_info_only)
5846 Warn("cannot read level '%s' -- using empty level", filename);
5851 // fseek(file, 0x0000, SEEK_SET);
5853 if (level_file_info->packed)
5855 // read "magic bytes" from start of file
5856 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5857 magic_bytes[0] = '\0';
5859 // check "magic bytes" for correct file format
5860 if (!strPrefix(magic_bytes, "DC2"))
5862 level->no_valid_file = TRUE;
5864 Warn("unknown DC level file '%s' -- using empty level", filename);
5869 if (strPrefix(magic_bytes, "DC2Win95") ||
5870 strPrefix(magic_bytes, "DC2Win98"))
5872 int position_first_level = 0x00fa;
5873 int extra_bytes = 4;
5876 // advance file stream to first level inside the level package
5877 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5879 // each block of level data is followed by block of non-level data
5880 num_levels_to_skip *= 2;
5882 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5883 while (num_levels_to_skip >= 0)
5885 // advance file stream to next level inside the level package
5886 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5888 level->no_valid_file = TRUE;
5890 Warn("cannot fseek in file '%s' -- using empty level", filename);
5895 // skip apparently unused extra bytes following each level
5896 ReadUnusedBytesFromFile(file, extra_bytes);
5898 // read size of next level in level package
5899 skip_bytes = getFile32BitLE(file);
5901 num_levels_to_skip--;
5906 level->no_valid_file = TRUE;
5908 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5914 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5920 // ----------------------------------------------------------------------------
5921 // functions for loading SB level
5922 // ----------------------------------------------------------------------------
5924 int getMappedElement_SB(int element_ascii, boolean use_ces)
5932 sb_element_mapping[] =
5934 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5935 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5936 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5937 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5938 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5939 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5940 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5941 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5948 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5949 if (element_ascii == sb_element_mapping[i].ascii)
5950 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5952 return EL_UNDEFINED;
5955 static void SetLevelSettings_SB(struct LevelInfo *level)
5959 level->use_step_counter = TRUE;
5962 level->score[SC_TIME_BONUS] = 0;
5963 level->time_score_base = 1;
5964 level->rate_time_over_score = TRUE;
5967 level->auto_exit_sokoban = TRUE;
5970 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5971 struct LevelFileInfo *level_file_info,
5972 boolean level_info_only)
5974 char *filename = level_file_info->filename;
5975 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5976 char last_comment[MAX_LINE_LEN];
5977 char level_name[MAX_LINE_LEN];
5980 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5981 boolean read_continued_line = FALSE;
5982 boolean reading_playfield = FALSE;
5983 boolean got_valid_playfield_line = FALSE;
5984 boolean invalid_playfield_char = FALSE;
5985 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5986 int file_level_nr = 0;
5988 int x = 0, y = 0; // initialized to make compilers happy
5990 last_comment[0] = '\0';
5991 level_name[0] = '\0';
5993 if (!(file = openFile(filename, MODE_READ)))
5995 level->no_valid_file = TRUE;
5997 if (!level_info_only)
5998 Warn("cannot read level '%s' -- using empty level", filename);
6003 while (!checkEndOfFile(file))
6005 // level successfully read, but next level may follow here
6006 if (!got_valid_playfield_line && reading_playfield)
6008 // read playfield from single level file -- skip remaining file
6009 if (!level_file_info->packed)
6012 if (file_level_nr >= num_levels_to_skip)
6017 last_comment[0] = '\0';
6018 level_name[0] = '\0';
6020 reading_playfield = FALSE;
6023 got_valid_playfield_line = FALSE;
6025 // read next line of input file
6026 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6029 // check if line was completely read and is terminated by line break
6030 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6033 // cut trailing line break (this can be newline and/or carriage return)
6034 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6035 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6038 // copy raw input line for later use (mainly debugging output)
6039 strcpy(line_raw, line);
6041 if (read_continued_line)
6043 // append new line to existing line, if there is enough space
6044 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6045 strcat(previous_line, line_ptr);
6047 strcpy(line, previous_line); // copy storage buffer to line
6049 read_continued_line = FALSE;
6052 // if the last character is '\', continue at next line
6053 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6055 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6056 strcpy(previous_line, line); // copy line to storage buffer
6058 read_continued_line = TRUE;
6064 if (line[0] == '\0')
6067 // extract comment text from comment line
6070 for (line_ptr = line; *line_ptr; line_ptr++)
6071 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6074 strcpy(last_comment, line_ptr);
6079 // extract level title text from line containing level title
6080 if (line[0] == '\'')
6082 strcpy(level_name, &line[1]);
6084 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6085 level_name[strlen(level_name) - 1] = '\0';
6090 // skip lines containing only spaces (or empty lines)
6091 for (line_ptr = line; *line_ptr; line_ptr++)
6092 if (*line_ptr != ' ')
6094 if (*line_ptr == '\0')
6097 // at this point, we have found a line containing part of a playfield
6099 got_valid_playfield_line = TRUE;
6101 if (!reading_playfield)
6103 reading_playfield = TRUE;
6104 invalid_playfield_char = FALSE;
6106 for (x = 0; x < MAX_LEV_FIELDX; x++)
6107 for (y = 0; y < MAX_LEV_FIELDY; y++)
6108 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6113 // start with topmost tile row
6117 // skip playfield line if larger row than allowed
6118 if (y >= MAX_LEV_FIELDY)
6121 // start with leftmost tile column
6124 // read playfield elements from line
6125 for (line_ptr = line; *line_ptr; line_ptr++)
6127 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6129 // stop parsing playfield line if larger column than allowed
6130 if (x >= MAX_LEV_FIELDX)
6133 if (mapped_sb_element == EL_UNDEFINED)
6135 invalid_playfield_char = TRUE;
6140 level->field[x][y] = mapped_sb_element;
6142 // continue with next tile column
6145 level->fieldx = MAX(x, level->fieldx);
6148 if (invalid_playfield_char)
6150 // if first playfield line, treat invalid lines as comment lines
6152 reading_playfield = FALSE;
6157 // continue with next tile row
6165 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6166 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6168 if (!reading_playfield)
6170 level->no_valid_file = TRUE;
6172 Warn("cannot read level '%s' -- using empty level", filename);
6177 if (*level_name != '\0')
6179 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6180 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6182 else if (*last_comment != '\0')
6184 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6185 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6189 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6192 // set all empty fields beyond the border walls to invisible steel wall
6193 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6195 if ((x == 0 || x == level->fieldx - 1 ||
6196 y == 0 || y == level->fieldy - 1) &&
6197 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6198 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6199 level->field, level->fieldx, level->fieldy);
6202 // set special level settings for Sokoban levels
6203 SetLevelSettings_SB(level);
6205 if (load_xsb_to_ces)
6207 // special global settings can now be set in level template
6208 level->use_custom_template = TRUE;
6213 // -------------------------------------------------------------------------
6214 // functions for handling native levels
6215 // -------------------------------------------------------------------------
6217 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6218 struct LevelFileInfo *level_file_info,
6219 boolean level_info_only)
6221 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6222 level->no_valid_file = TRUE;
6225 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6226 struct LevelFileInfo *level_file_info,
6227 boolean level_info_only)
6231 // determine position of requested level inside level package
6232 if (level_file_info->packed)
6233 pos = level_file_info->nr - leveldir_current->first_level;
6235 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6236 level->no_valid_file = TRUE;
6239 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6240 struct LevelFileInfo *level_file_info,
6241 boolean level_info_only)
6243 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6244 level->no_valid_file = TRUE;
6247 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6249 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6250 CopyNativeLevel_RND_to_EM(level);
6251 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6252 CopyNativeLevel_RND_to_SP(level);
6253 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6254 CopyNativeLevel_RND_to_MM(level);
6257 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6259 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6260 CopyNativeLevel_EM_to_RND(level);
6261 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6262 CopyNativeLevel_SP_to_RND(level);
6263 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6264 CopyNativeLevel_MM_to_RND(level);
6267 void SaveNativeLevel(struct LevelInfo *level)
6269 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6271 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6272 char *filename = getLevelFilenameFromBasename(basename);
6274 CopyNativeLevel_RND_to_SP(level);
6275 CopyNativeTape_RND_to_SP(level);
6277 SaveNativeLevel_SP(filename);
6282 // ----------------------------------------------------------------------------
6283 // functions for loading generic level
6284 // ----------------------------------------------------------------------------
6286 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6287 struct LevelFileInfo *level_file_info,
6288 boolean level_info_only)
6290 // always start with reliable default values
6291 setLevelInfoToDefaults(level, level_info_only, TRUE);
6293 switch (level_file_info->type)
6295 case LEVEL_FILE_TYPE_RND:
6296 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6299 case LEVEL_FILE_TYPE_EM:
6300 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6301 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6304 case LEVEL_FILE_TYPE_SP:
6305 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6306 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6309 case LEVEL_FILE_TYPE_MM:
6310 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6311 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6314 case LEVEL_FILE_TYPE_DC:
6315 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6318 case LEVEL_FILE_TYPE_SB:
6319 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6323 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6327 // if level file is invalid, restore level structure to default values
6328 if (level->no_valid_file)
6329 setLevelInfoToDefaults(level, level_info_only, FALSE);
6331 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6332 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6334 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6335 CopyNativeLevel_Native_to_RND(level);
6338 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6340 static struct LevelFileInfo level_file_info;
6342 // always start with reliable default values
6343 setFileInfoToDefaults(&level_file_info);
6345 level_file_info.nr = 0; // unknown level number
6346 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6348 setString(&level_file_info.filename, filename);
6350 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6353 static void LoadLevel_InitVersion(struct LevelInfo *level)
6357 if (leveldir_current == NULL) // only when dumping level
6360 // all engine modifications also valid for levels which use latest engine
6361 if (level->game_version < VERSION_IDENT(3,2,0,5))
6363 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6364 level->time_score_base = 10;
6367 if (leveldir_current->latest_engine)
6369 // ---------- use latest game engine --------------------------------------
6371 /* For all levels which are forced to use the latest game engine version
6372 (normally all but user contributed, private and undefined levels), set
6373 the game engine version to the actual version; this allows for actual
6374 corrections in the game engine to take effect for existing, converted
6375 levels (from "classic" or other existing games) to make the emulation
6376 of the corresponding game more accurate, while (hopefully) not breaking
6377 existing levels created from other players. */
6379 level->game_version = GAME_VERSION_ACTUAL;
6381 /* Set special EM style gems behaviour: EM style gems slip down from
6382 normal, steel and growing wall. As this is a more fundamental change,
6383 it seems better to set the default behaviour to "off" (as it is more
6384 natural) and make it configurable in the level editor (as a property
6385 of gem style elements). Already existing converted levels (neither
6386 private nor contributed levels) are changed to the new behaviour. */
6388 if (level->file_version < FILE_VERSION_2_0)
6389 level->em_slippery_gems = TRUE;
6394 // ---------- use game engine the level was created with --------------------
6396 /* For all levels which are not forced to use the latest game engine
6397 version (normally user contributed, private and undefined levels),
6398 use the version of the game engine the levels were created for.
6400 Since 2.0.1, the game engine version is now directly stored
6401 in the level file (chunk "VERS"), so there is no need anymore
6402 to set the game version from the file version (except for old,
6403 pre-2.0 levels, where the game version is still taken from the
6404 file format version used to store the level -- see above). */
6406 // player was faster than enemies in 1.0.0 and before
6407 if (level->file_version == FILE_VERSION_1_0)
6408 for (i = 0; i < MAX_PLAYERS; i++)
6409 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6411 // default behaviour for EM style gems was "slippery" only in 2.0.1
6412 if (level->game_version == VERSION_IDENT(2,0,1,0))
6413 level->em_slippery_gems = TRUE;
6415 // springs could be pushed over pits before (pre-release version) 2.2.0
6416 if (level->game_version < VERSION_IDENT(2,2,0,0))
6417 level->use_spring_bug = TRUE;
6419 if (level->game_version < VERSION_IDENT(3,2,0,5))
6421 // time orb caused limited time in endless time levels before 3.2.0-5
6422 level->use_time_orb_bug = TRUE;
6424 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6425 level->block_snap_field = FALSE;
6427 // extra time score was same value as time left score before 3.2.0-5
6428 level->extra_time_score = level->score[SC_TIME_BONUS];
6431 if (level->game_version < VERSION_IDENT(3,2,0,7))
6433 // default behaviour for snapping was "not continuous" before 3.2.0-7
6434 level->continuous_snapping = FALSE;
6437 // only few elements were able to actively move into acid before 3.1.0
6438 // trigger settings did not exist before 3.1.0; set to default "any"
6439 if (level->game_version < VERSION_IDENT(3,1,0,0))
6441 // correct "can move into acid" settings (all zero in old levels)
6443 level->can_move_into_acid_bits = 0; // nothing can move into acid
6444 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6446 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6447 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6448 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6449 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6452 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6454 // correct trigger settings (stored as zero == "none" in old levels)
6456 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6458 int element = EL_CUSTOM_START + i;
6459 struct ElementInfo *ei = &element_info[element];
6461 for (j = 0; j < ei->num_change_pages; j++)
6463 struct ElementChangeInfo *change = &ei->change_page[j];
6465 change->trigger_player = CH_PLAYER_ANY;
6466 change->trigger_page = CH_PAGE_ANY;
6471 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6473 int element = EL_CUSTOM_256;
6474 struct ElementInfo *ei = &element_info[element];
6475 struct ElementChangeInfo *change = &ei->change_page[0];
6477 /* This is needed to fix a problem that was caused by a bugfix in function
6478 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6479 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6480 not replace walkable elements, but instead just placed the player on it,
6481 without placing the Sokoban field under the player). Unfortunately, this
6482 breaks "Snake Bite" style levels when the snake is halfway through a door
6483 that just closes (the snake head is still alive and can be moved in this
6484 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6485 player (without Sokoban element) which then gets killed as designed). */
6487 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6488 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6489 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6490 change->target_element = EL_PLAYER_1;
6493 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6494 if (level->game_version < VERSION_IDENT(3,2,5,0))
6496 /* This is needed to fix a problem that was caused by a bugfix in function
6497 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6498 corrects the behaviour when a custom element changes to another custom
6499 element with a higher element number that has change actions defined.
6500 Normally, only one change per frame is allowed for custom elements.
6501 Therefore, it is checked if a custom element already changed in the
6502 current frame; if it did, subsequent changes are suppressed.
6503 Unfortunately, this is only checked for element changes, but not for
6504 change actions, which are still executed. As the function above loops
6505 through all custom elements from lower to higher, an element change
6506 resulting in a lower CE number won't be checked again, while a target
6507 element with a higher number will also be checked, and potential change
6508 actions will get executed for this CE, too (which is wrong), while
6509 further changes are ignored (which is correct). As this bugfix breaks
6510 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6511 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6512 behaviour for existing levels and tapes that make use of this bug */
6514 level->use_action_after_change_bug = TRUE;
6517 // not centering level after relocating player was default only in 3.2.3
6518 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6519 level->shifted_relocation = TRUE;
6521 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6522 if (level->game_version < VERSION_IDENT(3,2,6,0))
6523 level->em_explodes_by_fire = TRUE;
6525 // levels were solved by the first player entering an exit up to 4.1.0.0
6526 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6527 level->solved_by_one_player = TRUE;
6529 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6530 if (level->game_version < VERSION_IDENT(4,1,1,1))
6531 level->use_life_bugs = TRUE;
6533 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6534 if (level->game_version < VERSION_IDENT(4,1,1,1))
6535 level->sb_objects_needed = FALSE;
6537 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6538 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6539 level->finish_dig_collect = FALSE;
6541 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6542 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6543 level->keep_walkable_ce = TRUE;
6546 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6548 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6551 // check if this level is (not) a Sokoban level
6552 for (y = 0; y < level->fieldy; y++)
6553 for (x = 0; x < level->fieldx; x++)
6554 if (!IS_SB_ELEMENT(Tile[x][y]))
6555 is_sokoban_level = FALSE;
6557 if (is_sokoban_level)
6559 // set special level settings for Sokoban levels
6560 SetLevelSettings_SB(level);
6564 static void LoadLevel_InitSettings(struct LevelInfo *level)
6566 // adjust level settings for (non-native) Sokoban-style levels
6567 LoadLevel_InitSettings_SB(level);
6570 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6574 // map elements that have changed in newer versions
6575 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6576 level->game_version);
6577 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6578 for (x = 0; x < 3; x++)
6579 for (y = 0; y < 3; y++)
6580 level->yamyam_content[i].e[x][y] =
6581 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6582 level->game_version);
6586 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6590 // map custom element change events that have changed in newer versions
6591 // (these following values were accidentally changed in version 3.0.1)
6592 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6593 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6597 int element = EL_CUSTOM_START + i;
6599 // order of checking and copying events to be mapped is important
6600 // (do not change the start and end value -- they are constant)
6601 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6603 if (HAS_CHANGE_EVENT(element, j - 2))
6605 SET_CHANGE_EVENT(element, j - 2, FALSE);
6606 SET_CHANGE_EVENT(element, j, TRUE);
6610 // order of checking and copying events to be mapped is important
6611 // (do not change the start and end value -- they are constant)
6612 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6614 if (HAS_CHANGE_EVENT(element, j - 1))
6616 SET_CHANGE_EVENT(element, j - 1, FALSE);
6617 SET_CHANGE_EVENT(element, j, TRUE);
6623 // initialize "can_change" field for old levels with only one change page
6624 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6626 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6628 int element = EL_CUSTOM_START + i;
6630 if (CAN_CHANGE(element))
6631 element_info[element].change->can_change = TRUE;
6635 // correct custom element values (for old levels without these options)
6636 if (level->game_version < VERSION_IDENT(3,1,1,0))
6638 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6640 int element = EL_CUSTOM_START + i;
6641 struct ElementInfo *ei = &element_info[element];
6643 if (ei->access_direction == MV_NO_DIRECTION)
6644 ei->access_direction = MV_ALL_DIRECTIONS;
6648 // correct custom element values (fix invalid values for all versions)
6651 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6653 int element = EL_CUSTOM_START + i;
6654 struct ElementInfo *ei = &element_info[element];
6656 for (j = 0; j < ei->num_change_pages; j++)
6658 struct ElementChangeInfo *change = &ei->change_page[j];
6660 if (change->trigger_player == CH_PLAYER_NONE)
6661 change->trigger_player = CH_PLAYER_ANY;
6663 if (change->trigger_side == CH_SIDE_NONE)
6664 change->trigger_side = CH_SIDE_ANY;
6669 // initialize "can_explode" field for old levels which did not store this
6670 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6671 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6673 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6675 int element = EL_CUSTOM_START + i;
6677 if (EXPLODES_1X1_OLD(element))
6678 element_info[element].explosion_type = EXPLODES_1X1;
6680 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6681 EXPLODES_SMASHED(element) ||
6682 EXPLODES_IMPACT(element)));
6686 // correct previously hard-coded move delay values for maze runner style
6687 if (level->game_version < VERSION_IDENT(3,1,1,0))
6689 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6691 int element = EL_CUSTOM_START + i;
6693 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6695 // previously hard-coded and therefore ignored
6696 element_info[element].move_delay_fixed = 9;
6697 element_info[element].move_delay_random = 0;
6702 // set some other uninitialized values of custom elements in older levels
6703 if (level->game_version < VERSION_IDENT(3,1,0,0))
6705 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6707 int element = EL_CUSTOM_START + i;
6709 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6711 element_info[element].explosion_delay = 17;
6712 element_info[element].ignition_delay = 8;
6716 // set mouse click change events to work for left/middle/right mouse button
6717 if (level->game_version < VERSION_IDENT(4,2,3,0))
6719 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6721 int element = EL_CUSTOM_START + i;
6722 struct ElementInfo *ei = &element_info[element];
6724 for (j = 0; j < ei->num_change_pages; j++)
6726 struct ElementChangeInfo *change = &ei->change_page[j];
6728 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6729 change->has_event[CE_PRESSED_BY_MOUSE] ||
6730 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6731 change->has_event[CE_MOUSE_PRESSED_ON_X])
6732 change->trigger_side = CH_SIDE_ANY;
6738 static void LoadLevel_InitElements(struct LevelInfo *level)
6740 LoadLevel_InitStandardElements(level);
6742 if (level->file_has_custom_elements)
6743 LoadLevel_InitCustomElements(level);
6745 // initialize element properties for level editor etc.
6746 InitElementPropertiesEngine(level->game_version);
6747 InitElementPropertiesGfxElement();
6750 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6754 // map elements that have changed in newer versions
6755 for (y = 0; y < level->fieldy; y++)
6756 for (x = 0; x < level->fieldx; x++)
6757 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6758 level->game_version);
6760 // clear unused playfield data (nicer if level gets resized in editor)
6761 for (x = 0; x < MAX_LEV_FIELDX; x++)
6762 for (y = 0; y < MAX_LEV_FIELDY; y++)
6763 if (x >= level->fieldx || y >= level->fieldy)
6764 level->field[x][y] = EL_EMPTY;
6766 // copy elements to runtime playfield array
6767 for (x = 0; x < MAX_LEV_FIELDX; x++)
6768 for (y = 0; y < MAX_LEV_FIELDY; y++)
6769 Tile[x][y] = level->field[x][y];
6771 // initialize level size variables for faster access
6772 lev_fieldx = level->fieldx;
6773 lev_fieldy = level->fieldy;
6775 // determine border element for this level
6776 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6777 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6782 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6784 struct LevelFileInfo *level_file_info = &level->file_info;
6786 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6787 CopyNativeLevel_RND_to_Native(level);
6790 static void LoadLevelTemplate_LoadAndInit(void)
6792 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6794 LoadLevel_InitVersion(&level_template);
6795 LoadLevel_InitElements(&level_template);
6796 LoadLevel_InitSettings(&level_template);
6798 ActivateLevelTemplate();
6801 void LoadLevelTemplate(int nr)
6803 if (!fileExists(getGlobalLevelTemplateFilename()))
6805 Warn("no level template found for this level");
6810 setLevelFileInfo(&level_template.file_info, nr);
6812 LoadLevelTemplate_LoadAndInit();
6815 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6817 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6819 LoadLevelTemplate_LoadAndInit();
6822 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6824 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6826 if (level.use_custom_template)
6828 if (network_level != NULL)
6829 LoadNetworkLevelTemplate(network_level);
6831 LoadLevelTemplate(-1);
6834 LoadLevel_InitVersion(&level);
6835 LoadLevel_InitElements(&level);
6836 LoadLevel_InitPlayfield(&level);
6837 LoadLevel_InitSettings(&level);
6839 LoadLevel_InitNativeEngines(&level);
6842 void LoadLevel(int nr)
6844 SetLevelSetInfo(leveldir_current->identifier, nr);
6846 setLevelFileInfo(&level.file_info, nr);
6848 LoadLevel_LoadAndInit(NULL);
6851 void LoadLevelInfoOnly(int nr)
6853 setLevelFileInfo(&level.file_info, nr);
6855 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6858 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6860 SetLevelSetInfo(network_level->leveldir_identifier,
6861 network_level->file_info.nr);
6863 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6865 LoadLevel_LoadAndInit(network_level);
6868 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6872 chunk_size += putFileVersion(file, level->file_version);
6873 chunk_size += putFileVersion(file, level->game_version);
6878 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6882 chunk_size += putFile16BitBE(file, level->creation_date.year);
6883 chunk_size += putFile8Bit(file, level->creation_date.month);
6884 chunk_size += putFile8Bit(file, level->creation_date.day);
6889 #if ENABLE_HISTORIC_CHUNKS
6890 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6894 putFile8Bit(file, level->fieldx);
6895 putFile8Bit(file, level->fieldy);
6897 putFile16BitBE(file, level->time);
6898 putFile16BitBE(file, level->gems_needed);
6900 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6901 putFile8Bit(file, level->name[i]);
6903 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6904 putFile8Bit(file, level->score[i]);
6906 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6907 for (y = 0; y < 3; y++)
6908 for (x = 0; x < 3; x++)
6909 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6910 level->yamyam_content[i].e[x][y]));
6911 putFile8Bit(file, level->amoeba_speed);
6912 putFile8Bit(file, level->time_magic_wall);
6913 putFile8Bit(file, level->time_wheel);
6914 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6915 level->amoeba_content));
6916 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6917 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6918 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6919 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6921 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6923 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6924 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6925 putFile32BitBE(file, level->can_move_into_acid_bits);
6926 putFile8Bit(file, level->dont_collide_with_bits);
6928 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6929 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6931 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6932 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6933 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6935 putFile8Bit(file, level->game_engine_type);
6937 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6941 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6946 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6947 chunk_size += putFile8Bit(file, level->name[i]);
6952 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6957 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6958 chunk_size += putFile8Bit(file, level->author[i]);
6963 #if ENABLE_HISTORIC_CHUNKS
6964 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6969 for (y = 0; y < level->fieldy; y++)
6970 for (x = 0; x < level->fieldx; x++)
6971 if (level->encoding_16bit_field)
6972 chunk_size += putFile16BitBE(file, level->field[x][y]);
6974 chunk_size += putFile8Bit(file, level->field[x][y]);
6980 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6985 for (y = 0; y < level->fieldy; y++)
6986 for (x = 0; x < level->fieldx; x++)
6987 chunk_size += putFile16BitBE(file, level->field[x][y]);
6992 #if ENABLE_HISTORIC_CHUNKS
6993 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6997 putFile8Bit(file, EL_YAMYAM);
6998 putFile8Bit(file, level->num_yamyam_contents);
6999 putFile8Bit(file, 0);
7000 putFile8Bit(file, 0);
7002 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7003 for (y = 0; y < 3; y++)
7004 for (x = 0; x < 3; x++)
7005 if (level->encoding_16bit_field)
7006 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7008 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7012 #if ENABLE_HISTORIC_CHUNKS
7013 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7016 int num_contents, content_xsize, content_ysize;
7017 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7019 if (element == EL_YAMYAM)
7021 num_contents = level->num_yamyam_contents;
7025 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7026 for (y = 0; y < 3; y++)
7027 for (x = 0; x < 3; x++)
7028 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7030 else if (element == EL_BD_AMOEBA)
7036 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7037 for (y = 0; y < 3; y++)
7038 for (x = 0; x < 3; x++)
7039 content_array[i][x][y] = EL_EMPTY;
7040 content_array[0][0][0] = level->amoeba_content;
7044 // chunk header already written -- write empty chunk data
7045 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7047 Warn("cannot save content for element '%d'", element);
7052 putFile16BitBE(file, element);
7053 putFile8Bit(file, num_contents);
7054 putFile8Bit(file, content_xsize);
7055 putFile8Bit(file, content_ysize);
7057 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7059 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7060 for (y = 0; y < 3; y++)
7061 for (x = 0; x < 3; x++)
7062 putFile16BitBE(file, content_array[i][x][y]);
7066 #if ENABLE_HISTORIC_CHUNKS
7067 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7069 int envelope_nr = element - EL_ENVELOPE_1;
7070 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7074 chunk_size += putFile16BitBE(file, element);
7075 chunk_size += putFile16BitBE(file, envelope_len);
7076 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7077 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7079 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7080 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7082 for (i = 0; i < envelope_len; i++)
7083 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7089 #if ENABLE_HISTORIC_CHUNKS
7090 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7091 int num_changed_custom_elements)
7095 putFile16BitBE(file, num_changed_custom_elements);
7097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7099 int element = EL_CUSTOM_START + i;
7101 struct ElementInfo *ei = &element_info[element];
7103 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7105 if (check < num_changed_custom_elements)
7107 putFile16BitBE(file, element);
7108 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7115 if (check != num_changed_custom_elements) // should not happen
7116 Warn("inconsistent number of custom element properties");
7120 #if ENABLE_HISTORIC_CHUNKS
7121 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7122 int num_changed_custom_elements)
7126 putFile16BitBE(file, num_changed_custom_elements);
7128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7130 int element = EL_CUSTOM_START + i;
7132 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7134 if (check < num_changed_custom_elements)
7136 putFile16BitBE(file, element);
7137 putFile16BitBE(file, element_info[element].change->target_element);
7144 if (check != num_changed_custom_elements) // should not happen
7145 Warn("inconsistent number of custom target elements");
7149 #if ENABLE_HISTORIC_CHUNKS
7150 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7151 int num_changed_custom_elements)
7153 int i, j, x, y, check = 0;
7155 putFile16BitBE(file, num_changed_custom_elements);
7157 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7159 int element = EL_CUSTOM_START + i;
7160 struct ElementInfo *ei = &element_info[element];
7162 if (ei->modified_settings)
7164 if (check < num_changed_custom_elements)
7166 putFile16BitBE(file, element);
7168 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7169 putFile8Bit(file, ei->description[j]);
7171 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7173 // some free bytes for future properties and padding
7174 WriteUnusedBytesToFile(file, 7);
7176 putFile8Bit(file, ei->use_gfx_element);
7177 putFile16BitBE(file, ei->gfx_element_initial);
7179 putFile8Bit(file, ei->collect_score_initial);
7180 putFile8Bit(file, ei->collect_count_initial);
7182 putFile16BitBE(file, ei->push_delay_fixed);
7183 putFile16BitBE(file, ei->push_delay_random);
7184 putFile16BitBE(file, ei->move_delay_fixed);
7185 putFile16BitBE(file, ei->move_delay_random);
7187 putFile16BitBE(file, ei->move_pattern);
7188 putFile8Bit(file, ei->move_direction_initial);
7189 putFile8Bit(file, ei->move_stepsize);
7191 for (y = 0; y < 3; y++)
7192 for (x = 0; x < 3; x++)
7193 putFile16BitBE(file, ei->content.e[x][y]);
7195 putFile32BitBE(file, ei->change->events);
7197 putFile16BitBE(file, ei->change->target_element);
7199 putFile16BitBE(file, ei->change->delay_fixed);
7200 putFile16BitBE(file, ei->change->delay_random);
7201 putFile16BitBE(file, ei->change->delay_frames);
7203 putFile16BitBE(file, ei->change->initial_trigger_element);
7205 putFile8Bit(file, ei->change->explode);
7206 putFile8Bit(file, ei->change->use_target_content);
7207 putFile8Bit(file, ei->change->only_if_complete);
7208 putFile8Bit(file, ei->change->use_random_replace);
7210 putFile8Bit(file, ei->change->random_percentage);
7211 putFile8Bit(file, ei->change->replace_when);
7213 for (y = 0; y < 3; y++)
7214 for (x = 0; x < 3; x++)
7215 putFile16BitBE(file, ei->change->content.e[x][y]);
7217 putFile8Bit(file, ei->slippery_type);
7219 // some free bytes for future properties and padding
7220 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7227 if (check != num_changed_custom_elements) // should not happen
7228 Warn("inconsistent number of custom element properties");
7232 #if ENABLE_HISTORIC_CHUNKS
7233 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7235 struct ElementInfo *ei = &element_info[element];
7238 // ---------- custom element base property values (96 bytes) ----------------
7240 putFile16BitBE(file, element);
7242 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7243 putFile8Bit(file, ei->description[i]);
7245 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7247 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7249 putFile8Bit(file, ei->num_change_pages);
7251 putFile16BitBE(file, ei->ce_value_fixed_initial);
7252 putFile16BitBE(file, ei->ce_value_random_initial);
7253 putFile8Bit(file, ei->use_last_ce_value);
7255 putFile8Bit(file, ei->use_gfx_element);
7256 putFile16BitBE(file, ei->gfx_element_initial);
7258 putFile8Bit(file, ei->collect_score_initial);
7259 putFile8Bit(file, ei->collect_count_initial);
7261 putFile8Bit(file, ei->drop_delay_fixed);
7262 putFile8Bit(file, ei->push_delay_fixed);
7263 putFile8Bit(file, ei->drop_delay_random);
7264 putFile8Bit(file, ei->push_delay_random);
7265 putFile16BitBE(file, ei->move_delay_fixed);
7266 putFile16BitBE(file, ei->move_delay_random);
7268 // bits 0 - 15 of "move_pattern" ...
7269 putFile16BitBE(file, ei->move_pattern & 0xffff);
7270 putFile8Bit(file, ei->move_direction_initial);
7271 putFile8Bit(file, ei->move_stepsize);
7273 putFile8Bit(file, ei->slippery_type);
7275 for (y = 0; y < 3; y++)
7276 for (x = 0; x < 3; x++)
7277 putFile16BitBE(file, ei->content.e[x][y]);
7279 putFile16BitBE(file, ei->move_enter_element);
7280 putFile16BitBE(file, ei->move_leave_element);
7281 putFile8Bit(file, ei->move_leave_type);
7283 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7284 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7286 putFile8Bit(file, ei->access_direction);
7288 putFile8Bit(file, ei->explosion_delay);
7289 putFile8Bit(file, ei->ignition_delay);
7290 putFile8Bit(file, ei->explosion_type);
7292 // some free bytes for future custom property values and padding
7293 WriteUnusedBytesToFile(file, 1);
7295 // ---------- change page property values (48 bytes) ------------------------
7297 for (i = 0; i < ei->num_change_pages; i++)
7299 struct ElementChangeInfo *change = &ei->change_page[i];
7300 unsigned int event_bits;
7302 // bits 0 - 31 of "has_event[]" ...
7304 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7305 if (change->has_event[j])
7306 event_bits |= (1 << j);
7307 putFile32BitBE(file, event_bits);
7309 putFile16BitBE(file, change->target_element);
7311 putFile16BitBE(file, change->delay_fixed);
7312 putFile16BitBE(file, change->delay_random);
7313 putFile16BitBE(file, change->delay_frames);
7315 putFile16BitBE(file, change->initial_trigger_element);
7317 putFile8Bit(file, change->explode);
7318 putFile8Bit(file, change->use_target_content);
7319 putFile8Bit(file, change->only_if_complete);
7320 putFile8Bit(file, change->use_random_replace);
7322 putFile8Bit(file, change->random_percentage);
7323 putFile8Bit(file, change->replace_when);
7325 for (y = 0; y < 3; y++)
7326 for (x = 0; x < 3; x++)
7327 putFile16BitBE(file, change->target_content.e[x][y]);
7329 putFile8Bit(file, change->can_change);
7331 putFile8Bit(file, change->trigger_side);
7333 putFile8Bit(file, change->trigger_player);
7334 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7335 log_2(change->trigger_page)));
7337 putFile8Bit(file, change->has_action);
7338 putFile8Bit(file, change->action_type);
7339 putFile8Bit(file, change->action_mode);
7340 putFile16BitBE(file, change->action_arg);
7342 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7344 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7345 if (change->has_event[j])
7346 event_bits |= (1 << (j - 32));
7347 putFile8Bit(file, event_bits);
7352 #if ENABLE_HISTORIC_CHUNKS
7353 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7355 struct ElementInfo *ei = &element_info[element];
7356 struct ElementGroupInfo *group = ei->group;
7359 putFile16BitBE(file, element);
7361 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7362 putFile8Bit(file, ei->description[i]);
7364 putFile8Bit(file, group->num_elements);
7366 putFile8Bit(file, ei->use_gfx_element);
7367 putFile16BitBE(file, ei->gfx_element_initial);
7369 putFile8Bit(file, group->choice_mode);
7371 // some free bytes for future values and padding
7372 WriteUnusedBytesToFile(file, 3);
7374 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7375 putFile16BitBE(file, group->element[i]);
7379 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7380 boolean write_element)
7382 int save_type = entry->save_type;
7383 int data_type = entry->data_type;
7384 int conf_type = entry->conf_type;
7385 int byte_mask = conf_type & CONF_MASK_BYTES;
7386 int element = entry->element;
7387 int default_value = entry->default_value;
7389 boolean modified = FALSE;
7391 if (byte_mask != CONF_MASK_MULTI_BYTES)
7393 void *value_ptr = entry->value;
7394 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7397 // check if any settings have been modified before saving them
7398 if (value != default_value)
7401 // do not save if explicitly told or if unmodified default settings
7402 if ((save_type == SAVE_CONF_NEVER) ||
7403 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7407 num_bytes += putFile16BitBE(file, element);
7409 num_bytes += putFile8Bit(file, conf_type);
7410 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7411 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7412 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7415 else if (data_type == TYPE_STRING)
7417 char *default_string = entry->default_string;
7418 char *string = (char *)(entry->value);
7419 int string_length = strlen(string);
7422 // check if any settings have been modified before saving them
7423 if (!strEqual(string, default_string))
7426 // do not save if explicitly told or if unmodified default settings
7427 if ((save_type == SAVE_CONF_NEVER) ||
7428 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7432 num_bytes += putFile16BitBE(file, element);
7434 num_bytes += putFile8Bit(file, conf_type);
7435 num_bytes += putFile16BitBE(file, string_length);
7437 for (i = 0; i < string_length; i++)
7438 num_bytes += putFile8Bit(file, string[i]);
7440 else if (data_type == TYPE_ELEMENT_LIST)
7442 int *element_array = (int *)(entry->value);
7443 int num_elements = *(int *)(entry->num_entities);
7446 // check if any settings have been modified before saving them
7447 for (i = 0; i < num_elements; i++)
7448 if (element_array[i] != default_value)
7451 // do not save if explicitly told or if unmodified default settings
7452 if ((save_type == SAVE_CONF_NEVER) ||
7453 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7457 num_bytes += putFile16BitBE(file, element);
7459 num_bytes += putFile8Bit(file, conf_type);
7460 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7462 for (i = 0; i < num_elements; i++)
7463 num_bytes += putFile16BitBE(file, element_array[i]);
7465 else if (data_type == TYPE_CONTENT_LIST)
7467 struct Content *content = (struct Content *)(entry->value);
7468 int num_contents = *(int *)(entry->num_entities);
7471 // check if any settings have been modified before saving them
7472 for (i = 0; i < num_contents; i++)
7473 for (y = 0; y < 3; y++)
7474 for (x = 0; x < 3; x++)
7475 if (content[i].e[x][y] != default_value)
7478 // do not save if explicitly told or if unmodified default settings
7479 if ((save_type == SAVE_CONF_NEVER) ||
7480 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7484 num_bytes += putFile16BitBE(file, element);
7486 num_bytes += putFile8Bit(file, conf_type);
7487 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7489 for (i = 0; i < num_contents; i++)
7490 for (y = 0; y < 3; y++)
7491 for (x = 0; x < 3; x++)
7492 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7498 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7503 li = *level; // copy level data into temporary buffer
7505 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7506 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7511 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7516 li = *level; // copy level data into temporary buffer
7518 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7519 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7524 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7526 int envelope_nr = element - EL_ENVELOPE_1;
7530 chunk_size += putFile16BitBE(file, element);
7532 // copy envelope data into temporary buffer
7533 xx_envelope = level->envelope[envelope_nr];
7535 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7536 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7541 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7543 struct ElementInfo *ei = &element_info[element];
7547 chunk_size += putFile16BitBE(file, element);
7549 xx_ei = *ei; // copy element data into temporary buffer
7551 // set default description string for this specific element
7552 strcpy(xx_default_description, getDefaultElementDescription(ei));
7554 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7555 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7557 for (i = 0; i < ei->num_change_pages; i++)
7559 struct ElementChangeInfo *change = &ei->change_page[i];
7561 xx_current_change_page = i;
7563 xx_change = *change; // copy change data into temporary buffer
7566 setEventBitsFromEventFlags(change);
7568 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7569 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7576 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7578 struct ElementInfo *ei = &element_info[element];
7579 struct ElementGroupInfo *group = ei->group;
7583 chunk_size += putFile16BitBE(file, element);
7585 xx_ei = *ei; // copy element data into temporary buffer
7586 xx_group = *group; // copy group data into temporary buffer
7588 // set default description string for this specific element
7589 strcpy(xx_default_description, getDefaultElementDescription(ei));
7591 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7592 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7597 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7599 struct ElementInfo *ei = &element_info[element];
7603 chunk_size += putFile16BitBE(file, element);
7605 xx_ei = *ei; // copy element data into temporary buffer
7607 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7608 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7613 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7614 boolean save_as_template)
7620 if (!(file = fopen(filename, MODE_WRITE)))
7622 Warn("cannot save level file '%s'", filename);
7627 level->file_version = FILE_VERSION_ACTUAL;
7628 level->game_version = GAME_VERSION_ACTUAL;
7630 level->creation_date = getCurrentDate();
7632 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7633 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7635 chunk_size = SaveLevel_VERS(NULL, level);
7636 putFileChunkBE(file, "VERS", chunk_size);
7637 SaveLevel_VERS(file, level);
7639 chunk_size = SaveLevel_DATE(NULL, level);
7640 putFileChunkBE(file, "DATE", chunk_size);
7641 SaveLevel_DATE(file, level);
7643 chunk_size = SaveLevel_NAME(NULL, level);
7644 putFileChunkBE(file, "NAME", chunk_size);
7645 SaveLevel_NAME(file, level);
7647 chunk_size = SaveLevel_AUTH(NULL, level);
7648 putFileChunkBE(file, "AUTH", chunk_size);
7649 SaveLevel_AUTH(file, level);
7651 chunk_size = SaveLevel_INFO(NULL, level);
7652 putFileChunkBE(file, "INFO", chunk_size);
7653 SaveLevel_INFO(file, level);
7655 chunk_size = SaveLevel_BODY(NULL, level);
7656 putFileChunkBE(file, "BODY", chunk_size);
7657 SaveLevel_BODY(file, level);
7659 chunk_size = SaveLevel_ELEM(NULL, level);
7660 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7662 putFileChunkBE(file, "ELEM", chunk_size);
7663 SaveLevel_ELEM(file, level);
7666 for (i = 0; i < NUM_ENVELOPES; i++)
7668 int element = EL_ENVELOPE_1 + i;
7670 chunk_size = SaveLevel_NOTE(NULL, level, element);
7671 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7673 putFileChunkBE(file, "NOTE", chunk_size);
7674 SaveLevel_NOTE(file, level, element);
7678 // if not using template level, check for non-default custom/group elements
7679 if (!level->use_custom_template || save_as_template)
7681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7683 int element = EL_CUSTOM_START + i;
7685 chunk_size = SaveLevel_CUSX(NULL, level, element);
7686 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7688 putFileChunkBE(file, "CUSX", chunk_size);
7689 SaveLevel_CUSX(file, level, element);
7693 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7695 int element = EL_GROUP_START + i;
7697 chunk_size = SaveLevel_GRPX(NULL, level, element);
7698 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7700 putFileChunkBE(file, "GRPX", chunk_size);
7701 SaveLevel_GRPX(file, level, element);
7705 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7707 int element = GET_EMPTY_ELEMENT(i);
7709 chunk_size = SaveLevel_EMPX(NULL, level, element);
7710 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7712 putFileChunkBE(file, "EMPX", chunk_size);
7713 SaveLevel_EMPX(file, level, element);
7720 SetFilePermissions(filename, PERMS_PRIVATE);
7723 void SaveLevel(int nr)
7725 char *filename = getDefaultLevelFilename(nr);
7727 SaveLevelFromFilename(&level, filename, FALSE);
7730 void SaveLevelTemplate(void)
7732 char *filename = getLocalLevelTemplateFilename();
7734 SaveLevelFromFilename(&level, filename, TRUE);
7737 boolean SaveLevelChecked(int nr)
7739 char *filename = getDefaultLevelFilename(nr);
7740 boolean new_level = !fileExists(filename);
7741 boolean level_saved = FALSE;
7743 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7748 Request("Level saved!", REQ_CONFIRM);
7756 void DumpLevel(struct LevelInfo *level)
7758 if (level->no_level_file || level->no_valid_file)
7760 Warn("cannot dump -- no valid level file found");
7766 Print("Level xxx (file version %08d, game version %08d)\n",
7767 level->file_version, level->game_version);
7770 Print("Level author: '%s'\n", level->author);
7771 Print("Level title: '%s'\n", level->name);
7773 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7775 Print("Level time: %d seconds\n", level->time);
7776 Print("Gems needed: %d\n", level->gems_needed);
7778 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7779 Print("Time for wheel: %d seconds\n", level->time_wheel);
7780 Print("Time for light: %d seconds\n", level->time_light);
7781 Print("Time for timegate: %d seconds\n", level->time_timegate);
7783 Print("Amoeba speed: %d\n", level->amoeba_speed);
7786 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7787 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7788 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7789 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7790 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7791 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7796 void DumpLevels(void)
7798 static LevelDirTree *dumplevel_leveldir = NULL;
7800 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7801 global.dumplevel_leveldir);
7803 if (dumplevel_leveldir == NULL)
7804 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7806 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7807 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7808 Fail("no such level number: %d", global.dumplevel_level_nr);
7810 leveldir_current = dumplevel_leveldir;
7812 LoadLevel(global.dumplevel_level_nr);
7819 // ============================================================================
7820 // tape file functions
7821 // ============================================================================
7823 static void setTapeInfoToDefaults(void)
7827 // always start with reliable default values (empty tape)
7830 // default values (also for pre-1.2 tapes) with only the first player
7831 tape.player_participates[0] = TRUE;
7832 for (i = 1; i < MAX_PLAYERS; i++)
7833 tape.player_participates[i] = FALSE;
7835 // at least one (default: the first) player participates in every tape
7836 tape.num_participating_players = 1;
7838 tape.property_bits = TAPE_PROPERTY_NONE;
7840 tape.level_nr = level_nr;
7842 tape.changed = FALSE;
7844 tape.recording = FALSE;
7845 tape.playing = FALSE;
7846 tape.pausing = FALSE;
7848 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7849 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7851 tape.no_info_chunk = TRUE;
7852 tape.no_valid_file = FALSE;
7855 static int getTapePosSize(struct TapeInfo *tape)
7857 int tape_pos_size = 0;
7859 if (tape->use_key_actions)
7860 tape_pos_size += tape->num_participating_players;
7862 if (tape->use_mouse_actions)
7863 tape_pos_size += 3; // x and y position and mouse button mask
7865 tape_pos_size += 1; // tape action delay value
7867 return tape_pos_size;
7870 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7872 tape->use_key_actions = FALSE;
7873 tape->use_mouse_actions = FALSE;
7875 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7876 tape->use_key_actions = TRUE;
7878 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7879 tape->use_mouse_actions = TRUE;
7882 static int getTapeActionValue(struct TapeInfo *tape)
7884 return (tape->use_key_actions &&
7885 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7886 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7887 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7888 TAPE_ACTIONS_DEFAULT);
7891 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7893 tape->file_version = getFileVersion(file);
7894 tape->game_version = getFileVersion(file);
7899 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7903 tape->random_seed = getFile32BitBE(file);
7904 tape->date = getFile32BitBE(file);
7905 tape->length = getFile32BitBE(file);
7907 // read header fields that are new since version 1.2
7908 if (tape->file_version >= FILE_VERSION_1_2)
7910 byte store_participating_players = getFile8Bit(file);
7913 // since version 1.2, tapes store which players participate in the tape
7914 tape->num_participating_players = 0;
7915 for (i = 0; i < MAX_PLAYERS; i++)
7917 tape->player_participates[i] = FALSE;
7919 if (store_participating_players & (1 << i))
7921 tape->player_participates[i] = TRUE;
7922 tape->num_participating_players++;
7926 setTapeActionFlags(tape, getFile8Bit(file));
7928 tape->property_bits = getFile8Bit(file);
7930 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7932 engine_version = getFileVersion(file);
7933 if (engine_version > 0)
7934 tape->engine_version = engine_version;
7936 tape->engine_version = tape->game_version;
7942 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7944 tape->scr_fieldx = getFile8Bit(file);
7945 tape->scr_fieldy = getFile8Bit(file);
7950 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7952 char *level_identifier = NULL;
7953 int level_identifier_size;
7956 tape->no_info_chunk = FALSE;
7958 level_identifier_size = getFile16BitBE(file);
7960 level_identifier = checked_malloc(level_identifier_size);
7962 for (i = 0; i < level_identifier_size; i++)
7963 level_identifier[i] = getFile8Bit(file);
7965 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7966 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7968 checked_free(level_identifier);
7970 tape->level_nr = getFile16BitBE(file);
7972 chunk_size = 2 + level_identifier_size + 2;
7977 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7980 int tape_pos_size = getTapePosSize(tape);
7981 int chunk_size_expected = tape_pos_size * tape->length;
7983 if (chunk_size_expected != chunk_size)
7985 ReadUnusedBytesFromFile(file, chunk_size);
7986 return chunk_size_expected;
7989 for (i = 0; i < tape->length; i++)
7991 if (i >= MAX_TAPE_LEN)
7993 Warn("tape truncated -- size exceeds maximum tape size %d",
7996 // tape too large; read and ignore remaining tape data from this chunk
7997 for (;i < tape->length; i++)
7998 ReadUnusedBytesFromFile(file, tape_pos_size);
8003 if (tape->use_key_actions)
8005 for (j = 0; j < MAX_PLAYERS; j++)
8007 tape->pos[i].action[j] = MV_NONE;
8009 if (tape->player_participates[j])
8010 tape->pos[i].action[j] = getFile8Bit(file);
8014 if (tape->use_mouse_actions)
8016 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8017 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8018 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8021 tape->pos[i].delay = getFile8Bit(file);
8023 if (tape->file_version == FILE_VERSION_1_0)
8025 // eliminate possible diagonal moves in old tapes
8026 // this is only for backward compatibility
8028 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8029 byte action = tape->pos[i].action[0];
8030 int k, num_moves = 0;
8032 for (k = 0; k<4; k++)
8034 if (action & joy_dir[k])
8036 tape->pos[i + num_moves].action[0] = joy_dir[k];
8038 tape->pos[i + num_moves].delay = 0;
8047 tape->length += num_moves;
8050 else if (tape->file_version < FILE_VERSION_2_0)
8052 // convert pre-2.0 tapes to new tape format
8054 if (tape->pos[i].delay > 1)
8057 tape->pos[i + 1] = tape->pos[i];
8058 tape->pos[i + 1].delay = 1;
8061 for (j = 0; j < MAX_PLAYERS; j++)
8062 tape->pos[i].action[j] = MV_NONE;
8063 tape->pos[i].delay--;
8070 if (checkEndOfFile(file))
8074 if (i != tape->length)
8075 chunk_size = tape_pos_size * i;
8080 static void LoadTape_SokobanSolution(char *filename)
8083 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8085 if (!(file = openFile(filename, MODE_READ)))
8087 tape.no_valid_file = TRUE;
8092 while (!checkEndOfFile(file))
8094 unsigned char c = getByteFromFile(file);
8096 if (checkEndOfFile(file))
8103 tape.pos[tape.length].action[0] = MV_UP;
8104 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8110 tape.pos[tape.length].action[0] = MV_DOWN;
8111 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8117 tape.pos[tape.length].action[0] = MV_LEFT;
8118 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8124 tape.pos[tape.length].action[0] = MV_RIGHT;
8125 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8133 // ignore white-space characters
8137 tape.no_valid_file = TRUE;
8139 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8147 if (tape.no_valid_file)
8150 tape.length_frames = GetTapeLengthFrames();
8151 tape.length_seconds = GetTapeLengthSeconds();
8154 void LoadTapeFromFilename(char *filename)
8156 char cookie[MAX_LINE_LEN];
8157 char chunk_name[CHUNK_ID_LEN + 1];
8161 // always start with reliable default values
8162 setTapeInfoToDefaults();
8164 if (strSuffix(filename, ".sln"))
8166 LoadTape_SokobanSolution(filename);
8171 if (!(file = openFile(filename, MODE_READ)))
8173 tape.no_valid_file = TRUE;
8178 getFileChunkBE(file, chunk_name, NULL);
8179 if (strEqual(chunk_name, "RND1"))
8181 getFile32BitBE(file); // not used
8183 getFileChunkBE(file, chunk_name, NULL);
8184 if (!strEqual(chunk_name, "TAPE"))
8186 tape.no_valid_file = TRUE;
8188 Warn("unknown format of tape file '%s'", filename);
8195 else // check for pre-2.0 file format with cookie string
8197 strcpy(cookie, chunk_name);
8198 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8200 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8201 cookie[strlen(cookie) - 1] = '\0';
8203 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8205 tape.no_valid_file = TRUE;
8207 Warn("unknown format of tape file '%s'", filename);
8214 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8216 tape.no_valid_file = TRUE;
8218 Warn("unsupported version of tape file '%s'", filename);
8225 // pre-2.0 tape files have no game version, so use file version here
8226 tape.game_version = tape.file_version;
8229 if (tape.file_version < FILE_VERSION_1_2)
8231 // tape files from versions before 1.2.0 without chunk structure
8232 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8233 LoadTape_BODY(file, 2 * tape.length, &tape);
8241 int (*loader)(File *, int, struct TapeInfo *);
8245 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8246 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8247 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8248 { "INFO", -1, LoadTape_INFO },
8249 { "BODY", -1, LoadTape_BODY },
8253 while (getFileChunkBE(file, chunk_name, &chunk_size))
8257 while (chunk_info[i].name != NULL &&
8258 !strEqual(chunk_name, chunk_info[i].name))
8261 if (chunk_info[i].name == NULL)
8263 Warn("unknown chunk '%s' in tape file '%s'",
8264 chunk_name, filename);
8266 ReadUnusedBytesFromFile(file, chunk_size);
8268 else if (chunk_info[i].size != -1 &&
8269 chunk_info[i].size != chunk_size)
8271 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8272 chunk_size, chunk_name, filename);
8274 ReadUnusedBytesFromFile(file, chunk_size);
8278 // call function to load this tape chunk
8279 int chunk_size_expected =
8280 (chunk_info[i].loader)(file, chunk_size, &tape);
8282 // the size of some chunks cannot be checked before reading other
8283 // chunks first (like "HEAD" and "BODY") that contain some header
8284 // information, so check them here
8285 if (chunk_size_expected != chunk_size)
8287 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8288 chunk_size, chunk_name, filename);
8296 tape.length_frames = GetTapeLengthFrames();
8297 tape.length_seconds = GetTapeLengthSeconds();
8300 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8302 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8304 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8305 tape.engine_version);
8309 void LoadTape(int nr)
8311 char *filename = getTapeFilename(nr);
8313 LoadTapeFromFilename(filename);
8316 void LoadSolutionTape(int nr)
8318 char *filename = getSolutionTapeFilename(nr);
8320 LoadTapeFromFilename(filename);
8322 if (TAPE_IS_EMPTY(tape) &&
8323 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8324 level.native_sp_level->demo.is_available)
8325 CopyNativeTape_SP_to_RND(&level);
8328 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8330 // chunk required for team mode tapes with non-default screen size
8331 return (tape->num_participating_players > 1 &&
8332 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8333 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8336 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8338 putFileVersion(file, tape->file_version);
8339 putFileVersion(file, tape->game_version);
8342 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8345 byte store_participating_players = 0;
8347 // set bits for participating players for compact storage
8348 for (i = 0; i < MAX_PLAYERS; i++)
8349 if (tape->player_participates[i])
8350 store_participating_players |= (1 << i);
8352 putFile32BitBE(file, tape->random_seed);
8353 putFile32BitBE(file, tape->date);
8354 putFile32BitBE(file, tape->length);
8356 putFile8Bit(file, store_participating_players);
8358 putFile8Bit(file, getTapeActionValue(tape));
8360 putFile8Bit(file, tape->property_bits);
8362 // unused bytes not at the end here for 4-byte alignment of engine_version
8363 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8365 putFileVersion(file, tape->engine_version);
8368 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8370 putFile8Bit(file, tape->scr_fieldx);
8371 putFile8Bit(file, tape->scr_fieldy);
8374 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8376 int level_identifier_size = strlen(tape->level_identifier) + 1;
8379 putFile16BitBE(file, level_identifier_size);
8381 for (i = 0; i < level_identifier_size; i++)
8382 putFile8Bit(file, tape->level_identifier[i]);
8384 putFile16BitBE(file, tape->level_nr);
8387 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8391 for (i = 0; i < tape->length; i++)
8393 if (tape->use_key_actions)
8395 for (j = 0; j < MAX_PLAYERS; j++)
8396 if (tape->player_participates[j])
8397 putFile8Bit(file, tape->pos[i].action[j]);
8400 if (tape->use_mouse_actions)
8402 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8403 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8404 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8407 putFile8Bit(file, tape->pos[i].delay);
8411 void SaveTapeToFilename(char *filename)
8415 int info_chunk_size;
8416 int body_chunk_size;
8418 if (!(file = fopen(filename, MODE_WRITE)))
8420 Warn("cannot save level recording file '%s'", filename);
8425 tape_pos_size = getTapePosSize(&tape);
8427 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8428 body_chunk_size = tape_pos_size * tape.length;
8430 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8431 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8433 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8434 SaveTape_VERS(file, &tape);
8436 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8437 SaveTape_HEAD(file, &tape);
8439 if (checkSaveTape_SCRN(&tape))
8441 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8442 SaveTape_SCRN(file, &tape);
8445 putFileChunkBE(file, "INFO", info_chunk_size);
8446 SaveTape_INFO(file, &tape);
8448 putFileChunkBE(file, "BODY", body_chunk_size);
8449 SaveTape_BODY(file, &tape);
8453 SetFilePermissions(filename, PERMS_PRIVATE);
8456 static void SaveTapeExt(char *filename)
8460 tape.file_version = FILE_VERSION_ACTUAL;
8461 tape.game_version = GAME_VERSION_ACTUAL;
8463 tape.num_participating_players = 0;
8465 // count number of participating players
8466 for (i = 0; i < MAX_PLAYERS; i++)
8467 if (tape.player_participates[i])
8468 tape.num_participating_players++;
8470 SaveTapeToFilename(filename);
8472 tape.changed = FALSE;
8475 void SaveTape(int nr)
8477 char *filename = getTapeFilename(nr);
8479 InitTapeDirectory(leveldir_current->subdir);
8481 SaveTapeExt(filename);
8484 void SaveScoreTape(int nr)
8486 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8488 // used instead of "leveldir_current->subdir" (for network games)
8489 InitScoreTapeDirectory(levelset.identifier, nr);
8491 SaveTapeExt(filename);
8494 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8495 unsigned int req_state_added)
8497 char *filename = getTapeFilename(nr);
8498 boolean new_tape = !fileExists(filename);
8499 boolean tape_saved = FALSE;
8501 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8506 Request(msg_saved, REQ_CONFIRM | req_state_added);
8514 boolean SaveTapeChecked(int nr)
8516 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8519 boolean SaveTapeChecked_LevelSolved(int nr)
8521 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8522 "Level solved! Tape saved!", REQ_STAY_OPEN);
8525 void DumpTape(struct TapeInfo *tape)
8527 int tape_frame_counter;
8530 if (tape->no_valid_file)
8532 Warn("cannot dump -- no valid tape file found");
8539 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8540 tape->level_nr, tape->file_version, tape->game_version);
8541 Print(" (effective engine version %08d)\n",
8542 tape->engine_version);
8543 Print("Level series identifier: '%s'\n", tape->level_identifier);
8545 Print("Special tape properties: ");
8546 if (tape->property_bits == TAPE_PROPERTY_NONE)
8548 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8549 Print("[em_random_bug]");
8550 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8551 Print("[game_speed]");
8552 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8554 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8555 Print("[single_step]");
8556 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8557 Print("[snapshot]");
8558 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8559 Print("[replayed]");
8560 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8561 Print("[tas_keys]");
8562 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8563 Print("[small_graphics]");
8566 int year2 = tape->date / 10000;
8567 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8568 int month_index_raw = (tape->date / 100) % 100;
8569 int month_index = month_index_raw % 12; // prevent invalid index
8570 int month = month_index + 1;
8571 int day = tape->date % 100;
8573 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8577 tape_frame_counter = 0;
8579 for (i = 0; i < tape->length; i++)
8581 if (i >= MAX_TAPE_LEN)
8586 for (j = 0; j < MAX_PLAYERS; j++)
8588 if (tape->player_participates[j])
8590 int action = tape->pos[i].action[j];
8592 Print("%d:%02x ", j, action);
8593 Print("[%c%c%c%c|%c%c] - ",
8594 (action & JOY_LEFT ? '<' : ' '),
8595 (action & JOY_RIGHT ? '>' : ' '),
8596 (action & JOY_UP ? '^' : ' '),
8597 (action & JOY_DOWN ? 'v' : ' '),
8598 (action & JOY_BUTTON_1 ? '1' : ' '),
8599 (action & JOY_BUTTON_2 ? '2' : ' '));
8603 Print("(%03d) ", tape->pos[i].delay);
8604 Print("[%05d]\n", tape_frame_counter);
8606 tape_frame_counter += tape->pos[i].delay;
8612 void DumpTapes(void)
8614 static LevelDirTree *dumptape_leveldir = NULL;
8616 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8617 global.dumptape_leveldir);
8619 if (dumptape_leveldir == NULL)
8620 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8622 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8623 global.dumptape_level_nr > dumptape_leveldir->last_level)
8624 Fail("no such level number: %d", global.dumptape_level_nr);
8626 leveldir_current = dumptape_leveldir;
8628 if (options.mytapes)
8629 LoadTape(global.dumptape_level_nr);
8631 LoadSolutionTape(global.dumptape_level_nr);
8639 // ============================================================================
8640 // score file functions
8641 // ============================================================================
8643 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8647 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8649 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8650 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8651 scores->entry[i].score = 0;
8652 scores->entry[i].time = 0;
8655 scores->num_entries = 0;
8656 scores->last_added = -1;
8657 scores->last_added_local = -1;
8659 scores->updated = FALSE;
8660 scores->uploaded = FALSE;
8661 scores->force_last_added = FALSE;
8664 static void setScoreInfoToDefaults(void)
8666 setScoreInfoToDefaultsExt(&scores);
8669 static void setServerScoreInfoToDefaults(void)
8671 setScoreInfoToDefaultsExt(&server_scores);
8674 static void LoadScore_OLD(int nr)
8677 char *filename = getScoreFilename(nr);
8678 char cookie[MAX_LINE_LEN];
8679 char line[MAX_LINE_LEN];
8683 if (!(file = fopen(filename, MODE_READ)))
8686 // check file identifier
8687 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8689 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8690 cookie[strlen(cookie) - 1] = '\0';
8692 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8694 Warn("unknown format of score file '%s'", filename);
8701 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8703 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8704 Warn("fscanf() failed; %s", strerror(errno));
8706 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8709 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8710 line[strlen(line) - 1] = '\0';
8712 for (line_ptr = line; *line_ptr; line_ptr++)
8714 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8716 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8717 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8726 static void ConvertScore_OLD(void)
8728 // only convert score to time for levels that rate playing time over score
8729 if (!level.rate_time_over_score)
8732 // convert old score to playing time for score-less levels (like Supaplex)
8733 int time_final_max = 999;
8736 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8738 int score = scores.entry[i].score;
8740 if (score > 0 && score < time_final_max)
8741 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8745 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8747 scores->file_version = getFileVersion(file);
8748 scores->game_version = getFileVersion(file);
8753 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8755 char *level_identifier = NULL;
8756 int level_identifier_size;
8759 level_identifier_size = getFile16BitBE(file);
8761 level_identifier = checked_malloc(level_identifier_size);
8763 for (i = 0; i < level_identifier_size; i++)
8764 level_identifier[i] = getFile8Bit(file);
8766 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8767 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8769 checked_free(level_identifier);
8771 scores->level_nr = getFile16BitBE(file);
8772 scores->num_entries = getFile16BitBE(file);
8774 chunk_size = 2 + level_identifier_size + 2 + 2;
8779 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8783 for (i = 0; i < scores->num_entries; i++)
8785 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8786 scores->entry[i].name[j] = getFile8Bit(file);
8788 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8791 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8796 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8800 for (i = 0; i < scores->num_entries; i++)
8801 scores->entry[i].score = getFile16BitBE(file);
8803 chunk_size = scores->num_entries * 2;
8808 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8812 for (i = 0; i < scores->num_entries; i++)
8813 scores->entry[i].time = getFile32BitBE(file);
8815 chunk_size = scores->num_entries * 4;
8820 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8824 for (i = 0; i < scores->num_entries; i++)
8826 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8827 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8829 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8832 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8837 void LoadScore(int nr)
8839 char *filename = getScoreFilename(nr);
8840 char cookie[MAX_LINE_LEN];
8841 char chunk_name[CHUNK_ID_LEN + 1];
8843 boolean old_score_file_format = FALSE;
8846 // always start with reliable default values
8847 setScoreInfoToDefaults();
8849 if (!(file = openFile(filename, MODE_READ)))
8852 getFileChunkBE(file, chunk_name, NULL);
8853 if (strEqual(chunk_name, "RND1"))
8855 getFile32BitBE(file); // not used
8857 getFileChunkBE(file, chunk_name, NULL);
8858 if (!strEqual(chunk_name, "SCOR"))
8860 Warn("unknown format of score file '%s'", filename);
8867 else // check for old file format with cookie string
8869 strcpy(cookie, chunk_name);
8870 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8872 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8873 cookie[strlen(cookie) - 1] = '\0';
8875 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8877 Warn("unknown format of score file '%s'", filename);
8884 old_score_file_format = TRUE;
8887 if (old_score_file_format)
8889 // score files from versions before 4.2.4.0 without chunk structure
8892 // convert score to time, if possible (mainly for Supaplex levels)
8901 int (*loader)(File *, int, struct ScoreInfo *);
8905 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8906 { "INFO", -1, LoadScore_INFO },
8907 { "NAME", -1, LoadScore_NAME },
8908 { "SCOR", -1, LoadScore_SCOR },
8909 { "TIME", -1, LoadScore_TIME },
8910 { "TAPE", -1, LoadScore_TAPE },
8915 while (getFileChunkBE(file, chunk_name, &chunk_size))
8919 while (chunk_info[i].name != NULL &&
8920 !strEqual(chunk_name, chunk_info[i].name))
8923 if (chunk_info[i].name == NULL)
8925 Warn("unknown chunk '%s' in score file '%s'",
8926 chunk_name, filename);
8928 ReadUnusedBytesFromFile(file, chunk_size);
8930 else if (chunk_info[i].size != -1 &&
8931 chunk_info[i].size != chunk_size)
8933 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8934 chunk_size, chunk_name, filename);
8936 ReadUnusedBytesFromFile(file, chunk_size);
8940 // call function to load this score chunk
8941 int chunk_size_expected =
8942 (chunk_info[i].loader)(file, chunk_size, &scores);
8944 // the size of some chunks cannot be checked before reading other
8945 // chunks first (like "HEAD" and "BODY") that contain some header
8946 // information, so check them here
8947 if (chunk_size_expected != chunk_size)
8949 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8950 chunk_size, chunk_name, filename);
8959 #if ENABLE_HISTORIC_CHUNKS
8960 void SaveScore_OLD(int nr)
8963 char *filename = getScoreFilename(nr);
8966 // used instead of "leveldir_current->subdir" (for network games)
8967 InitScoreDirectory(levelset.identifier);
8969 if (!(file = fopen(filename, MODE_WRITE)))
8971 Warn("cannot save score for level %d", nr);
8976 fprintf(file, "%s\n\n", SCORE_COOKIE);
8978 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8979 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8983 SetFilePermissions(filename, PERMS_PRIVATE);
8987 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
8989 putFileVersion(file, scores->file_version);
8990 putFileVersion(file, scores->game_version);
8993 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
8995 int level_identifier_size = strlen(scores->level_identifier) + 1;
8998 putFile16BitBE(file, level_identifier_size);
9000 for (i = 0; i < level_identifier_size; i++)
9001 putFile8Bit(file, scores->level_identifier[i]);
9003 putFile16BitBE(file, scores->level_nr);
9004 putFile16BitBE(file, scores->num_entries);
9007 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9011 for (i = 0; i < scores->num_entries; i++)
9013 int name_size = strlen(scores->entry[i].name);
9015 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9016 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9020 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9024 for (i = 0; i < scores->num_entries; i++)
9025 putFile16BitBE(file, scores->entry[i].score);
9028 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9032 for (i = 0; i < scores->num_entries; i++)
9033 putFile32BitBE(file, scores->entry[i].time);
9036 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9040 for (i = 0; i < scores->num_entries; i++)
9042 int size = strlen(scores->entry[i].tape_basename);
9044 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9045 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9049 static void SaveScoreToFilename(char *filename)
9052 int info_chunk_size;
9053 int name_chunk_size;
9054 int scor_chunk_size;
9055 int time_chunk_size;
9056 int tape_chunk_size;
9058 if (!(file = fopen(filename, MODE_WRITE)))
9060 Warn("cannot save score file '%s'", filename);
9065 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9066 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9067 scor_chunk_size = scores.num_entries * 2;
9068 time_chunk_size = scores.num_entries * 4;
9069 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9071 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9072 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9074 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9075 SaveScore_VERS(file, &scores);
9077 putFileChunkBE(file, "INFO", info_chunk_size);
9078 SaveScore_INFO(file, &scores);
9080 putFileChunkBE(file, "NAME", name_chunk_size);
9081 SaveScore_NAME(file, &scores);
9083 putFileChunkBE(file, "SCOR", scor_chunk_size);
9084 SaveScore_SCOR(file, &scores);
9086 putFileChunkBE(file, "TIME", time_chunk_size);
9087 SaveScore_TIME(file, &scores);
9089 putFileChunkBE(file, "TAPE", tape_chunk_size);
9090 SaveScore_TAPE(file, &scores);
9094 SetFilePermissions(filename, PERMS_PRIVATE);
9097 void SaveScore(int nr)
9099 char *filename = getScoreFilename(nr);
9102 // used instead of "leveldir_current->subdir" (for network games)
9103 InitScoreDirectory(levelset.identifier);
9105 scores.file_version = FILE_VERSION_ACTUAL;
9106 scores.game_version = GAME_VERSION_ACTUAL;
9108 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9109 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9110 scores.level_nr = level_nr;
9112 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9113 if (scores.entry[i].score == 0 &&
9114 scores.entry[i].time == 0 &&
9115 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9118 scores.num_entries = i;
9120 if (scores.num_entries == 0)
9123 SaveScoreToFilename(filename);
9126 void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
9129 #if defined(PLATFORM_EMSCRIPTEN)
9130 // threads currently not fully supported by Emscripten/SDL and some browsers
9133 SDL_Thread *thread = SDL_CreateThread(function, name, data);
9136 SDL_DetachThread(thread);
9138 Error("Cannot create thread to %s!", error);
9140 // nasty kludge to lower probability of intermingled thread error messages
9145 char *getPasswordJSON(char *password)
9147 static char password_json[MAX_FILENAME_LEN] = "";
9148 static boolean initialized = FALSE;
9152 if (password != NULL &&
9153 !strEqual(password, "") &&
9154 !strEqual(password, UNDEFINED_PASSWORD))
9155 snprintf(password_json, MAX_FILENAME_LEN,
9156 " \"password\": \"%s\",\n",
9157 setup.api_server_password);
9162 return password_json;
9165 struct ApiGetScoreThreadData
9168 char *score_cache_filename;
9171 static void *CreateThreadData_ApiGetScore(int nr)
9173 struct ApiGetScoreThreadData *data =
9174 checked_malloc(sizeof(struct ApiGetScoreThreadData));
9175 char *score_cache_filename = getScoreCacheFilename(nr);
9177 data->level_nr = nr;
9178 data->score_cache_filename = getStringCopy(score_cache_filename);
9183 static void FreeThreadData_ApiGetScore(void *data_raw)
9185 struct ApiGetScoreThreadData *data = data_raw;
9187 checked_free(data->score_cache_filename);
9191 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
9194 struct ApiGetScoreThreadData *data = data_raw;
9195 int level_nr = data->level_nr;
9197 request->hostname = setup.api_server_hostname;
9198 request->port = API_SERVER_PORT;
9199 request->method = API_SERVER_METHOD;
9200 request->uri = API_SERVER_URI_GET;
9202 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9203 char *levelset_name = getEscapedJSON(leveldir_current->name);
9205 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9208 " \"game_version\": \"%s\",\n"
9209 " \"game_platform\": \"%s\",\n"
9210 " \"levelset_identifier\": \"%s\",\n"
9211 " \"levelset_name\": \"%s\",\n"
9212 " \"level_nr\": \"%d\"\n"
9214 getPasswordJSON(setup.api_server_password),
9215 getProgramRealVersionString(),
9216 getProgramPlatformString(),
9217 levelset_identifier,
9221 checked_free(levelset_identifier);
9222 checked_free(levelset_name);
9224 ConvertHttpRequestBodyToServerEncoding(request);
9229 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
9232 struct ApiGetScoreThreadData *data = data_raw;
9234 if (response->body_size == 0)
9236 // no scores available for this level
9241 ConvertHttpResponseBodyToClientEncoding(response);
9243 char *filename = data->score_cache_filename;
9247 // used instead of "leveldir_current->subdir" (for network games)
9248 InitScoreCacheDirectory(levelset.identifier);
9250 if (!(file = fopen(filename, MODE_WRITE)))
9252 Warn("cannot save score cache file '%s'", filename);
9257 for (i = 0; i < response->body_size; i++)
9258 fputc(response->body[i], file);
9262 SetFilePermissions(filename, PERMS_PRIVATE);
9264 server_scores.updated = TRUE;
9267 #if defined(PLATFORM_EMSCRIPTEN)
9268 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
9269 void *buffer, unsigned int size)
9271 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9273 if (response != NULL)
9275 HandleResponse_ApiGetScore(response, data_raw);
9277 checked_free(response);
9281 Error("server response too large to handle (%d bytes)", size);
9284 FreeThreadData_ApiGetScore(data_raw);
9287 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
9288 int code, const char *status)
9290 Error("server failed to handle request: %d %s", code, status);
9292 FreeThreadData_ApiGetScore(data_raw);
9295 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
9296 int bytes, int size)
9298 // nothing to do here
9301 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
9304 if (!SetRequest_ApiGetScore(request, data_raw))
9306 FreeThreadData_ApiGetScore(data_raw);
9311 emscripten_async_wget2_data(request->uri,
9316 Emscripten_ApiGetScore_Loaded,
9317 Emscripten_ApiGetScore_Failed,
9318 Emscripten_ApiGetScore_Progress);
9323 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
9324 struct HttpResponse *response,
9327 if (!SetRequest_ApiGetScore(request, data_raw))
9330 if (!DoHttpRequest(request, response))
9332 Error("HTTP request failed: %s", GetHttpError());
9337 if (!HTTP_SUCCESS(response->status_code))
9339 // do not show error message if no scores found for this level set
9340 if (response->status_code == 404)
9343 Error("server failed to handle request: %d %s",
9344 response->status_code,
9345 response->status_text);
9350 HandleResponse_ApiGetScore(response, data_raw);
9353 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
9354 struct HttpResponse *response,
9357 ApiGetScore_HttpRequestExt(request, response, data_raw);
9359 FreeThreadData_ApiGetScore(data_raw);
9363 static int ApiGetScoreThread(void *data_raw)
9365 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9366 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9368 program.api_thread_count++;
9370 #if defined(PLATFORM_EMSCRIPTEN)
9371 Emscripten_ApiGetScore_HttpRequest(request, data_raw);
9373 ApiGetScore_HttpRequest(request, response, data_raw);
9376 program.api_thread_count--;
9378 checked_free(request);
9379 checked_free(response);
9384 static void ApiGetScoreAsThread(int nr)
9386 struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
9388 ExecuteAsThread(ApiGetScoreThread,
9389 "ApiGetScore", data,
9390 "download scores from server");
9393 static void LoadServerScoreFromCache(int nr)
9395 struct ScoreEntry score_entry;
9404 { &score_entry.score, FALSE, 0 },
9405 { &score_entry.time, FALSE, 0 },
9406 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9407 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9411 char *filename = getScoreCacheFilename(nr);
9412 SetupFileHash *score_hash = loadSetupFileHash(filename);
9415 server_scores.num_entries = 0;
9417 if (score_hash == NULL)
9420 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9422 score_entry = server_scores.entry[i];
9424 for (j = 0; score_mapping[j].value != NULL; j++)
9428 sprintf(token, "%02d.%d", i, j);
9430 char *value = getHashEntry(score_hash, token);
9435 if (score_mapping[j].is_string)
9437 char *score_value = (char *)score_mapping[j].value;
9438 int value_size = score_mapping[j].string_size;
9440 strncpy(score_value, value, value_size);
9441 score_value[value_size] = '\0';
9445 int *score_value = (int *)score_mapping[j].value;
9447 *score_value = atoi(value);
9450 server_scores.num_entries = i + 1;
9453 server_scores.entry[i] = score_entry;
9456 freeSetupFileHash(score_hash);
9459 void LoadServerScore(int nr, boolean download_score)
9461 if (!setup.use_api_server)
9464 // always start with reliable default values
9465 setServerScoreInfoToDefaults();
9467 // 1st step: load server scores from cache file (which may not exist)
9468 // (this should prevent reading it while the thread is writing to it)
9469 LoadServerScoreFromCache(nr);
9471 if (download_score && runtime.use_api_server)
9473 // 2nd step: download server scores from score server to cache file
9474 // (as thread, as it might time out if the server is not reachable)
9475 ApiGetScoreAsThread(nr);
9479 static char *get_file_base64(char *filename)
9481 struct stat file_status;
9483 if (stat(filename, &file_status) != 0)
9485 Error("cannot stat file '%s'", filename);
9490 int buffer_size = file_status.st_size;
9491 byte *buffer = checked_malloc(buffer_size);
9495 if (!(file = fopen(filename, MODE_READ)))
9497 Error("cannot open file '%s'", filename);
9499 checked_free(buffer);
9504 for (i = 0; i < buffer_size; i++)
9506 int c = fgetc(file);
9510 Error("cannot read from input file '%s'", filename);
9513 checked_free(buffer);
9518 buffer[i] = (byte)c;
9523 int buffer_encoded_size = base64_encoded_size(buffer_size);
9524 char *buffer_encoded = checked_malloc(buffer_encoded_size);
9526 base64_encode(buffer_encoded, buffer, buffer_size);
9528 checked_free(buffer);
9530 return buffer_encoded;
9533 static void PrepareScoreTapesForUpload(char *leveldir_subdir)
9535 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9537 // if score tape not uploaded, ask for uploading missing tapes later
9538 if (!setup.has_remaining_tapes)
9539 setup.ask_for_remaining_tapes = TRUE;
9541 setup.provide_uploading_tapes = TRUE;
9542 setup.has_remaining_tapes = TRUE;
9544 SaveSetup_ServerSetup();
9547 struct ApiAddScoreThreadData
9551 char *leveldir_subdir;
9552 char *score_tape_filename;
9553 struct ScoreEntry score_entry;
9556 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
9557 char *score_tape_filename)
9559 struct ApiAddScoreThreadData *data =
9560 checked_malloc(sizeof(struct ApiAddScoreThreadData));
9561 struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
9563 if (score_tape_filename == NULL)
9564 score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
9566 data->level_nr = nr;
9567 data->tape_saved = tape_saved;
9568 data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
9569 data->score_tape_filename = getStringCopy(score_tape_filename);
9570 data->score_entry = *score_entry;
9575 static void FreeThreadData_ApiAddScore(void *data_raw)
9577 struct ApiAddScoreThreadData *data = data_raw;
9579 checked_free(data->leveldir_subdir);
9580 checked_free(data->score_tape_filename);
9584 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
9587 struct ApiAddScoreThreadData *data = data_raw;
9588 struct ScoreEntry *score_entry = &data->score_entry;
9589 char *score_tape_filename = data->score_tape_filename;
9590 boolean tape_saved = data->tape_saved;
9591 int level_nr = data->level_nr;
9593 request->hostname = setup.api_server_hostname;
9594 request->port = API_SERVER_PORT;
9595 request->method = API_SERVER_METHOD;
9596 request->uri = API_SERVER_URI_ADD;
9598 char *tape_base64 = get_file_base64(score_tape_filename);
9600 if (tape_base64 == NULL)
9602 Error("loading and base64 encoding score tape file failed");
9607 char *player_name_raw = score_entry->name;
9608 char *player_uuid_raw = setup.player_uuid;
9610 if (options.player_name != NULL && global.autoplay_leveldir != NULL)
9612 player_name_raw = options.player_name;
9613 player_uuid_raw = "";
9616 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9617 char *levelset_name = getEscapedJSON(leveldir_current->name);
9618 char *levelset_author = getEscapedJSON(leveldir_current->author);
9619 char *level_name = getEscapedJSON(level.name);
9620 char *level_author = getEscapedJSON(level.author);
9621 char *player_name = getEscapedJSON(player_name_raw);
9622 char *player_uuid = getEscapedJSON(player_uuid_raw);
9624 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9627 " \"game_version\": \"%s\",\n"
9628 " \"game_platform\": \"%s\",\n"
9629 " \"batch_time\": \"%d\",\n"
9630 " \"levelset_identifier\": \"%s\",\n"
9631 " \"levelset_name\": \"%s\",\n"
9632 " \"levelset_author\": \"%s\",\n"
9633 " \"levelset_num_levels\": \"%d\",\n"
9634 " \"levelset_first_level\": \"%d\",\n"
9635 " \"level_nr\": \"%d\",\n"
9636 " \"level_name\": \"%s\",\n"
9637 " \"level_author\": \"%s\",\n"
9638 " \"use_step_counter\": \"%d\",\n"
9639 " \"rate_time_over_score\": \"%d\",\n"
9640 " \"player_name\": \"%s\",\n"
9641 " \"player_uuid\": \"%s\",\n"
9642 " \"score\": \"%d\",\n"
9643 " \"time\": \"%d\",\n"
9644 " \"tape_basename\": \"%s\",\n"
9645 " \"tape_saved\": \"%d\",\n"
9646 " \"tape\": \"%s\"\n"
9648 getPasswordJSON(setup.api_server_password),
9649 getProgramRealVersionString(),
9650 getProgramPlatformString(),
9651 (int)global.autoplay_time,
9652 levelset_identifier,
9655 leveldir_current->levels,
9656 leveldir_current->first_level,
9660 level.use_step_counter,
9661 level.rate_time_over_score,
9666 score_entry->tape_basename,
9670 checked_free(tape_base64);
9672 checked_free(levelset_identifier);
9673 checked_free(levelset_name);
9674 checked_free(levelset_author);
9675 checked_free(level_name);
9676 checked_free(level_author);
9677 checked_free(player_name);
9678 checked_free(player_uuid);
9680 ConvertHttpRequestBodyToServerEncoding(request);
9685 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
9688 server_scores.uploaded = TRUE;
9691 static void HandleFailure_ApiAddScore(void *data_raw)
9693 struct ApiAddScoreThreadData *data = data_raw;
9695 PrepareScoreTapesForUpload(data->leveldir_subdir);
9698 #if defined(PLATFORM_EMSCRIPTEN)
9699 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
9700 void *buffer, unsigned int size)
9702 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9704 if (response != NULL)
9706 HandleResponse_ApiAddScore(response, data_raw);
9708 checked_free(response);
9712 Error("server response too large to handle (%d bytes)", size);
9714 HandleFailure_ApiAddScore(data_raw);
9717 FreeThreadData_ApiAddScore(data_raw);
9720 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
9721 int code, const char *status)
9723 Error("server failed to handle request: %d %s", code, status);
9725 HandleFailure_ApiAddScore(data_raw);
9727 FreeThreadData_ApiAddScore(data_raw);
9730 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
9731 int bytes, int size)
9733 // nothing to do here
9736 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
9739 if (!SetRequest_ApiAddScore(request, data_raw))
9741 FreeThreadData_ApiAddScore(data_raw);
9746 emscripten_async_wget2_data(request->uri,
9751 Emscripten_ApiAddScore_Loaded,
9752 Emscripten_ApiAddScore_Failed,
9753 Emscripten_ApiAddScore_Progress);
9758 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
9759 struct HttpResponse *response,
9762 if (!SetRequest_ApiAddScore(request, data_raw))
9765 if (!DoHttpRequest(request, response))
9767 Error("HTTP request failed: %s", GetHttpError());
9769 HandleFailure_ApiAddScore(data_raw);
9774 if (!HTTP_SUCCESS(response->status_code))
9776 Error("server failed to handle request: %d %s",
9777 response->status_code,
9778 response->status_text);
9780 HandleFailure_ApiAddScore(data_raw);
9785 HandleResponse_ApiAddScore(response, data_raw);
9788 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
9789 struct HttpResponse *response,
9792 ApiAddScore_HttpRequestExt(request, response, data_raw);
9794 FreeThreadData_ApiAddScore(data_raw);
9798 static int ApiAddScoreThread(void *data_raw)
9800 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9801 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9803 program.api_thread_count++;
9805 #if defined(PLATFORM_EMSCRIPTEN)
9806 Emscripten_ApiAddScore_HttpRequest(request, data_raw);
9808 ApiAddScore_HttpRequest(request, response, data_raw);
9811 program.api_thread_count--;
9813 checked_free(request);
9814 checked_free(response);
9819 static void ApiAddScoreAsThread(int nr, boolean tape_saved,
9820 char *score_tape_filename)
9822 struct ApiAddScoreThreadData *data =
9823 CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
9825 ExecuteAsThread(ApiAddScoreThread,
9826 "ApiAddScore", data,
9827 "upload score to server");
9830 void SaveServerScore(int nr, boolean tape_saved)
9832 if (!runtime.use_api_server)
9834 PrepareScoreTapesForUpload(leveldir_current->subdir);
9839 ApiAddScoreAsThread(nr, tape_saved, NULL);
9842 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9843 char *score_tape_filename)
9845 if (!runtime.use_api_server)
9848 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9851 void LoadLocalAndServerScore(int nr, boolean download_score)
9853 int last_added_local = scores.last_added_local;
9855 // needed if only showing server scores
9856 setScoreInfoToDefaults();
9858 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9861 // restore last added local score entry (before merging server scores)
9862 scores.last_added = scores.last_added_local = last_added_local;
9864 if (setup.use_api_server &&
9865 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9867 // load server scores from cache file and trigger update from server
9868 LoadServerScore(nr, download_score);
9870 // merge local scores with scores from server
9876 // ============================================================================
9877 // setup file functions
9878 // ============================================================================
9880 #define TOKEN_STR_PLAYER_PREFIX "player_"
9883 static struct TokenInfo global_setup_tokens[] =
9887 &setup.player_name, "player_name"
9891 &setup.multiple_users, "multiple_users"
9895 &setup.sound, "sound"
9899 &setup.sound_loops, "repeating_sound_loops"
9903 &setup.sound_music, "background_music"
9907 &setup.sound_simple, "simple_sound_effects"
9911 &setup.toons, "toons"
9915 &setup.scroll_delay, "scroll_delay"
9919 &setup.forced_scroll_delay, "forced_scroll_delay"
9923 &setup.scroll_delay_value, "scroll_delay_value"
9927 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9931 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9935 &setup.fade_screens, "fade_screens"
9939 &setup.autorecord, "automatic_tape_recording"
9943 &setup.show_titlescreen, "show_titlescreen"
9947 &setup.quick_doors, "quick_doors"
9951 &setup.team_mode, "team_mode"
9955 &setup.handicap, "handicap"
9959 &setup.skip_levels, "skip_levels"
9963 &setup.increment_levels, "increment_levels"
9967 &setup.auto_play_next_level, "auto_play_next_level"
9971 &setup.count_score_after_game, "count_score_after_game"
9975 &setup.show_scores_after_game, "show_scores_after_game"
9979 &setup.time_limit, "time_limit"
9983 &setup.fullscreen, "fullscreen"
9987 &setup.window_scaling_percent, "window_scaling_percent"
9991 &setup.window_scaling_quality, "window_scaling_quality"
9995 &setup.screen_rendering_mode, "screen_rendering_mode"
9999 &setup.vsync_mode, "vsync_mode"
10003 &setup.ask_on_escape, "ask_on_escape"
10007 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10011 &setup.ask_on_game_over, "ask_on_game_over"
10015 &setup.ask_on_quit_game, "ask_on_quit_game"
10019 &setup.ask_on_quit_program, "ask_on_quit_program"
10023 &setup.quick_switch, "quick_player_switch"
10027 &setup.input_on_focus, "input_on_focus"
10031 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10035 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10039 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10043 &setup.game_speed_extended, "game_speed_extended"
10047 &setup.game_frame_delay, "game_frame_delay"
10051 &setup.sp_show_border_elements, "sp_show_border_elements"
10055 &setup.small_game_graphics, "small_game_graphics"
10059 &setup.show_load_save_buttons, "show_load_save_buttons"
10063 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10067 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10071 &setup.graphics_set, "graphics_set"
10075 &setup.sounds_set, "sounds_set"
10079 &setup.music_set, "music_set"
10083 &setup.override_level_graphics, "override_level_graphics"
10087 &setup.override_level_sounds, "override_level_sounds"
10091 &setup.override_level_music, "override_level_music"
10095 &setup.volume_simple, "volume_simple"
10099 &setup.volume_loops, "volume_loops"
10103 &setup.volume_music, "volume_music"
10107 &setup.network_mode, "network_mode"
10111 &setup.network_player_nr, "network_player"
10115 &setup.network_server_hostname, "network_server_hostname"
10119 &setup.touch.control_type, "touch.control_type"
10123 &setup.touch.move_distance, "touch.move_distance"
10127 &setup.touch.drop_distance, "touch.drop_distance"
10131 &setup.touch.transparency, "touch.transparency"
10135 &setup.touch.draw_outlined, "touch.draw_outlined"
10139 &setup.touch.draw_pressed, "touch.draw_pressed"
10143 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10147 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10151 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10155 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10159 static struct TokenInfo auto_setup_tokens[] =
10163 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10167 static struct TokenInfo server_setup_tokens[] =
10171 &setup.player_uuid, "player_uuid"
10175 &setup.player_version, "player_version"
10179 &setup.use_api_server, TEST_PREFIX "use_api_server"
10183 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10187 &setup.api_server_password, TEST_PREFIX "api_server_password"
10191 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10195 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10199 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10203 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10207 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10211 static struct TokenInfo editor_setup_tokens[] =
10215 &setup.editor.el_classic, "editor.el_classic"
10219 &setup.editor.el_custom, "editor.el_custom"
10223 &setup.editor.el_user_defined, "editor.el_user_defined"
10227 &setup.editor.el_dynamic, "editor.el_dynamic"
10231 &setup.editor.el_headlines, "editor.el_headlines"
10235 &setup.editor.show_element_token, "editor.show_element_token"
10239 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10243 static struct TokenInfo editor_cascade_setup_tokens[] =
10247 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10251 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10255 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10259 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10263 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10267 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10271 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10275 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10279 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10283 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10287 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10291 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10295 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10299 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10303 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10307 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10311 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10315 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10319 static struct TokenInfo shortcut_setup_tokens[] =
10323 &setup.shortcut.save_game, "shortcut.save_game"
10327 &setup.shortcut.load_game, "shortcut.load_game"
10331 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10335 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10339 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10343 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10347 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10351 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10355 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10359 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10363 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10367 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10371 &setup.shortcut.tape_record, "shortcut.tape_record"
10375 &setup.shortcut.tape_play, "shortcut.tape_play"
10379 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10383 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10387 &setup.shortcut.sound_music, "shortcut.sound_music"
10391 &setup.shortcut.snap_left, "shortcut.snap_left"
10395 &setup.shortcut.snap_right, "shortcut.snap_right"
10399 &setup.shortcut.snap_up, "shortcut.snap_up"
10403 &setup.shortcut.snap_down, "shortcut.snap_down"
10407 static struct SetupInputInfo setup_input;
10408 static struct TokenInfo player_setup_tokens[] =
10412 &setup_input.use_joystick, ".use_joystick"
10416 &setup_input.joy.device_name, ".joy.device_name"
10420 &setup_input.joy.xleft, ".joy.xleft"
10424 &setup_input.joy.xmiddle, ".joy.xmiddle"
10428 &setup_input.joy.xright, ".joy.xright"
10432 &setup_input.joy.yupper, ".joy.yupper"
10436 &setup_input.joy.ymiddle, ".joy.ymiddle"
10440 &setup_input.joy.ylower, ".joy.ylower"
10444 &setup_input.joy.snap, ".joy.snap_field"
10448 &setup_input.joy.drop, ".joy.place_bomb"
10452 &setup_input.key.left, ".key.move_left"
10456 &setup_input.key.right, ".key.move_right"
10460 &setup_input.key.up, ".key.move_up"
10464 &setup_input.key.down, ".key.move_down"
10468 &setup_input.key.snap, ".key.snap_field"
10472 &setup_input.key.drop, ".key.place_bomb"
10476 static struct TokenInfo system_setup_tokens[] =
10480 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10484 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10488 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10492 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10496 static struct TokenInfo internal_setup_tokens[] =
10500 &setup.internal.program_title, "program_title"
10504 &setup.internal.program_version, "program_version"
10508 &setup.internal.program_author, "program_author"
10512 &setup.internal.program_email, "program_email"
10516 &setup.internal.program_website, "program_website"
10520 &setup.internal.program_copyright, "program_copyright"
10524 &setup.internal.program_company, "program_company"
10528 &setup.internal.program_icon_file, "program_icon_file"
10532 &setup.internal.default_graphics_set, "default_graphics_set"
10536 &setup.internal.default_sounds_set, "default_sounds_set"
10540 &setup.internal.default_music_set, "default_music_set"
10544 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10548 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10552 &setup.internal.fallback_music_file, "fallback_music_file"
10556 &setup.internal.default_level_series, "default_level_series"
10560 &setup.internal.default_window_width, "default_window_width"
10564 &setup.internal.default_window_height, "default_window_height"
10568 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10572 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10576 &setup.internal.create_user_levelset, "create_user_levelset"
10580 &setup.internal.menu_game, "menu_game"
10584 &setup.internal.menu_editor, "menu_editor"
10588 &setup.internal.menu_graphics, "menu_graphics"
10592 &setup.internal.menu_sound, "menu_sound"
10596 &setup.internal.menu_artwork, "menu_artwork"
10600 &setup.internal.menu_input, "menu_input"
10604 &setup.internal.menu_touch, "menu_touch"
10608 &setup.internal.menu_shortcuts, "menu_shortcuts"
10612 &setup.internal.menu_exit, "menu_exit"
10616 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10620 static struct TokenInfo debug_setup_tokens[] =
10624 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10628 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10632 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10636 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10640 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10644 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10648 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10652 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10656 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10660 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10664 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10668 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10672 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10676 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10680 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10684 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10688 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10692 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10696 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10700 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10704 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10707 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10711 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10715 &setup.debug.xsn_mode, "debug.xsn_mode"
10719 &setup.debug.xsn_percent, "debug.xsn_percent"
10723 static struct TokenInfo options_setup_tokens[] =
10727 &setup.options.verbose, "options.verbose"
10731 static void setSetupInfoToDefaults(struct SetupInfo *si)
10735 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10737 si->multiple_users = TRUE;
10740 si->sound_loops = TRUE;
10741 si->sound_music = TRUE;
10742 si->sound_simple = TRUE;
10744 si->scroll_delay = TRUE;
10745 si->forced_scroll_delay = FALSE;
10746 si->scroll_delay_value = STD_SCROLL_DELAY;
10747 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10748 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10749 si->fade_screens = TRUE;
10750 si->autorecord = TRUE;
10751 si->show_titlescreen = TRUE;
10752 si->quick_doors = FALSE;
10753 si->team_mode = FALSE;
10754 si->handicap = TRUE;
10755 si->skip_levels = TRUE;
10756 si->increment_levels = TRUE;
10757 si->auto_play_next_level = TRUE;
10758 si->count_score_after_game = TRUE;
10759 si->show_scores_after_game = TRUE;
10760 si->time_limit = TRUE;
10761 si->fullscreen = FALSE;
10762 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10763 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10764 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10765 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10766 si->ask_on_escape = TRUE;
10767 si->ask_on_escape_editor = TRUE;
10768 si->ask_on_game_over = TRUE;
10769 si->ask_on_quit_game = TRUE;
10770 si->ask_on_quit_program = TRUE;
10771 si->quick_switch = FALSE;
10772 si->input_on_focus = FALSE;
10773 si->prefer_aga_graphics = TRUE;
10774 si->prefer_lowpass_sounds = FALSE;
10775 si->prefer_extra_panel_items = TRUE;
10776 si->game_speed_extended = FALSE;
10777 si->game_frame_delay = GAME_FRAME_DELAY;
10778 si->sp_show_border_elements = FALSE;
10779 si->small_game_graphics = FALSE;
10780 si->show_load_save_buttons = FALSE;
10781 si->show_undo_redo_buttons = FALSE;
10782 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10784 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10785 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10786 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10788 si->override_level_graphics = FALSE;
10789 si->override_level_sounds = FALSE;
10790 si->override_level_music = FALSE;
10792 si->volume_simple = 100; // percent
10793 si->volume_loops = 100; // percent
10794 si->volume_music = 100; // percent
10796 si->network_mode = FALSE;
10797 si->network_player_nr = 0; // first player
10798 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10800 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10801 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10802 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10803 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10804 si->touch.draw_outlined = TRUE;
10805 si->touch.draw_pressed = TRUE;
10807 for (i = 0; i < 2; i++)
10809 char *default_grid_button[6][2] =
10815 { "111222", " vv " },
10816 { "111222", " vv " }
10818 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10819 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10820 int min_xsize = MIN(6, grid_xsize);
10821 int min_ysize = MIN(6, grid_ysize);
10822 int startx = grid_xsize - min_xsize;
10823 int starty = grid_ysize - min_ysize;
10826 // virtual buttons grid can only be set to defaults if video is initialized
10827 // (this will be repeated if virtual buttons are not loaded from setup file)
10828 if (video.initialized)
10830 si->touch.grid_xsize[i] = grid_xsize;
10831 si->touch.grid_ysize[i] = grid_ysize;
10835 si->touch.grid_xsize[i] = -1;
10836 si->touch.grid_ysize[i] = -1;
10839 for (x = 0; x < MAX_GRID_XSIZE; x++)
10840 for (y = 0; y < MAX_GRID_YSIZE; y++)
10841 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10843 for (x = 0; x < min_xsize; x++)
10844 for (y = 0; y < min_ysize; y++)
10845 si->touch.grid_button[i][x][starty + y] =
10846 default_grid_button[y][0][x];
10848 for (x = 0; x < min_xsize; x++)
10849 for (y = 0; y < min_ysize; y++)
10850 si->touch.grid_button[i][startx + x][starty + y] =
10851 default_grid_button[y][1][x];
10854 si->touch.grid_initialized = video.initialized;
10856 si->editor.el_boulderdash = TRUE;
10857 si->editor.el_emerald_mine = TRUE;
10858 si->editor.el_emerald_mine_club = TRUE;
10859 si->editor.el_more = TRUE;
10860 si->editor.el_sokoban = TRUE;
10861 si->editor.el_supaplex = TRUE;
10862 si->editor.el_diamond_caves = TRUE;
10863 si->editor.el_dx_boulderdash = TRUE;
10865 si->editor.el_mirror_magic = TRUE;
10866 si->editor.el_deflektor = TRUE;
10868 si->editor.el_chars = TRUE;
10869 si->editor.el_steel_chars = TRUE;
10871 si->editor.el_classic = TRUE;
10872 si->editor.el_custom = TRUE;
10874 si->editor.el_user_defined = FALSE;
10875 si->editor.el_dynamic = TRUE;
10877 si->editor.el_headlines = TRUE;
10879 si->editor.show_element_token = FALSE;
10881 si->editor.show_read_only_warning = TRUE;
10883 si->editor.use_template_for_new_levels = TRUE;
10885 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10886 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10887 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10889 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10890 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10891 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10892 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10893 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10895 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10896 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10897 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10898 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10899 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10900 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10902 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10903 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10904 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10906 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10907 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10908 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10909 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10911 for (i = 0; i < MAX_PLAYERS; i++)
10913 si->input[i].use_joystick = FALSE;
10914 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10915 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10916 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10917 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10918 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10919 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10920 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10921 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10922 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10923 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10924 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10925 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10926 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10927 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10928 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10931 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10932 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10933 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10934 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10936 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10937 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10938 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10939 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10940 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10941 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10942 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10944 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10946 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10947 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10948 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10950 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10951 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10952 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10954 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10955 si->internal.choose_from_top_leveldir = FALSE;
10956 si->internal.show_scaling_in_title = TRUE;
10957 si->internal.create_user_levelset = TRUE;
10959 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10960 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10962 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10963 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10964 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10965 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10966 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10967 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10968 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10969 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10970 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10971 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10973 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10974 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10975 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10976 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10977 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10978 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10979 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10980 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10981 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10982 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10984 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10985 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10987 si->debug.show_frames_per_second = FALSE;
10989 si->debug.xsn_mode = AUTO;
10990 si->debug.xsn_percent = 0;
10992 si->options.verbose = FALSE;
10994 #if defined(PLATFORM_ANDROID)
10995 si->fullscreen = TRUE;
10998 setHideSetupEntry(&setup.debug.xsn_mode);
11001 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11003 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11006 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11008 si->player_uuid = NULL; // (will be set later)
11009 si->player_version = 1; // (will be set later)
11011 si->use_api_server = TRUE;
11012 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11013 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11014 si->ask_for_uploading_tapes = TRUE;
11015 si->ask_for_remaining_tapes = FALSE;
11016 si->provide_uploading_tapes = TRUE;
11017 si->ask_for_using_api_server = TRUE;
11018 si->has_remaining_tapes = FALSE;
11021 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11023 si->editor_cascade.el_bd = TRUE;
11024 si->editor_cascade.el_em = TRUE;
11025 si->editor_cascade.el_emc = TRUE;
11026 si->editor_cascade.el_rnd = TRUE;
11027 si->editor_cascade.el_sb = TRUE;
11028 si->editor_cascade.el_sp = TRUE;
11029 si->editor_cascade.el_dc = TRUE;
11030 si->editor_cascade.el_dx = TRUE;
11032 si->editor_cascade.el_mm = TRUE;
11033 si->editor_cascade.el_df = TRUE;
11035 si->editor_cascade.el_chars = FALSE;
11036 si->editor_cascade.el_steel_chars = FALSE;
11037 si->editor_cascade.el_ce = FALSE;
11038 si->editor_cascade.el_ge = FALSE;
11039 si->editor_cascade.el_es = FALSE;
11040 si->editor_cascade.el_ref = FALSE;
11041 si->editor_cascade.el_user = FALSE;
11042 si->editor_cascade.el_dynamic = FALSE;
11045 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11047 static char *getHideSetupToken(void *setup_value)
11049 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11051 if (setup_value != NULL)
11052 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11054 return hide_setup_token;
11057 void setHideSetupEntry(void *setup_value)
11059 char *hide_setup_token = getHideSetupToken(setup_value);
11061 if (hide_setup_hash == NULL)
11062 hide_setup_hash = newSetupFileHash();
11064 if (setup_value != NULL)
11065 setHashEntry(hide_setup_hash, hide_setup_token, "");
11068 void removeHideSetupEntry(void *setup_value)
11070 char *hide_setup_token = getHideSetupToken(setup_value);
11072 if (setup_value != NULL)
11073 removeHashEntry(hide_setup_hash, hide_setup_token);
11076 boolean hideSetupEntry(void *setup_value)
11078 char *hide_setup_token = getHideSetupToken(setup_value);
11080 return (setup_value != NULL &&
11081 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11084 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11085 struct TokenInfo *token_info,
11086 int token_nr, char *token_text)
11088 char *token_hide_text = getStringCat2(token_text, ".hide");
11089 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11091 // set the value of this setup option in the setup option structure
11092 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11094 // check if this setup option should be hidden in the setup menu
11095 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11096 setHideSetupEntry(token_info[token_nr].value);
11098 free(token_hide_text);
11101 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11102 struct TokenInfo *token_info,
11105 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11106 token_info[token_nr].text);
11109 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11113 if (!setup_file_hash)
11116 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11117 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11119 setup.touch.grid_initialized = TRUE;
11120 for (i = 0; i < 2; i++)
11122 int grid_xsize = setup.touch.grid_xsize[i];
11123 int grid_ysize = setup.touch.grid_ysize[i];
11126 // if virtual buttons are not loaded from setup file, repeat initializing
11127 // virtual buttons grid with default values later when video is initialized
11128 if (grid_xsize == -1 ||
11131 setup.touch.grid_initialized = FALSE;
11136 for (y = 0; y < grid_ysize; y++)
11138 char token_string[MAX_LINE_LEN];
11140 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11142 char *value_string = getHashEntry(setup_file_hash, token_string);
11144 if (value_string == NULL)
11147 for (x = 0; x < grid_xsize; x++)
11149 char c = value_string[x];
11151 setup.touch.grid_button[i][x][y] =
11152 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11157 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11158 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11160 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11161 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11163 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11167 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11169 setup_input = setup.input[pnr];
11170 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11172 char full_token[100];
11174 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11175 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11178 setup.input[pnr] = setup_input;
11181 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11182 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11184 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11185 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11187 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11188 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11190 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11191 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11193 setHideRelatedSetupEntries();
11196 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11200 if (!setup_file_hash)
11203 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11204 setSetupInfo(auto_setup_tokens, i,
11205 getHashEntry(setup_file_hash,
11206 auto_setup_tokens[i].text));
11209 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11213 if (!setup_file_hash)
11216 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11217 setSetupInfo(server_setup_tokens, i,
11218 getHashEntry(setup_file_hash,
11219 server_setup_tokens[i].text));
11222 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11226 if (!setup_file_hash)
11229 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11230 setSetupInfo(editor_cascade_setup_tokens, i,
11231 getHashEntry(setup_file_hash,
11232 editor_cascade_setup_tokens[i].text));
11235 void LoadUserNames(void)
11237 int last_user_nr = user.nr;
11240 if (global.user_names != NULL)
11242 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11243 checked_free(global.user_names[i]);
11245 checked_free(global.user_names);
11248 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11250 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11254 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11256 if (setup_file_hash)
11258 char *player_name = getHashEntry(setup_file_hash, "player_name");
11260 global.user_names[i] = getFixedUserName(player_name);
11262 freeSetupFileHash(setup_file_hash);
11265 if (global.user_names[i] == NULL)
11266 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11269 user.nr = last_user_nr;
11272 void LoadSetupFromFilename(char *filename)
11274 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11276 if (setup_file_hash)
11278 decodeSetupFileHash_Default(setup_file_hash);
11280 freeSetupFileHash(setup_file_hash);
11284 Debug("setup", "using default setup values");
11288 static void LoadSetup_SpecialPostProcessing(void)
11290 char *player_name_new;
11292 // needed to work around problems with fixed length strings
11293 player_name_new = getFixedUserName(setup.player_name);
11294 free(setup.player_name);
11295 setup.player_name = player_name_new;
11297 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11298 if (setup.scroll_delay == FALSE)
11300 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11301 setup.scroll_delay = TRUE; // now always "on"
11304 // make sure that scroll delay value stays inside valid range
11305 setup.scroll_delay_value =
11306 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11309 void LoadSetup_Default(void)
11313 // always start with reliable default values
11314 setSetupInfoToDefaults(&setup);
11316 // try to load setup values from default setup file
11317 filename = getDefaultSetupFilename();
11319 if (fileExists(filename))
11320 LoadSetupFromFilename(filename);
11322 // try to load setup values from user setup file
11323 filename = getSetupFilename();
11325 LoadSetupFromFilename(filename);
11327 LoadSetup_SpecialPostProcessing();
11330 void LoadSetup_AutoSetup(void)
11332 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11333 SetupFileHash *setup_file_hash = NULL;
11335 // always start with reliable default values
11336 setSetupInfoToDefaults_AutoSetup(&setup);
11338 setup_file_hash = loadSetupFileHash(filename);
11340 if (setup_file_hash)
11342 decodeSetupFileHash_AutoSetup(setup_file_hash);
11344 freeSetupFileHash(setup_file_hash);
11350 void LoadSetup_ServerSetup(void)
11352 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11353 SetupFileHash *setup_file_hash = NULL;
11355 // always start with reliable default values
11356 setSetupInfoToDefaults_ServerSetup(&setup);
11358 setup_file_hash = loadSetupFileHash(filename);
11360 if (setup_file_hash)
11362 decodeSetupFileHash_ServerSetup(setup_file_hash);
11364 freeSetupFileHash(setup_file_hash);
11369 if (setup.player_uuid == NULL)
11371 // player UUID does not yet exist in setup file
11372 setup.player_uuid = getStringCopy(getUUID());
11373 setup.player_version = 2;
11375 SaveSetup_ServerSetup();
11379 void LoadSetup_EditorCascade(void)
11381 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11382 SetupFileHash *setup_file_hash = NULL;
11384 // always start with reliable default values
11385 setSetupInfoToDefaults_EditorCascade(&setup);
11387 setup_file_hash = loadSetupFileHash(filename);
11389 if (setup_file_hash)
11391 decodeSetupFileHash_EditorCascade(setup_file_hash);
11393 freeSetupFileHash(setup_file_hash);
11399 void LoadSetup(void)
11401 LoadSetup_Default();
11402 LoadSetup_AutoSetup();
11403 LoadSetup_ServerSetup();
11404 LoadSetup_EditorCascade();
11407 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11408 char *mapping_line)
11410 char mapping_guid[MAX_LINE_LEN];
11411 char *mapping_start, *mapping_end;
11413 // get GUID from game controller mapping line: copy complete line
11414 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11415 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11417 // get GUID from game controller mapping line: cut after GUID part
11418 mapping_start = strchr(mapping_guid, ',');
11419 if (mapping_start != NULL)
11420 *mapping_start = '\0';
11422 // cut newline from game controller mapping line
11423 mapping_end = strchr(mapping_line, '\n');
11424 if (mapping_end != NULL)
11425 *mapping_end = '\0';
11427 // add mapping entry to game controller mappings hash
11428 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11431 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11436 if (!(file = fopen(filename, MODE_READ)))
11438 Warn("cannot read game controller mappings file '%s'", filename);
11443 while (!feof(file))
11445 char line[MAX_LINE_LEN];
11447 if (!fgets(line, MAX_LINE_LEN, file))
11450 addGameControllerMappingToHash(mappings_hash, line);
11456 void SaveSetup_Default(void)
11458 char *filename = getSetupFilename();
11462 InitUserDataDirectory();
11464 if (!(file = fopen(filename, MODE_WRITE)))
11466 Warn("cannot write setup file '%s'", filename);
11471 fprintFileHeader(file, SETUP_FILENAME);
11473 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11475 // just to make things nicer :)
11476 if (global_setup_tokens[i].value == &setup.multiple_users ||
11477 global_setup_tokens[i].value == &setup.sound ||
11478 global_setup_tokens[i].value == &setup.graphics_set ||
11479 global_setup_tokens[i].value == &setup.volume_simple ||
11480 global_setup_tokens[i].value == &setup.network_mode ||
11481 global_setup_tokens[i].value == &setup.touch.control_type ||
11482 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11483 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11484 fprintf(file, "\n");
11486 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11489 for (i = 0; i < 2; i++)
11491 int grid_xsize = setup.touch.grid_xsize[i];
11492 int grid_ysize = setup.touch.grid_ysize[i];
11495 fprintf(file, "\n");
11497 for (y = 0; y < grid_ysize; y++)
11499 char token_string[MAX_LINE_LEN];
11500 char value_string[MAX_LINE_LEN];
11502 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11504 for (x = 0; x < grid_xsize; x++)
11506 char c = setup.touch.grid_button[i][x][y];
11508 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11511 value_string[grid_xsize] = '\0';
11513 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11517 fprintf(file, "\n");
11518 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11519 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11521 fprintf(file, "\n");
11522 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11523 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11525 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11529 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11530 fprintf(file, "\n");
11532 setup_input = setup.input[pnr];
11533 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11534 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11537 fprintf(file, "\n");
11538 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11539 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11541 // (internal setup values not saved to user setup file)
11543 fprintf(file, "\n");
11544 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11545 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11546 setup.debug.xsn_mode != AUTO)
11547 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11549 fprintf(file, "\n");
11550 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11551 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11555 SetFilePermissions(filename, PERMS_PRIVATE);
11558 void SaveSetup_AutoSetup(void)
11560 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11564 InitUserDataDirectory();
11566 if (!(file = fopen(filename, MODE_WRITE)))
11568 Warn("cannot write auto setup file '%s'", filename);
11575 fprintFileHeader(file, AUTOSETUP_FILENAME);
11577 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11578 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11582 SetFilePermissions(filename, PERMS_PRIVATE);
11587 void SaveSetup_ServerSetup(void)
11589 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11593 InitUserDataDirectory();
11595 if (!(file = fopen(filename, MODE_WRITE)))
11597 Warn("cannot write server setup file '%s'", filename);
11604 fprintFileHeader(file, SERVERSETUP_FILENAME);
11606 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11608 // just to make things nicer :)
11609 if (server_setup_tokens[i].value == &setup.use_api_server)
11610 fprintf(file, "\n");
11612 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11617 SetFilePermissions(filename, PERMS_PRIVATE);
11622 void SaveSetup_EditorCascade(void)
11624 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11628 InitUserDataDirectory();
11630 if (!(file = fopen(filename, MODE_WRITE)))
11632 Warn("cannot write editor cascade state file '%s'", filename);
11639 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11641 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11642 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11646 SetFilePermissions(filename, PERMS_PRIVATE);
11651 void SaveSetup(void)
11653 SaveSetup_Default();
11654 SaveSetup_AutoSetup();
11655 SaveSetup_ServerSetup();
11656 SaveSetup_EditorCascade();
11659 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11664 if (!(file = fopen(filename, MODE_WRITE)))
11666 Warn("cannot write game controller mappings file '%s'", filename);
11671 BEGIN_HASH_ITERATION(mappings_hash, itr)
11673 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11675 END_HASH_ITERATION(mappings_hash, itr)
11680 void SaveSetup_AddGameControllerMapping(char *mapping)
11682 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11683 SetupFileHash *mappings_hash = newSetupFileHash();
11685 InitUserDataDirectory();
11687 // load existing personal game controller mappings
11688 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11690 // add new mapping to personal game controller mappings
11691 addGameControllerMappingToHash(mappings_hash, mapping);
11693 // save updated personal game controller mappings
11694 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11696 freeSetupFileHash(mappings_hash);
11700 void LoadCustomElementDescriptions(void)
11702 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11703 SetupFileHash *setup_file_hash;
11706 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11708 if (element_info[i].custom_description != NULL)
11710 free(element_info[i].custom_description);
11711 element_info[i].custom_description = NULL;
11715 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11718 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11720 char *token = getStringCat2(element_info[i].token_name, ".name");
11721 char *value = getHashEntry(setup_file_hash, token);
11724 element_info[i].custom_description = getStringCopy(value);
11729 freeSetupFileHash(setup_file_hash);
11732 static int getElementFromToken(char *token)
11734 char *value = getHashEntry(element_token_hash, token);
11737 return atoi(value);
11739 Warn("unknown element token '%s'", token);
11741 return EL_UNDEFINED;
11744 void FreeGlobalAnimEventInfo(void)
11746 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11748 if (gaei->event_list == NULL)
11753 for (i = 0; i < gaei->num_event_lists; i++)
11755 checked_free(gaei->event_list[i]->event_value);
11756 checked_free(gaei->event_list[i]);
11759 checked_free(gaei->event_list);
11761 gaei->event_list = NULL;
11762 gaei->num_event_lists = 0;
11765 static int AddGlobalAnimEventList(void)
11767 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11768 int list_pos = gaei->num_event_lists++;
11770 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11771 sizeof(struct GlobalAnimEventListInfo *));
11773 gaei->event_list[list_pos] =
11774 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11776 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11778 gaeli->event_value = NULL;
11779 gaeli->num_event_values = 0;
11784 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11786 // do not add empty global animation events
11787 if (event_value == ANIM_EVENT_NONE)
11790 // if list position is undefined, create new list
11791 if (list_pos == ANIM_EVENT_UNDEFINED)
11792 list_pos = AddGlobalAnimEventList();
11794 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11795 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11796 int value_pos = gaeli->num_event_values++;
11798 gaeli->event_value = checked_realloc(gaeli->event_value,
11799 gaeli->num_event_values * sizeof(int *));
11801 gaeli->event_value[value_pos] = event_value;
11806 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11808 if (list_pos == ANIM_EVENT_UNDEFINED)
11809 return ANIM_EVENT_NONE;
11811 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11812 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11814 return gaeli->event_value[value_pos];
11817 int GetGlobalAnimEventValueCount(int list_pos)
11819 if (list_pos == ANIM_EVENT_UNDEFINED)
11822 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11823 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11825 return gaeli->num_event_values;
11828 // This function checks if a string <s> of the format "string1, string2, ..."
11829 // exactly contains a string <s_contained>.
11831 static boolean string_has_parameter(char *s, char *s_contained)
11835 if (s == NULL || s_contained == NULL)
11838 if (strlen(s_contained) > strlen(s))
11841 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11843 char next_char = s[strlen(s_contained)];
11845 // check if next character is delimiter or whitespace
11846 return (next_char == ',' || next_char == '\0' ||
11847 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11850 // check if string contains another parameter string after a comma
11851 substring = strchr(s, ',');
11852 if (substring == NULL) // string does not contain a comma
11855 // advance string pointer to next character after the comma
11858 // skip potential whitespaces after the comma
11859 while (*substring == ' ' || *substring == '\t')
11862 return string_has_parameter(substring, s_contained);
11865 static int get_anim_parameter_value(char *s)
11867 int event_value[] =
11875 char *pattern_1[] =
11883 char *pattern_2 = ".part_";
11884 char *matching_char = NULL;
11886 int pattern_1_len = 0;
11887 int result = ANIM_EVENT_NONE;
11890 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11892 matching_char = strstr(s_ptr, pattern_1[i]);
11893 pattern_1_len = strlen(pattern_1[i]);
11894 result = event_value[i];
11896 if (matching_char != NULL)
11900 if (matching_char == NULL)
11901 return ANIM_EVENT_NONE;
11903 s_ptr = matching_char + pattern_1_len;
11905 // check for main animation number ("anim_X" or "anim_XX")
11906 if (*s_ptr >= '0' && *s_ptr <= '9')
11908 int gic_anim_nr = (*s_ptr++ - '0');
11910 if (*s_ptr >= '0' && *s_ptr <= '9')
11911 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11913 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11914 return ANIM_EVENT_NONE;
11916 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11920 // invalid main animation number specified
11922 return ANIM_EVENT_NONE;
11925 // check for animation part number ("part_X" or "part_XX") (optional)
11926 if (strPrefix(s_ptr, pattern_2))
11928 s_ptr += strlen(pattern_2);
11930 if (*s_ptr >= '0' && *s_ptr <= '9')
11932 int gic_part_nr = (*s_ptr++ - '0');
11934 if (*s_ptr >= '0' && *s_ptr <= '9')
11935 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11937 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11938 return ANIM_EVENT_NONE;
11940 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11944 // invalid animation part number specified
11946 return ANIM_EVENT_NONE;
11950 // discard result if next character is neither delimiter nor whitespace
11951 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11952 *s_ptr == ' ' || *s_ptr == '\t'))
11953 return ANIM_EVENT_NONE;
11958 static int get_anim_parameter_values(char *s)
11960 int list_pos = ANIM_EVENT_UNDEFINED;
11961 int event_value = ANIM_EVENT_DEFAULT;
11963 if (string_has_parameter(s, "any"))
11964 event_value |= ANIM_EVENT_ANY;
11966 if (string_has_parameter(s, "click:self") ||
11967 string_has_parameter(s, "click") ||
11968 string_has_parameter(s, "self"))
11969 event_value |= ANIM_EVENT_SELF;
11971 if (string_has_parameter(s, "unclick:any"))
11972 event_value |= ANIM_EVENT_UNCLICK_ANY;
11974 // if animation event found, add it to global animation event list
11975 if (event_value != ANIM_EVENT_NONE)
11976 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11980 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11981 event_value = get_anim_parameter_value(s);
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);
11987 // continue with next part of the string, starting with next comma
11988 s = strchr(s + 1, ',');
11994 static int get_anim_action_parameter_value(char *token)
11996 // check most common default case first to massively speed things up
11997 if (strEqual(token, ARG_UNDEFINED))
11998 return ANIM_EVENT_ACTION_NONE;
12000 int result = getImageIDFromToken(token);
12004 char *gfx_token = getStringCat2("gfx.", token);
12006 result = getImageIDFromToken(gfx_token);
12008 checked_free(gfx_token);
12013 Key key = getKeyFromX11KeyName(token);
12015 if (key != KSYM_UNDEFINED)
12016 result = -(int)key;
12020 result = ANIM_EVENT_ACTION_NONE;
12025 int get_parameter_value(char *value_raw, char *suffix, int type)
12027 char *value = getStringToLower(value_raw);
12028 int result = 0; // probably a save default value
12030 if (strEqual(suffix, ".direction"))
12032 result = (strEqual(value, "left") ? MV_LEFT :
12033 strEqual(value, "right") ? MV_RIGHT :
12034 strEqual(value, "up") ? MV_UP :
12035 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12037 else if (strEqual(suffix, ".position"))
12039 result = (strEqual(value, "left") ? POS_LEFT :
12040 strEqual(value, "right") ? POS_RIGHT :
12041 strEqual(value, "top") ? POS_TOP :
12042 strEqual(value, "upper") ? POS_UPPER :
12043 strEqual(value, "middle") ? POS_MIDDLE :
12044 strEqual(value, "lower") ? POS_LOWER :
12045 strEqual(value, "bottom") ? POS_BOTTOM :
12046 strEqual(value, "any") ? POS_ANY :
12047 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12049 else if (strEqual(suffix, ".align"))
12051 result = (strEqual(value, "left") ? ALIGN_LEFT :
12052 strEqual(value, "right") ? ALIGN_RIGHT :
12053 strEqual(value, "center") ? ALIGN_CENTER :
12054 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12056 else if (strEqual(suffix, ".valign"))
12058 result = (strEqual(value, "top") ? VALIGN_TOP :
12059 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12060 strEqual(value, "middle") ? VALIGN_MIDDLE :
12061 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12063 else if (strEqual(suffix, ".anim_mode"))
12065 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12066 string_has_parameter(value, "loop") ? ANIM_LOOP :
12067 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12068 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12069 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12070 string_has_parameter(value, "random") ? ANIM_RANDOM :
12071 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12072 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12073 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12074 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12075 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12076 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12077 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12078 string_has_parameter(value, "all") ? ANIM_ALL :
12079 string_has_parameter(value, "tiled") ? ANIM_TILED :
12082 if (string_has_parameter(value, "once"))
12083 result |= ANIM_ONCE;
12085 if (string_has_parameter(value, "reverse"))
12086 result |= ANIM_REVERSE;
12088 if (string_has_parameter(value, "opaque_player"))
12089 result |= ANIM_OPAQUE_PLAYER;
12091 if (string_has_parameter(value, "static_panel"))
12092 result |= ANIM_STATIC_PANEL;
12094 else if (strEqual(suffix, ".init_event") ||
12095 strEqual(suffix, ".anim_event"))
12097 result = get_anim_parameter_values(value);
12099 else if (strEqual(suffix, ".init_delay_action") ||
12100 strEqual(suffix, ".anim_delay_action") ||
12101 strEqual(suffix, ".post_delay_action") ||
12102 strEqual(suffix, ".init_event_action") ||
12103 strEqual(suffix, ".anim_event_action"))
12105 result = get_anim_action_parameter_value(value_raw);
12107 else if (strEqual(suffix, ".class"))
12109 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12110 get_hash_from_key(value));
12112 else if (strEqual(suffix, ".style"))
12114 result = STYLE_DEFAULT;
12116 if (string_has_parameter(value, "accurate_borders"))
12117 result |= STYLE_ACCURATE_BORDERS;
12119 if (string_has_parameter(value, "inner_corners"))
12120 result |= STYLE_INNER_CORNERS;
12122 if (string_has_parameter(value, "reverse"))
12123 result |= STYLE_REVERSE;
12125 if (string_has_parameter(value, "leftmost_position"))
12126 result |= STYLE_LEFTMOST_POSITION;
12128 if (string_has_parameter(value, "block_clicks"))
12129 result |= STYLE_BLOCK;
12131 if (string_has_parameter(value, "passthrough_clicks"))
12132 result |= STYLE_PASSTHROUGH;
12134 if (string_has_parameter(value, "multiple_actions"))
12135 result |= STYLE_MULTIPLE_ACTIONS;
12137 else if (strEqual(suffix, ".fade_mode"))
12139 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12140 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12141 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12142 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12143 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12144 FADE_MODE_DEFAULT);
12146 else if (strEqual(suffix, ".auto_delay_unit"))
12148 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12149 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12150 AUTO_DELAY_UNIT_DEFAULT);
12152 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12154 result = gfx.get_font_from_token_function(value);
12156 else // generic parameter of type integer or boolean
12158 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12159 type == TYPE_INTEGER ? get_integer_from_string(value) :
12160 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12161 ARG_UNDEFINED_VALUE);
12169 static int get_token_parameter_value(char *token, char *value_raw)
12173 if (token == NULL || value_raw == NULL)
12174 return ARG_UNDEFINED_VALUE;
12176 suffix = strrchr(token, '.');
12177 if (suffix == NULL)
12180 if (strEqual(suffix, ".element"))
12181 return getElementFromToken(value_raw);
12183 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12184 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12187 void InitMenuDesignSettings_Static(void)
12191 // always start with reliable default values from static default config
12192 for (i = 0; image_config_vars[i].token != NULL; i++)
12194 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12197 *image_config_vars[i].value =
12198 get_token_parameter_value(image_config_vars[i].token, value);
12202 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12206 // the following initializes hierarchical values from static configuration
12208 // special case: initialize "ARG_DEFAULT" values in static default config
12209 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12210 titlescreen_initial_first_default.fade_mode =
12211 title_initial_first_default.fade_mode;
12212 titlescreen_initial_first_default.fade_delay =
12213 title_initial_first_default.fade_delay;
12214 titlescreen_initial_first_default.post_delay =
12215 title_initial_first_default.post_delay;
12216 titlescreen_initial_first_default.auto_delay =
12217 title_initial_first_default.auto_delay;
12218 titlescreen_initial_first_default.auto_delay_unit =
12219 title_initial_first_default.auto_delay_unit;
12220 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12221 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12222 titlescreen_first_default.post_delay = title_first_default.post_delay;
12223 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12224 titlescreen_first_default.auto_delay_unit =
12225 title_first_default.auto_delay_unit;
12226 titlemessage_initial_first_default.fade_mode =
12227 title_initial_first_default.fade_mode;
12228 titlemessage_initial_first_default.fade_delay =
12229 title_initial_first_default.fade_delay;
12230 titlemessage_initial_first_default.post_delay =
12231 title_initial_first_default.post_delay;
12232 titlemessage_initial_first_default.auto_delay =
12233 title_initial_first_default.auto_delay;
12234 titlemessage_initial_first_default.auto_delay_unit =
12235 title_initial_first_default.auto_delay_unit;
12236 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12237 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12238 titlemessage_first_default.post_delay = title_first_default.post_delay;
12239 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12240 titlemessage_first_default.auto_delay_unit =
12241 title_first_default.auto_delay_unit;
12243 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12244 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12245 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12246 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12247 titlescreen_initial_default.auto_delay_unit =
12248 title_initial_default.auto_delay_unit;
12249 titlescreen_default.fade_mode = title_default.fade_mode;
12250 titlescreen_default.fade_delay = title_default.fade_delay;
12251 titlescreen_default.post_delay = title_default.post_delay;
12252 titlescreen_default.auto_delay = title_default.auto_delay;
12253 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12254 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12255 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12256 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12257 titlemessage_initial_default.auto_delay_unit =
12258 title_initial_default.auto_delay_unit;
12259 titlemessage_default.fade_mode = title_default.fade_mode;
12260 titlemessage_default.fade_delay = title_default.fade_delay;
12261 titlemessage_default.post_delay = title_default.post_delay;
12262 titlemessage_default.auto_delay = title_default.auto_delay;
12263 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12265 // special case: initialize "ARG_DEFAULT" values in static default config
12266 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12267 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12269 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12270 titlescreen_first[i] = titlescreen_first_default;
12271 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12272 titlemessage_first[i] = titlemessage_first_default;
12274 titlescreen_initial[i] = titlescreen_initial_default;
12275 titlescreen[i] = titlescreen_default;
12276 titlemessage_initial[i] = titlemessage_initial_default;
12277 titlemessage[i] = titlemessage_default;
12280 // special case: initialize "ARG_DEFAULT" values in static default config
12281 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12282 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12284 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12287 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12288 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12289 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12292 // special case: initialize "ARG_DEFAULT" values in static default config
12293 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12294 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12296 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12297 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12298 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12300 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12303 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12307 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12311 struct XY *dst, *src;
12313 game_buttons_xy[] =
12315 { &game.button.save, &game.button.stop },
12316 { &game.button.pause2, &game.button.pause },
12317 { &game.button.load, &game.button.play },
12318 { &game.button.undo, &game.button.stop },
12319 { &game.button.redo, &game.button.play },
12325 // special case: initialize later added SETUP list size from LEVELS value
12326 if (menu.list_size[GAME_MODE_SETUP] == -1)
12327 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12329 // set default position for snapshot buttons to stop/pause/play buttons
12330 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12331 if ((*game_buttons_xy[i].dst).x == -1 &&
12332 (*game_buttons_xy[i].dst).y == -1)
12333 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12335 // --------------------------------------------------------------------------
12336 // dynamic viewports (including playfield margins, borders and alignments)
12337 // --------------------------------------------------------------------------
12339 // dynamic viewports currently only supported for landscape mode
12340 int display_width = MAX(video.display_width, video.display_height);
12341 int display_height = MIN(video.display_width, video.display_height);
12343 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12345 struct RectWithBorder *vp_window = &viewport.window[i];
12346 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12347 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12348 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12349 boolean dynamic_window_width = (vp_window->min_width != -1);
12350 boolean dynamic_window_height = (vp_window->min_height != -1);
12351 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12352 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12354 // adjust window size if min/max width/height is specified
12356 if (vp_window->min_width != -1)
12358 int window_width = display_width;
12360 // when using static window height, use aspect ratio of display
12361 if (vp_window->min_height == -1)
12362 window_width = vp_window->height * display_width / display_height;
12364 vp_window->width = MAX(vp_window->min_width, window_width);
12367 if (vp_window->min_height != -1)
12369 int window_height = display_height;
12371 // when using static window width, use aspect ratio of display
12372 if (vp_window->min_width == -1)
12373 window_height = vp_window->width * display_height / display_width;
12375 vp_window->height = MAX(vp_window->min_height, window_height);
12378 if (vp_window->max_width != -1)
12379 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12381 if (vp_window->max_height != -1)
12382 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12384 int playfield_width = vp_window->width;
12385 int playfield_height = vp_window->height;
12387 // adjust playfield size and position according to specified margins
12389 playfield_width -= vp_playfield->margin_left;
12390 playfield_width -= vp_playfield->margin_right;
12392 playfield_height -= vp_playfield->margin_top;
12393 playfield_height -= vp_playfield->margin_bottom;
12395 // adjust playfield size if min/max width/height is specified
12397 if (vp_playfield->min_width != -1)
12398 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12400 if (vp_playfield->min_height != -1)
12401 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12403 if (vp_playfield->max_width != -1)
12404 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12406 if (vp_playfield->max_height != -1)
12407 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12409 // adjust playfield position according to specified alignment
12411 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12412 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12413 else if (vp_playfield->align == ALIGN_CENTER)
12414 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12415 else if (vp_playfield->align == ALIGN_RIGHT)
12416 vp_playfield->x += playfield_width - vp_playfield->width;
12418 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12419 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12420 else if (vp_playfield->valign == VALIGN_MIDDLE)
12421 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12422 else if (vp_playfield->valign == VALIGN_BOTTOM)
12423 vp_playfield->y += playfield_height - vp_playfield->height;
12425 vp_playfield->x += vp_playfield->margin_left;
12426 vp_playfield->y += vp_playfield->margin_top;
12428 // adjust individual playfield borders if only default border is specified
12430 if (vp_playfield->border_left == -1)
12431 vp_playfield->border_left = vp_playfield->border_size;
12432 if (vp_playfield->border_right == -1)
12433 vp_playfield->border_right = vp_playfield->border_size;
12434 if (vp_playfield->border_top == -1)
12435 vp_playfield->border_top = vp_playfield->border_size;
12436 if (vp_playfield->border_bottom == -1)
12437 vp_playfield->border_bottom = vp_playfield->border_size;
12439 // set dynamic playfield borders if borders are specified as undefined
12440 // (but only if window size was dynamic and playfield size was static)
12442 if (dynamic_window_width && !dynamic_playfield_width)
12444 if (vp_playfield->border_left == -1)
12446 vp_playfield->border_left = (vp_playfield->x -
12447 vp_playfield->margin_left);
12448 vp_playfield->x -= vp_playfield->border_left;
12449 vp_playfield->width += vp_playfield->border_left;
12452 if (vp_playfield->border_right == -1)
12454 vp_playfield->border_right = (vp_window->width -
12456 vp_playfield->width -
12457 vp_playfield->margin_right);
12458 vp_playfield->width += vp_playfield->border_right;
12462 if (dynamic_window_height && !dynamic_playfield_height)
12464 if (vp_playfield->border_top == -1)
12466 vp_playfield->border_top = (vp_playfield->y -
12467 vp_playfield->margin_top);
12468 vp_playfield->y -= vp_playfield->border_top;
12469 vp_playfield->height += vp_playfield->border_top;
12472 if (vp_playfield->border_bottom == -1)
12474 vp_playfield->border_bottom = (vp_window->height -
12476 vp_playfield->height -
12477 vp_playfield->margin_bottom);
12478 vp_playfield->height += vp_playfield->border_bottom;
12482 // adjust playfield size to be a multiple of a defined alignment tile size
12484 int align_size = vp_playfield->align_size;
12485 int playfield_xtiles = vp_playfield->width / align_size;
12486 int playfield_ytiles = vp_playfield->height / align_size;
12487 int playfield_width_corrected = playfield_xtiles * align_size;
12488 int playfield_height_corrected = playfield_ytiles * align_size;
12489 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12490 i == GFX_SPECIAL_ARG_EDITOR);
12492 if (is_playfield_mode &&
12493 dynamic_playfield_width &&
12494 vp_playfield->width != playfield_width_corrected)
12496 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12498 vp_playfield->width = playfield_width_corrected;
12500 if (vp_playfield->align == ALIGN_LEFT)
12502 vp_playfield->border_left += playfield_xdiff;
12504 else if (vp_playfield->align == ALIGN_RIGHT)
12506 vp_playfield->border_right += playfield_xdiff;
12508 else if (vp_playfield->align == ALIGN_CENTER)
12510 int border_left_diff = playfield_xdiff / 2;
12511 int border_right_diff = playfield_xdiff - border_left_diff;
12513 vp_playfield->border_left += border_left_diff;
12514 vp_playfield->border_right += border_right_diff;
12518 if (is_playfield_mode &&
12519 dynamic_playfield_height &&
12520 vp_playfield->height != playfield_height_corrected)
12522 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12524 vp_playfield->height = playfield_height_corrected;
12526 if (vp_playfield->valign == VALIGN_TOP)
12528 vp_playfield->border_top += playfield_ydiff;
12530 else if (vp_playfield->align == VALIGN_BOTTOM)
12532 vp_playfield->border_right += playfield_ydiff;
12534 else if (vp_playfield->align == VALIGN_MIDDLE)
12536 int border_top_diff = playfield_ydiff / 2;
12537 int border_bottom_diff = playfield_ydiff - border_top_diff;
12539 vp_playfield->border_top += border_top_diff;
12540 vp_playfield->border_bottom += border_bottom_diff;
12544 // adjust door positions according to specified alignment
12546 for (j = 0; j < 2; j++)
12548 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12550 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12551 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12552 else if (vp_door->align == ALIGN_CENTER)
12553 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12554 else if (vp_door->align == ALIGN_RIGHT)
12555 vp_door->x += vp_window->width - vp_door->width;
12557 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12558 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12559 else if (vp_door->valign == VALIGN_MIDDLE)
12560 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12561 else if (vp_door->valign == VALIGN_BOTTOM)
12562 vp_door->y += vp_window->height - vp_door->height;
12567 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12571 struct XYTileSize *dst, *src;
12574 editor_buttons_xy[] =
12577 &editor.button.element_left, &editor.palette.element_left,
12578 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12581 &editor.button.element_middle, &editor.palette.element_middle,
12582 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12585 &editor.button.element_right, &editor.palette.element_right,
12586 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12593 // set default position for element buttons to element graphics
12594 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12596 if ((*editor_buttons_xy[i].dst).x == -1 &&
12597 (*editor_buttons_xy[i].dst).y == -1)
12599 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12601 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12603 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12607 // adjust editor palette rows and columns if specified to be dynamic
12609 if (editor.palette.cols == -1)
12611 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12612 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12613 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12615 editor.palette.cols = (vp_width - sc_width) / bt_width;
12617 if (editor.palette.x == -1)
12619 int palette_width = editor.palette.cols * bt_width + sc_width;
12621 editor.palette.x = (vp_width - palette_width) / 2;
12625 if (editor.palette.rows == -1)
12627 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12628 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12629 int tx_height = getFontHeight(FONT_TEXT_2);
12631 editor.palette.rows = (vp_height - tx_height) / bt_height;
12633 if (editor.palette.y == -1)
12635 int palette_height = editor.palette.rows * bt_height + tx_height;
12637 editor.palette.y = (vp_height - palette_height) / 2;
12642 static void LoadMenuDesignSettingsFromFilename(char *filename)
12644 static struct TitleFadingInfo tfi;
12645 static struct TitleMessageInfo tmi;
12646 static struct TokenInfo title_tokens[] =
12648 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12649 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12650 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12651 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12652 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12656 static struct TokenInfo titlemessage_tokens[] =
12658 { TYPE_INTEGER, &tmi.x, ".x" },
12659 { TYPE_INTEGER, &tmi.y, ".y" },
12660 { TYPE_INTEGER, &tmi.width, ".width" },
12661 { TYPE_INTEGER, &tmi.height, ".height" },
12662 { TYPE_INTEGER, &tmi.chars, ".chars" },
12663 { TYPE_INTEGER, &tmi.lines, ".lines" },
12664 { TYPE_INTEGER, &tmi.align, ".align" },
12665 { TYPE_INTEGER, &tmi.valign, ".valign" },
12666 { TYPE_INTEGER, &tmi.font, ".font" },
12667 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12668 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12669 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12670 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12671 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12672 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12673 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12674 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12675 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12681 struct TitleFadingInfo *info;
12686 // initialize first titles from "enter screen" definitions, if defined
12687 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12688 { &title_first_default, "menu.enter_screen.TITLE" },
12690 // initialize title screens from "next screen" definitions, if defined
12691 { &title_initial_default, "menu.next_screen.TITLE" },
12692 { &title_default, "menu.next_screen.TITLE" },
12698 struct TitleMessageInfo *array;
12701 titlemessage_arrays[] =
12703 // initialize first titles from "enter screen" definitions, if defined
12704 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12705 { titlescreen_first, "menu.enter_screen.TITLE" },
12706 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12707 { titlemessage_first, "menu.enter_screen.TITLE" },
12709 // initialize titles from "next screen" definitions, if defined
12710 { titlescreen_initial, "menu.next_screen.TITLE" },
12711 { titlescreen, "menu.next_screen.TITLE" },
12712 { titlemessage_initial, "menu.next_screen.TITLE" },
12713 { titlemessage, "menu.next_screen.TITLE" },
12715 // overwrite titles with title definitions, if defined
12716 { titlescreen_initial_first, "[title_initial]" },
12717 { titlescreen_first, "[title]" },
12718 { titlemessage_initial_first, "[title_initial]" },
12719 { titlemessage_first, "[title]" },
12721 { titlescreen_initial, "[title_initial]" },
12722 { titlescreen, "[title]" },
12723 { titlemessage_initial, "[title_initial]" },
12724 { titlemessage, "[title]" },
12726 // overwrite titles with title screen/message definitions, if defined
12727 { titlescreen_initial_first, "[titlescreen_initial]" },
12728 { titlescreen_first, "[titlescreen]" },
12729 { titlemessage_initial_first, "[titlemessage_initial]" },
12730 { titlemessage_first, "[titlemessage]" },
12732 { titlescreen_initial, "[titlescreen_initial]" },
12733 { titlescreen, "[titlescreen]" },
12734 { titlemessage_initial, "[titlemessage_initial]" },
12735 { titlemessage, "[titlemessage]" },
12739 SetupFileHash *setup_file_hash;
12742 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12745 // the following initializes hierarchical values from dynamic configuration
12747 // special case: initialize with default values that may be overwritten
12748 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12749 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12751 struct TokenIntPtrInfo menu_config[] =
12753 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12754 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12755 { "menu.list_size", &menu.list_size[i] }
12758 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12760 char *token = menu_config[j].token;
12761 char *value = getHashEntry(setup_file_hash, token);
12764 *menu_config[j].value = get_integer_from_string(value);
12768 // special case: initialize with default values that may be overwritten
12769 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12770 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12772 struct TokenIntPtrInfo menu_config[] =
12774 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12775 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12776 { "menu.list_size.INFO", &menu.list_size_info[i] }
12779 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12781 char *token = menu_config[j].token;
12782 char *value = getHashEntry(setup_file_hash, token);
12785 *menu_config[j].value = get_integer_from_string(value);
12789 // special case: initialize with default values that may be overwritten
12790 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12791 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12793 struct TokenIntPtrInfo menu_config[] =
12795 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12796 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12799 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12801 char *token = menu_config[j].token;
12802 char *value = getHashEntry(setup_file_hash, token);
12805 *menu_config[j].value = get_integer_from_string(value);
12809 // special case: initialize with default values that may be overwritten
12810 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12811 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12813 struct TokenIntPtrInfo menu_config[] =
12815 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12816 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12817 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12818 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12819 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12820 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12821 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12822 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12823 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12826 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12828 char *token = menu_config[j].token;
12829 char *value = getHashEntry(setup_file_hash, token);
12832 *menu_config[j].value = get_integer_from_string(value);
12836 // special case: initialize with default values that may be overwritten
12837 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12838 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12840 struct TokenIntPtrInfo menu_config[] =
12842 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12843 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12844 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12845 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12846 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12847 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12848 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12849 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12850 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12853 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12855 char *token = menu_config[j].token;
12856 char *value = getHashEntry(setup_file_hash, token);
12859 *menu_config[j].value = get_token_parameter_value(token, value);
12863 // special case: initialize with default values that may be overwritten
12864 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12865 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12869 char *token_prefix;
12870 struct RectWithBorder *struct_ptr;
12874 { "viewport.window", &viewport.window[i] },
12875 { "viewport.playfield", &viewport.playfield[i] },
12876 { "viewport.door_1", &viewport.door_1[i] },
12877 { "viewport.door_2", &viewport.door_2[i] }
12880 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12882 struct TokenIntPtrInfo vp_config[] =
12884 { ".x", &vp_struct[j].struct_ptr->x },
12885 { ".y", &vp_struct[j].struct_ptr->y },
12886 { ".width", &vp_struct[j].struct_ptr->width },
12887 { ".height", &vp_struct[j].struct_ptr->height },
12888 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12889 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12890 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12891 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12892 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12893 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12894 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12895 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12896 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12897 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12898 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12899 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12900 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12901 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12902 { ".align", &vp_struct[j].struct_ptr->align },
12903 { ".valign", &vp_struct[j].struct_ptr->valign }
12906 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12908 char *token = getStringCat2(vp_struct[j].token_prefix,
12909 vp_config[k].token);
12910 char *value = getHashEntry(setup_file_hash, token);
12913 *vp_config[k].value = get_token_parameter_value(token, value);
12920 // special case: initialize with default values that may be overwritten
12921 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12922 for (i = 0; title_info[i].info != NULL; i++)
12924 struct TitleFadingInfo *info = title_info[i].info;
12925 char *base_token = title_info[i].text;
12927 for (j = 0; title_tokens[j].type != -1; j++)
12929 char *token = getStringCat2(base_token, title_tokens[j].text);
12930 char *value = getHashEntry(setup_file_hash, token);
12934 int parameter_value = get_token_parameter_value(token, value);
12938 *(int *)title_tokens[j].value = (int)parameter_value;
12947 // special case: initialize with default values that may be overwritten
12948 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12949 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12951 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12952 char *base_token = titlemessage_arrays[i].text;
12954 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12956 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12957 char *value = getHashEntry(setup_file_hash, token);
12961 int parameter_value = get_token_parameter_value(token, value);
12963 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12967 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12968 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12970 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12980 // special case: check if network and preview player positions are redefined,
12981 // to compare this later against the main menu level preview being redefined
12982 struct TokenIntPtrInfo menu_config_players[] =
12984 { "main.network_players.x", &menu.main.network_players.redefined },
12985 { "main.network_players.y", &menu.main.network_players.redefined },
12986 { "main.preview_players.x", &menu.main.preview_players.redefined },
12987 { "main.preview_players.y", &menu.main.preview_players.redefined },
12988 { "preview.x", &preview.redefined },
12989 { "preview.y", &preview.redefined }
12992 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12993 *menu_config_players[i].value = FALSE;
12995 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12996 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12997 *menu_config_players[i].value = TRUE;
12999 // read (and overwrite with) values that may be specified in config file
13000 for (i = 0; image_config_vars[i].token != NULL; i++)
13002 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13004 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13005 if (value != NULL && !strEqual(value, ARG_DEFAULT))
13006 *image_config_vars[i].value =
13007 get_token_parameter_value(image_config_vars[i].token, value);
13010 freeSetupFileHash(setup_file_hash);
13013 void LoadMenuDesignSettings(void)
13015 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13017 InitMenuDesignSettings_Static();
13018 InitMenuDesignSettings_SpecialPreProcessing();
13020 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13022 // first look for special settings configured in level series config
13023 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13025 if (fileExists(filename_base))
13026 LoadMenuDesignSettingsFromFilename(filename_base);
13029 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13031 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13032 LoadMenuDesignSettingsFromFilename(filename_local);
13034 InitMenuDesignSettings_SpecialPostProcessing();
13037 void LoadMenuDesignSettings_AfterGraphics(void)
13039 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13042 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13044 char *filename = getEditorSetupFilename();
13045 SetupFileList *setup_file_list, *list;
13046 SetupFileHash *element_hash;
13047 int num_unknown_tokens = 0;
13050 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13053 element_hash = newSetupFileHash();
13055 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13056 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13058 // determined size may be larger than needed (due to unknown elements)
13060 for (list = setup_file_list; list != NULL; list = list->next)
13063 // add space for up to 3 more elements for padding that may be needed
13064 *num_elements += 3;
13066 // free memory for old list of elements, if needed
13067 checked_free(*elements);
13069 // allocate memory for new list of elements
13070 *elements = checked_malloc(*num_elements * sizeof(int));
13073 for (list = setup_file_list; list != NULL; list = list->next)
13075 char *value = getHashEntry(element_hash, list->token);
13077 if (value == NULL) // try to find obsolete token mapping
13079 char *mapped_token = get_mapped_token(list->token);
13081 if (mapped_token != NULL)
13083 value = getHashEntry(element_hash, mapped_token);
13085 free(mapped_token);
13091 (*elements)[(*num_elements)++] = atoi(value);
13095 if (num_unknown_tokens == 0)
13098 Warn("unknown token(s) found in config file:");
13099 Warn("- config file: '%s'", filename);
13101 num_unknown_tokens++;
13104 Warn("- token: '%s'", list->token);
13108 if (num_unknown_tokens > 0)
13111 while (*num_elements % 4) // pad with empty elements, if needed
13112 (*elements)[(*num_elements)++] = EL_EMPTY;
13114 freeSetupFileList(setup_file_list);
13115 freeSetupFileHash(element_hash);
13118 for (i = 0; i < *num_elements; i++)
13119 Debug("editor", "element '%s' [%d]\n",
13120 element_info[(*elements)[i]].token_name, (*elements)[i]);
13124 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13127 SetupFileHash *setup_file_hash = NULL;
13128 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13129 char *filename_music, *filename_prefix, *filename_info;
13135 token_to_value_ptr[] =
13137 { "title_header", &tmp_music_file_info.title_header },
13138 { "artist_header", &tmp_music_file_info.artist_header },
13139 { "album_header", &tmp_music_file_info.album_header },
13140 { "year_header", &tmp_music_file_info.year_header },
13142 { "title", &tmp_music_file_info.title },
13143 { "artist", &tmp_music_file_info.artist },
13144 { "album", &tmp_music_file_info.album },
13145 { "year", &tmp_music_file_info.year },
13151 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13152 getCustomMusicFilename(basename));
13154 if (filename_music == NULL)
13157 // ---------- try to replace file extension ----------
13159 filename_prefix = getStringCopy(filename_music);
13160 if (strrchr(filename_prefix, '.') != NULL)
13161 *strrchr(filename_prefix, '.') = '\0';
13162 filename_info = getStringCat2(filename_prefix, ".txt");
13164 if (fileExists(filename_info))
13165 setup_file_hash = loadSetupFileHash(filename_info);
13167 free(filename_prefix);
13168 free(filename_info);
13170 if (setup_file_hash == NULL)
13172 // ---------- try to add file extension ----------
13174 filename_prefix = getStringCopy(filename_music);
13175 filename_info = getStringCat2(filename_prefix, ".txt");
13177 if (fileExists(filename_info))
13178 setup_file_hash = loadSetupFileHash(filename_info);
13180 free(filename_prefix);
13181 free(filename_info);
13184 if (setup_file_hash == NULL)
13187 // ---------- music file info found ----------
13189 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13191 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13193 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13195 *token_to_value_ptr[i].value_ptr =
13196 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13199 tmp_music_file_info.basename = getStringCopy(basename);
13200 tmp_music_file_info.music = music;
13201 tmp_music_file_info.is_sound = is_sound;
13203 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13204 *new_music_file_info = tmp_music_file_info;
13206 return new_music_file_info;
13209 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13211 return get_music_file_info_ext(basename, music, FALSE);
13214 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13216 return get_music_file_info_ext(basename, sound, TRUE);
13219 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13220 char *basename, boolean is_sound)
13222 for (; list != NULL; list = list->next)
13223 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13229 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13231 return music_info_listed_ext(list, basename, FALSE);
13234 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13236 return music_info_listed_ext(list, basename, TRUE);
13239 void LoadMusicInfo(void)
13241 char *music_directory = getCustomMusicDirectory();
13242 int num_music = getMusicListSize();
13243 int num_music_noconf = 0;
13244 int num_sounds = getSoundListSize();
13246 DirectoryEntry *dir_entry;
13247 struct FileInfo *music, *sound;
13248 struct MusicFileInfo *next, **new;
13251 while (music_file_info != NULL)
13253 next = music_file_info->next;
13255 checked_free(music_file_info->basename);
13257 checked_free(music_file_info->title_header);
13258 checked_free(music_file_info->artist_header);
13259 checked_free(music_file_info->album_header);
13260 checked_free(music_file_info->year_header);
13262 checked_free(music_file_info->title);
13263 checked_free(music_file_info->artist);
13264 checked_free(music_file_info->album);
13265 checked_free(music_file_info->year);
13267 free(music_file_info);
13269 music_file_info = next;
13272 new = &music_file_info;
13274 for (i = 0; i < num_music; i++)
13276 music = getMusicListEntry(i);
13278 if (music->filename == NULL)
13281 if (strEqual(music->filename, UNDEFINED_FILENAME))
13284 // a configured file may be not recognized as music
13285 if (!FileIsMusic(music->filename))
13288 if (!music_info_listed(music_file_info, music->filename))
13290 *new = get_music_file_info(music->filename, i);
13293 new = &(*new)->next;
13297 if ((dir = openDirectory(music_directory)) == NULL)
13299 Warn("cannot read music directory '%s'", music_directory);
13304 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
13306 char *basename = dir_entry->basename;
13307 boolean music_already_used = FALSE;
13310 // skip all music files that are configured in music config file
13311 for (i = 0; i < num_music; i++)
13313 music = getMusicListEntry(i);
13315 if (music->filename == NULL)
13318 if (strEqual(basename, music->filename))
13320 music_already_used = TRUE;
13325 if (music_already_used)
13328 if (!FileIsMusic(dir_entry->filename))
13331 if (!music_info_listed(music_file_info, basename))
13333 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13336 new = &(*new)->next;
13339 num_music_noconf++;
13342 closeDirectory(dir);
13344 for (i = 0; i < num_sounds; i++)
13346 sound = getSoundListEntry(i);
13348 if (sound->filename == NULL)
13351 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13354 // a configured file may be not recognized as sound
13355 if (!FileIsSound(sound->filename))
13358 if (!sound_info_listed(music_file_info, sound->filename))
13360 *new = get_sound_file_info(sound->filename, i);
13362 new = &(*new)->next;
13367 static void add_helpanim_entry(int element, int action, int direction,
13368 int delay, int *num_list_entries)
13370 struct HelpAnimInfo *new_list_entry;
13371 (*num_list_entries)++;
13374 checked_realloc(helpanim_info,
13375 *num_list_entries * sizeof(struct HelpAnimInfo));
13376 new_list_entry = &helpanim_info[*num_list_entries - 1];
13378 new_list_entry->element = element;
13379 new_list_entry->action = action;
13380 new_list_entry->direction = direction;
13381 new_list_entry->delay = delay;
13384 static void print_unknown_token(char *filename, char *token, int token_nr)
13389 Warn("unknown token(s) found in config file:");
13390 Warn("- config file: '%s'", filename);
13393 Warn("- token: '%s'", token);
13396 static void print_unknown_token_end(int token_nr)
13402 void LoadHelpAnimInfo(void)
13404 char *filename = getHelpAnimFilename();
13405 SetupFileList *setup_file_list = NULL, *list;
13406 SetupFileHash *element_hash, *action_hash, *direction_hash;
13407 int num_list_entries = 0;
13408 int num_unknown_tokens = 0;
13411 if (fileExists(filename))
13412 setup_file_list = loadSetupFileList(filename);
13414 if (setup_file_list == NULL)
13416 // use reliable default values from static configuration
13417 SetupFileList *insert_ptr;
13419 insert_ptr = setup_file_list =
13420 newSetupFileList(helpanim_config[0].token,
13421 helpanim_config[0].value);
13423 for (i = 1; helpanim_config[i].token; i++)
13424 insert_ptr = addListEntry(insert_ptr,
13425 helpanim_config[i].token,
13426 helpanim_config[i].value);
13429 element_hash = newSetupFileHash();
13430 action_hash = newSetupFileHash();
13431 direction_hash = newSetupFileHash();
13433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13434 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13436 for (i = 0; i < NUM_ACTIONS; i++)
13437 setHashEntry(action_hash, element_action_info[i].suffix,
13438 i_to_a(element_action_info[i].value));
13440 // do not store direction index (bit) here, but direction value!
13441 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13442 setHashEntry(direction_hash, element_direction_info[i].suffix,
13443 i_to_a(1 << element_direction_info[i].value));
13445 for (list = setup_file_list; list != NULL; list = list->next)
13447 char *element_token, *action_token, *direction_token;
13448 char *element_value, *action_value, *direction_value;
13449 int delay = atoi(list->value);
13451 if (strEqual(list->token, "end"))
13453 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13458 /* first try to break element into element/action/direction parts;
13459 if this does not work, also accept combined "element[.act][.dir]"
13460 elements (like "dynamite.active"), which are unique elements */
13462 if (strchr(list->token, '.') == NULL) // token contains no '.'
13464 element_value = getHashEntry(element_hash, list->token);
13465 if (element_value != NULL) // element found
13466 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13467 &num_list_entries);
13470 // no further suffixes found -- this is not an element
13471 print_unknown_token(filename, list->token, num_unknown_tokens++);
13477 // token has format "<prefix>.<something>"
13479 action_token = strchr(list->token, '.'); // suffix may be action ...
13480 direction_token = action_token; // ... or direction
13482 element_token = getStringCopy(list->token);
13483 *strchr(element_token, '.') = '\0';
13485 element_value = getHashEntry(element_hash, element_token);
13487 if (element_value == NULL) // this is no element
13489 element_value = getHashEntry(element_hash, list->token);
13490 if (element_value != NULL) // combined element found
13491 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13492 &num_list_entries);
13494 print_unknown_token(filename, list->token, num_unknown_tokens++);
13496 free(element_token);
13501 action_value = getHashEntry(action_hash, action_token);
13503 if (action_value != NULL) // action found
13505 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13506 &num_list_entries);
13508 free(element_token);
13513 direction_value = getHashEntry(direction_hash, direction_token);
13515 if (direction_value != NULL) // direction found
13517 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13518 &num_list_entries);
13520 free(element_token);
13525 if (strchr(action_token + 1, '.') == NULL)
13527 // no further suffixes found -- this is not an action nor direction
13529 element_value = getHashEntry(element_hash, list->token);
13530 if (element_value != NULL) // combined element found
13531 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13532 &num_list_entries);
13534 print_unknown_token(filename, list->token, num_unknown_tokens++);
13536 free(element_token);
13541 // token has format "<prefix>.<suffix>.<something>"
13543 direction_token = strchr(action_token + 1, '.');
13545 action_token = getStringCopy(action_token);
13546 *strchr(action_token + 1, '.') = '\0';
13548 action_value = getHashEntry(action_hash, action_token);
13550 if (action_value == NULL) // this is no action
13552 element_value = getHashEntry(element_hash, list->token);
13553 if (element_value != NULL) // combined element found
13554 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13555 &num_list_entries);
13557 print_unknown_token(filename, list->token, num_unknown_tokens++);
13559 free(element_token);
13560 free(action_token);
13565 direction_value = getHashEntry(direction_hash, direction_token);
13567 if (direction_value != NULL) // direction found
13569 add_helpanim_entry(atoi(element_value), atoi(action_value),
13570 atoi(direction_value), delay, &num_list_entries);
13572 free(element_token);
13573 free(action_token);
13578 // this is no direction
13580 element_value = getHashEntry(element_hash, list->token);
13581 if (element_value != NULL) // combined element found
13582 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13583 &num_list_entries);
13585 print_unknown_token(filename, list->token, num_unknown_tokens++);
13587 free(element_token);
13588 free(action_token);
13591 print_unknown_token_end(num_unknown_tokens);
13593 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13594 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13596 freeSetupFileList(setup_file_list);
13597 freeSetupFileHash(element_hash);
13598 freeSetupFileHash(action_hash);
13599 freeSetupFileHash(direction_hash);
13602 for (i = 0; i < num_list_entries; i++)
13603 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13604 EL_NAME(helpanim_info[i].element),
13605 helpanim_info[i].element,
13606 helpanim_info[i].action,
13607 helpanim_info[i].direction,
13608 helpanim_info[i].delay);
13612 void LoadHelpTextInfo(void)
13614 char *filename = getHelpTextFilename();
13617 if (helptext_info != NULL)
13619 freeSetupFileHash(helptext_info);
13620 helptext_info = NULL;
13623 if (fileExists(filename))
13624 helptext_info = loadSetupFileHash(filename);
13626 if (helptext_info == NULL)
13628 // use reliable default values from static configuration
13629 helptext_info = newSetupFileHash();
13631 for (i = 0; helptext_config[i].token; i++)
13632 setHashEntry(helptext_info,
13633 helptext_config[i].token,
13634 helptext_config[i].value);
13638 BEGIN_HASH_ITERATION(helptext_info, itr)
13640 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13641 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13643 END_HASH_ITERATION(hash, itr)
13648 // ----------------------------------------------------------------------------
13650 // ----------------------------------------------------------------------------
13652 #define MAX_NUM_CONVERT_LEVELS 1000
13654 void ConvertLevels(void)
13656 static LevelDirTree *convert_leveldir = NULL;
13657 static int convert_level_nr = -1;
13658 static int num_levels_handled = 0;
13659 static int num_levels_converted = 0;
13660 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13663 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13664 global.convert_leveldir);
13666 if (convert_leveldir == NULL)
13667 Fail("no such level identifier: '%s'", global.convert_leveldir);
13669 leveldir_current = convert_leveldir;
13671 if (global.convert_level_nr != -1)
13673 convert_leveldir->first_level = global.convert_level_nr;
13674 convert_leveldir->last_level = global.convert_level_nr;
13677 convert_level_nr = convert_leveldir->first_level;
13679 PrintLine("=", 79);
13680 Print("Converting levels\n");
13681 PrintLine("-", 79);
13682 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13683 Print("Level series name: '%s'\n", convert_leveldir->name);
13684 Print("Level series author: '%s'\n", convert_leveldir->author);
13685 Print("Number of levels: %d\n", convert_leveldir->levels);
13686 PrintLine("=", 79);
13689 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13690 levels_failed[i] = FALSE;
13692 while (convert_level_nr <= convert_leveldir->last_level)
13694 char *level_filename;
13697 level_nr = convert_level_nr++;
13699 Print("Level %03d: ", level_nr);
13701 LoadLevel(level_nr);
13702 if (level.no_level_file || level.no_valid_file)
13704 Print("(no level)\n");
13708 Print("converting level ... ");
13711 // special case: conversion of some EMC levels as requested by ACME
13712 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13715 level_filename = getDefaultLevelFilename(level_nr);
13716 new_level = !fileExists(level_filename);
13720 SaveLevel(level_nr);
13722 num_levels_converted++;
13724 Print("converted.\n");
13728 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13729 levels_failed[level_nr] = TRUE;
13731 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13734 num_levels_handled++;
13738 PrintLine("=", 79);
13739 Print("Number of levels handled: %d\n", num_levels_handled);
13740 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13741 (num_levels_handled ?
13742 num_levels_converted * 100 / num_levels_handled : 0));
13743 PrintLine("-", 79);
13744 Print("Summary (for automatic parsing by scripts):\n");
13745 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13746 convert_leveldir->identifier, num_levels_converted,
13747 num_levels_handled,
13748 (num_levels_handled ?
13749 num_levels_converted * 100 / num_levels_handled : 0));
13751 if (num_levels_handled != num_levels_converted)
13753 Print(", FAILED:");
13754 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13755 if (levels_failed[i])
13760 PrintLine("=", 79);
13762 CloseAllAndExit(0);
13766 // ----------------------------------------------------------------------------
13767 // create and save images for use in level sketches (raw BMP format)
13768 // ----------------------------------------------------------------------------
13770 void CreateLevelSketchImages(void)
13776 InitElementPropertiesGfxElement();
13778 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13779 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13781 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13783 int element = getMappedElement(i);
13784 char basename1[16];
13785 char basename2[16];
13789 sprintf(basename1, "%04d.bmp", i);
13790 sprintf(basename2, "%04ds.bmp", i);
13792 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13793 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13795 DrawSizedElement(0, 0, element, TILESIZE);
13796 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13798 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13799 Fail("cannot save level sketch image file '%s'", filename1);
13801 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13802 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13804 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13805 Fail("cannot save level sketch image file '%s'", filename2);
13810 // create corresponding SQL statements (for normal and small images)
13813 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13814 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13817 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13818 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13820 // optional: create content for forum level sketch demonstration post
13822 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13825 FreeBitmap(bitmap1);
13826 FreeBitmap(bitmap2);
13829 fprintf(stderr, "\n");
13831 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13833 CloseAllAndExit(0);
13837 // ----------------------------------------------------------------------------
13838 // create and save images for element collecting animations (raw BMP format)
13839 // ----------------------------------------------------------------------------
13841 static boolean createCollectImage(int element)
13843 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13846 void CreateCollectElementImages(void)
13850 int anim_frames = num_steps - 1;
13851 int tile_size = TILESIZE;
13852 int anim_width = tile_size * anim_frames;
13853 int anim_height = tile_size;
13854 int num_collect_images = 0;
13855 int pos_collect_images = 0;
13857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13858 if (createCollectImage(i))
13859 num_collect_images++;
13861 Info("Creating %d element collecting animation images ...",
13862 num_collect_images);
13864 int dst_width = anim_width * 2;
13865 int dst_height = anim_height * num_collect_images / 2;
13866 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13867 char *basename = "RocksCollect.bmp";
13868 char *filename = getPath2(global.create_collect_images_dir, basename);
13870 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13872 if (!createCollectImage(i))
13875 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13876 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13877 int graphic = el2img(i);
13878 char *token_name = element_info[i].token_name;
13879 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13880 Bitmap *src_bitmap;
13883 Info("- creating collecting image for '%s' ...", token_name);
13885 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13887 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13888 tile_size, tile_size, 0, 0);
13890 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13892 for (j = 0; j < anim_frames; j++)
13894 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13895 int frame_size = frame_size_final * num_steps;
13896 int offset = (tile_size - frame_size_final) / 2;
13897 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13899 while (frame_size > frame_size_final)
13903 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13905 FreeBitmap(frame_bitmap);
13907 frame_bitmap = half_bitmap;
13910 BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13911 frame_size_final, frame_size_final,
13912 dst_x + j * tile_size + offset, dst_y + offset);
13914 FreeBitmap(frame_bitmap);
13917 tmp_bitmap->surface_masked = NULL;
13919 FreeBitmap(tmp_bitmap);
13921 pos_collect_images++;
13924 if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13925 Fail("cannot save element collecting image file '%s'", filename);
13927 FreeBitmap(dst_bitmap);
13931 CloseAllAndExit(0);
13935 // ----------------------------------------------------------------------------
13936 // create and save images for custom and group elements (raw BMP format)
13937 // ----------------------------------------------------------------------------
13939 void CreateCustomElementImages(char *directory)
13941 char *src_basename = "RocksCE-template.ilbm";
13942 char *dst_basename = "RocksCE.bmp";
13943 char *src_filename = getPath2(directory, src_basename);
13944 char *dst_filename = getPath2(directory, dst_basename);
13945 Bitmap *src_bitmap;
13947 int yoffset_ce = 0;
13948 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13951 InitVideoDefaults();
13953 ReCreateBitmap(&backbuffer, video.width, video.height);
13955 src_bitmap = LoadImage(src_filename);
13957 bitmap = CreateBitmap(TILEX * 16 * 2,
13958 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13961 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13968 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13969 TILEX * x, TILEY * y + yoffset_ce);
13971 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13973 TILEX * x + TILEX * 16,
13974 TILEY * y + yoffset_ce);
13976 for (j = 2; j >= 0; j--)
13980 BlitBitmap(src_bitmap, bitmap,
13981 TILEX + c * 7, 0, 6, 10,
13982 TILEX * x + 6 + j * 7,
13983 TILEY * y + 11 + yoffset_ce);
13985 BlitBitmap(src_bitmap, bitmap,
13986 TILEX + c * 8, TILEY, 6, 10,
13987 TILEX * 16 + TILEX * x + 6 + j * 8,
13988 TILEY * y + 10 + yoffset_ce);
13994 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14001 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14002 TILEX * x, TILEY * y + yoffset_ge);
14004 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14006 TILEX * x + TILEX * 16,
14007 TILEY * y + yoffset_ge);
14009 for (j = 1; j >= 0; j--)
14013 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14014 TILEX * x + 6 + j * 10,
14015 TILEY * y + 11 + yoffset_ge);
14017 BlitBitmap(src_bitmap, bitmap,
14018 TILEX + c * 8, TILEY + 12, 6, 10,
14019 TILEX * 16 + TILEX * x + 10 + j * 8,
14020 TILEY * y + 10 + yoffset_ge);
14026 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14027 Fail("cannot save CE graphics file '%s'", dst_filename);
14029 FreeBitmap(bitmap);
14031 CloseAllAndExit(0);