1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
927 &li.rotate_mm_ball_content, TRUE
931 EL_MM_STEEL_BLOCK, -1,
932 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
933 &li.mm_time_block, 75
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.score[SC_ELEM_BONUS], 10
941 // ---------- unused values -------------------------------------------------
944 EL_UNKNOWN, SAVE_CONF_NEVER,
945 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
946 &li.score[SC_UNKNOWN_15], 10
956 static struct LevelFileConfigInfo chunk_config_NOTE[] =
960 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
961 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
965 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
966 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
971 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
972 &xx_envelope.autowrap, FALSE
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
977 &xx_envelope.centered, FALSE
982 TYPE_STRING, CONF_VALUE_BYTES(1),
983 &xx_envelope.text, -1, NULL,
984 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
985 &xx_default_string_empty[0]
995 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
999 TYPE_STRING, CONF_VALUE_BYTES(1),
1000 &xx_ei.description[0], -1,
1001 &yy_ei.description[0],
1002 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1003 &xx_default_description[0]
1008 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1009 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1010 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1012 #if ENABLE_RESERVED_CODE
1013 // (reserved for later use)
1016 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1017 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1018 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1024 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1025 &xx_ei.use_gfx_element, FALSE,
1026 &yy_ei.use_gfx_element
1030 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1031 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1032 &yy_ei.gfx_element_initial
1037 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1038 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1039 &yy_ei.access_direction
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1045 &xx_ei.collect_score_initial, 10,
1046 &yy_ei.collect_score_initial
1050 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1051 &xx_ei.collect_count_initial, 1,
1052 &yy_ei.collect_count_initial
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1058 &xx_ei.ce_value_fixed_initial, 0,
1059 &yy_ei.ce_value_fixed_initial
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1064 &xx_ei.ce_value_random_initial, 0,
1065 &yy_ei.ce_value_random_initial
1069 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1070 &xx_ei.use_last_ce_value, FALSE,
1071 &yy_ei.use_last_ce_value
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1077 &xx_ei.push_delay_fixed, 8,
1078 &yy_ei.push_delay_fixed
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1083 &xx_ei.push_delay_random, 8,
1084 &yy_ei.push_delay_random
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1089 &xx_ei.drop_delay_fixed, 0,
1090 &yy_ei.drop_delay_fixed
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1095 &xx_ei.drop_delay_random, 0,
1096 &yy_ei.drop_delay_random
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1101 &xx_ei.move_delay_fixed, 0,
1102 &yy_ei.move_delay_fixed
1106 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1107 &xx_ei.move_delay_random, 0,
1108 &yy_ei.move_delay_random
1112 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1113 &xx_ei.step_delay_fixed, 0,
1114 &yy_ei.step_delay_fixed
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1119 &xx_ei.step_delay_random, 0,
1120 &yy_ei.step_delay_random
1125 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1126 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1131 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1132 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1133 &yy_ei.move_direction_initial
1137 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1138 &xx_ei.move_stepsize, TILEX / 8,
1139 &yy_ei.move_stepsize
1144 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1145 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1146 &yy_ei.move_enter_element
1150 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1151 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1152 &yy_ei.move_leave_element
1156 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1157 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1158 &yy_ei.move_leave_type
1163 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1164 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1165 &yy_ei.slippery_type
1170 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1171 &xx_ei.explosion_type, EXPLODES_3X3,
1172 &yy_ei.explosion_type
1176 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1177 &xx_ei.explosion_delay, 16,
1178 &yy_ei.explosion_delay
1182 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1183 &xx_ei.ignition_delay, 8,
1184 &yy_ei.ignition_delay
1189 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1190 &xx_ei.content, EL_EMPTY_SPACE,
1192 &xx_num_contents, 1, 1
1195 // ---------- "num_change_pages" must be the last entry ---------------------
1198 -1, SAVE_CONF_ALWAYS,
1199 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1200 &xx_ei.num_change_pages, 1,
1201 &yy_ei.num_change_pages
1212 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1214 // ---------- "current_change_page" must be the first entry -----------------
1217 -1, SAVE_CONF_ALWAYS,
1218 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1219 &xx_current_change_page, -1
1222 // ---------- (the remaining entries can be in any order) -------------------
1226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1227 &xx_change.can_change, FALSE
1232 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1233 &xx_event_bits[0], 0
1237 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1238 &xx_event_bits[1], 0
1243 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1244 &xx_change.trigger_player, CH_PLAYER_ANY
1248 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1249 &xx_change.trigger_side, CH_SIDE_ANY
1253 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1254 &xx_change.trigger_page, CH_PAGE_ANY
1259 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1260 &xx_change.target_element, EL_EMPTY_SPACE
1265 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1266 &xx_change.delay_fixed, 0
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1271 &xx_change.delay_random, 0
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1276 &xx_change.delay_frames, FRAMES_PER_SECOND
1281 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1282 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1288 &xx_change.explode, FALSE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1293 &xx_change.use_target_content, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1298 &xx_change.only_if_complete, FALSE
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1303 &xx_change.use_random_replace, FALSE
1307 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1308 &xx_change.random_percentage, 100
1312 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1313 &xx_change.replace_when, CP_WHEN_EMPTY
1318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1319 &xx_change.has_action, FALSE
1323 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1324 &xx_change.action_type, CA_NO_ACTION
1328 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1329 &xx_change.action_mode, CA_MODE_UNDEFINED
1333 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1334 &xx_change.action_arg, CA_ARG_UNDEFINED
1339 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1340 &xx_change.action_element, EL_EMPTY_SPACE
1345 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1346 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1347 &xx_num_contents, 1, 1
1357 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1361 TYPE_STRING, CONF_VALUE_BYTES(1),
1362 &xx_ei.description[0], -1, NULL,
1363 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1364 &xx_default_description[0]
1369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1370 &xx_ei.use_gfx_element, FALSE
1374 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1375 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1380 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1381 &xx_group.choice_mode, ANIM_RANDOM
1386 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1387 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1388 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1398 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1403 &xx_ei.use_gfx_element, FALSE
1407 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1408 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1418 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1423 &li.block_snap_field, TRUE
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1428 &li.continuous_snapping, TRUE
1432 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1433 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1438 &li.use_start_element[0], FALSE
1442 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1443 &li.start_element[0], EL_PLAYER_1
1447 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1448 &li.use_artwork_element[0], FALSE
1452 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1453 &li.artwork_element[0], EL_PLAYER_1
1457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1458 &li.use_explosion_element[0], FALSE
1462 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1463 &li.explosion_element[0], EL_PLAYER_1
1478 filetype_id_list[] =
1480 { LEVEL_FILE_TYPE_RND, "RND" },
1481 { LEVEL_FILE_TYPE_BD, "BD" },
1482 { LEVEL_FILE_TYPE_EM, "EM" },
1483 { LEVEL_FILE_TYPE_SP, "SP" },
1484 { LEVEL_FILE_TYPE_DX, "DX" },
1485 { LEVEL_FILE_TYPE_SB, "SB" },
1486 { LEVEL_FILE_TYPE_DC, "DC" },
1487 { LEVEL_FILE_TYPE_MM, "MM" },
1488 { LEVEL_FILE_TYPE_MM, "DF" },
1493 // ============================================================================
1494 // level file functions
1495 // ============================================================================
1497 static boolean check_special_flags(char *flag)
1499 if (strEqual(options.special_flags, flag) ||
1500 strEqual(leveldir_current->special_flags, flag))
1506 static struct DateInfo getCurrentDate(void)
1508 time_t epoch_seconds = time(NULL);
1509 struct tm *now = localtime(&epoch_seconds);
1510 struct DateInfo date;
1512 date.year = now->tm_year + 1900;
1513 date.month = now->tm_mon + 1;
1514 date.day = now->tm_mday;
1516 date.src = DATE_SRC_CLOCK;
1521 static void resetEventFlags(struct ElementChangeInfo *change)
1525 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1526 change->has_event[i] = FALSE;
1529 static void resetEventBits(void)
1533 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1534 xx_event_bits[i] = 0;
1537 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1541 /* important: only change event flag if corresponding event bit is set
1542 (this is because all xx_event_bits[] values are loaded separately,
1543 and all xx_event_bits[] values are set back to zero before loading
1544 another value xx_event_bits[x] (each value representing 32 flags)) */
1546 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1547 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1548 change->has_event[i] = TRUE;
1551 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1555 /* in contrast to the above function setEventFlagsFromEventBits(), it
1556 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1557 depending on the corresponding change->has_event[i] values here, as
1558 all xx_event_bits[] values are reset in resetEventBits() before */
1560 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1561 if (change->has_event[i])
1562 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1565 static char *getDefaultElementDescription(struct ElementInfo *ei)
1567 static char description[MAX_ELEMENT_NAME_LEN + 1];
1568 char *default_description = (ei->custom_description != NULL ?
1569 ei->custom_description :
1570 ei->editor_description);
1573 // always start with reliable default values
1574 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1575 description[i] = '\0';
1577 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1578 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1580 return &description[0];
1583 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1585 char *default_description = getDefaultElementDescription(ei);
1588 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1589 ei->description[i] = default_description[i];
1592 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1596 for (i = 0; conf[i].data_type != -1; i++)
1598 int default_value = conf[i].default_value;
1599 int data_type = conf[i].data_type;
1600 int conf_type = conf[i].conf_type;
1601 int byte_mask = conf_type & CONF_MASK_BYTES;
1603 if (byte_mask == CONF_MASK_MULTI_BYTES)
1605 int default_num_entities = conf[i].default_num_entities;
1606 int max_num_entities = conf[i].max_num_entities;
1608 *(int *)(conf[i].num_entities) = default_num_entities;
1610 if (data_type == TYPE_STRING)
1612 char *default_string = conf[i].default_string;
1613 char *string = (char *)(conf[i].value);
1615 strncpy(string, default_string, max_num_entities);
1617 else if (data_type == TYPE_ELEMENT_LIST)
1619 int *element_array = (int *)(conf[i].value);
1622 for (j = 0; j < max_num_entities; j++)
1623 element_array[j] = default_value;
1625 else if (data_type == TYPE_CONTENT_LIST)
1627 struct Content *content = (struct Content *)(conf[i].value);
1630 for (c = 0; c < max_num_entities; c++)
1631 for (y = 0; y < 3; y++)
1632 for (x = 0; x < 3; x++)
1633 content[c].e[x][y] = default_value;
1636 else // constant size configuration data (1, 2 or 4 bytes)
1638 if (data_type == TYPE_BOOLEAN)
1639 *(boolean *)(conf[i].value) = default_value;
1641 *(int *) (conf[i].value) = default_value;
1646 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1650 for (i = 0; conf[i].data_type != -1; i++)
1652 int data_type = conf[i].data_type;
1653 int conf_type = conf[i].conf_type;
1654 int byte_mask = conf_type & CONF_MASK_BYTES;
1656 if (byte_mask == CONF_MASK_MULTI_BYTES)
1658 int max_num_entities = conf[i].max_num_entities;
1660 if (data_type == TYPE_STRING)
1662 char *string = (char *)(conf[i].value);
1663 char *string_copy = (char *)(conf[i].value_copy);
1665 strncpy(string_copy, string, max_num_entities);
1667 else if (data_type == TYPE_ELEMENT_LIST)
1669 int *element_array = (int *)(conf[i].value);
1670 int *element_array_copy = (int *)(conf[i].value_copy);
1673 for (j = 0; j < max_num_entities; j++)
1674 element_array_copy[j] = element_array[j];
1676 else if (data_type == TYPE_CONTENT_LIST)
1678 struct Content *content = (struct Content *)(conf[i].value);
1679 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1682 for (c = 0; c < max_num_entities; c++)
1683 for (y = 0; y < 3; y++)
1684 for (x = 0; x < 3; x++)
1685 content_copy[c].e[x][y] = content[c].e[x][y];
1688 else // constant size configuration data (1, 2 or 4 bytes)
1690 if (data_type == TYPE_BOOLEAN)
1691 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1693 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1698 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1702 xx_ei = *ei_from; // copy element data into temporary buffer
1703 yy_ei = *ei_to; // copy element data into temporary buffer
1705 copyConfigFromConfigList(chunk_config_CUSX_base);
1710 // ---------- reinitialize and copy change pages ----------
1712 ei_to->num_change_pages = ei_from->num_change_pages;
1713 ei_to->current_change_page = ei_from->current_change_page;
1715 setElementChangePages(ei_to, ei_to->num_change_pages);
1717 for (i = 0; i < ei_to->num_change_pages; i++)
1718 ei_to->change_page[i] = ei_from->change_page[i];
1720 // ---------- copy group element info ----------
1721 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1722 *ei_to->group = *ei_from->group;
1724 // mark this custom element as modified
1725 ei_to->modified_settings = TRUE;
1728 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1730 int change_page_size = sizeof(struct ElementChangeInfo);
1732 ei->num_change_pages = MAX(1, change_pages);
1735 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1737 if (ei->current_change_page >= ei->num_change_pages)
1738 ei->current_change_page = ei->num_change_pages - 1;
1740 ei->change = &ei->change_page[ei->current_change_page];
1743 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1745 xx_change = *change; // copy change data into temporary buffer
1747 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1749 *change = xx_change;
1751 resetEventFlags(change);
1753 change->direct_action = 0;
1754 change->other_action = 0;
1756 change->pre_change_function = NULL;
1757 change->change_function = NULL;
1758 change->post_change_function = NULL;
1761 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1765 li = *level; // copy level data into temporary buffer
1766 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1767 *level = li; // copy temporary buffer back to level data
1769 setLevelInfoToDefaults_EM();
1770 setLevelInfoToDefaults_SP();
1771 setLevelInfoToDefaults_MM();
1773 level->native_em_level = &native_em_level;
1774 level->native_sp_level = &native_sp_level;
1775 level->native_mm_level = &native_mm_level;
1777 level->file_version = FILE_VERSION_ACTUAL;
1778 level->game_version = GAME_VERSION_ACTUAL;
1780 level->creation_date = getCurrentDate();
1782 level->encoding_16bit_field = TRUE;
1783 level->encoding_16bit_yamyam = TRUE;
1784 level->encoding_16bit_amoeba = TRUE;
1786 // clear level name and level author string buffers
1787 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1788 level->name[i] = '\0';
1789 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1790 level->author[i] = '\0';
1792 // set level name and level author to default values
1793 strcpy(level->name, NAMELESS_LEVEL_NAME);
1794 strcpy(level->author, ANONYMOUS_NAME);
1796 // set level playfield to playable default level with player and exit
1797 for (x = 0; x < MAX_LEV_FIELDX; x++)
1798 for (y = 0; y < MAX_LEV_FIELDY; y++)
1799 level->field[x][y] = EL_SAND;
1801 level->field[0][0] = EL_PLAYER_1;
1802 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1804 BorderElement = EL_STEELWALL;
1806 // detect custom elements when loading them
1807 level->file_has_custom_elements = FALSE;
1809 // set all bug compatibility flags to "false" => do not emulate this bug
1810 level->use_action_after_change_bug = FALSE;
1812 if (leveldir_current)
1814 // try to determine better author name than 'anonymous'
1815 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1817 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1818 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1822 switch (LEVELCLASS(leveldir_current))
1824 case LEVELCLASS_TUTORIAL:
1825 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1828 case LEVELCLASS_CONTRIB:
1829 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1830 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1833 case LEVELCLASS_PRIVATE:
1834 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1835 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1839 // keep default value
1846 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1848 static boolean clipboard_elements_initialized = FALSE;
1851 InitElementPropertiesStatic();
1853 li = *level; // copy level data into temporary buffer
1854 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1855 *level = li; // copy temporary buffer back to level data
1857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1860 struct ElementInfo *ei = &element_info[element];
1862 if (element == EL_MM_GRAY_BALL)
1864 struct LevelInfo_MM *level_mm = level->native_mm_level;
1867 for (j = 0; j < level->num_mm_ball_contents; j++)
1868 level->mm_ball_content[j] =
1869 map_element_MM_to_RND(level_mm->ball_content[j]);
1872 // never initialize clipboard elements after the very first time
1873 // (to be able to use clipboard elements between several levels)
1874 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1877 if (IS_ENVELOPE(element))
1879 int envelope_nr = element - EL_ENVELOPE_1;
1881 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1883 level->envelope[envelope_nr] = xx_envelope;
1886 if (IS_CUSTOM_ELEMENT(element) ||
1887 IS_GROUP_ELEMENT(element) ||
1888 IS_INTERNAL_ELEMENT(element))
1890 xx_ei = *ei; // copy element data into temporary buffer
1892 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1897 setElementChangePages(ei, 1);
1898 setElementChangeInfoToDefaults(ei->change);
1900 if (IS_CUSTOM_ELEMENT(element) ||
1901 IS_GROUP_ELEMENT(element) ||
1902 IS_INTERNAL_ELEMENT(element))
1904 setElementDescriptionToDefault(ei);
1906 ei->modified_settings = FALSE;
1909 if (IS_CUSTOM_ELEMENT(element) ||
1910 IS_INTERNAL_ELEMENT(element))
1912 // internal values used in level editor
1914 ei->access_type = 0;
1915 ei->access_layer = 0;
1916 ei->access_protected = 0;
1917 ei->walk_to_action = 0;
1918 ei->smash_targets = 0;
1921 ei->can_explode_by_fire = FALSE;
1922 ei->can_explode_smashed = FALSE;
1923 ei->can_explode_impact = FALSE;
1925 ei->current_change_page = 0;
1928 if (IS_GROUP_ELEMENT(element) ||
1929 IS_INTERNAL_ELEMENT(element))
1931 struct ElementGroupInfo *group;
1933 // initialize memory for list of elements in group
1934 if (ei->group == NULL)
1935 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1939 xx_group = *group; // copy group data into temporary buffer
1941 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1946 if (IS_EMPTY_ELEMENT(element) ||
1947 IS_INTERNAL_ELEMENT(element))
1949 xx_ei = *ei; // copy element data into temporary buffer
1951 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1957 clipboard_elements_initialized = TRUE;
1960 static void setLevelInfoToDefaults(struct LevelInfo *level,
1961 boolean level_info_only,
1962 boolean reset_file_status)
1964 setLevelInfoToDefaults_Level(level);
1966 if (!level_info_only)
1967 setLevelInfoToDefaults_Elements(level);
1969 if (reset_file_status)
1971 level->no_valid_file = FALSE;
1972 level->no_level_file = FALSE;
1975 level->changed = FALSE;
1978 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1980 level_file_info->nr = 0;
1981 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1982 level_file_info->packed = FALSE;
1984 setString(&level_file_info->basename, NULL);
1985 setString(&level_file_info->filename, NULL);
1988 int getMappedElement_SB(int, boolean);
1990 static void ActivateLevelTemplate(void)
1994 if (check_special_flags("load_xsb_to_ces"))
1996 // fill smaller playfields with padding "beyond border wall" elements
1997 if (level.fieldx < level_template.fieldx ||
1998 level.fieldy < level_template.fieldy)
2000 short field[level.fieldx][level.fieldy];
2001 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2002 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2003 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2004 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2006 // copy old playfield (which is smaller than the visible area)
2007 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2008 field[x][y] = level.field[x][y];
2010 // fill new, larger playfield with "beyond border wall" elements
2011 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2012 level.field[x][y] = getMappedElement_SB('_', TRUE);
2014 // copy the old playfield to the middle of the new playfield
2015 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2016 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2018 level.fieldx = new_fieldx;
2019 level.fieldy = new_fieldy;
2023 // Currently there is no special action needed to activate the template
2024 // data, because 'element_info' property settings overwrite the original
2025 // level data, while all other variables do not change.
2027 // Exception: 'from_level_template' elements in the original level playfield
2028 // are overwritten with the corresponding elements at the same position in
2029 // playfield from the level template.
2031 for (x = 0; x < level.fieldx; x++)
2032 for (y = 0; y < level.fieldy; y++)
2033 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2034 level.field[x][y] = level_template.field[x][y];
2036 if (check_special_flags("load_xsb_to_ces"))
2038 struct LevelInfo level_backup = level;
2040 // overwrite all individual level settings from template level settings
2041 level = level_template;
2043 // restore level file info
2044 level.file_info = level_backup.file_info;
2046 // restore playfield size
2047 level.fieldx = level_backup.fieldx;
2048 level.fieldy = level_backup.fieldy;
2050 // restore playfield content
2051 for (x = 0; x < level.fieldx; x++)
2052 for (y = 0; y < level.fieldy; y++)
2053 level.field[x][y] = level_backup.field[x][y];
2055 // restore name and author from individual level
2056 strcpy(level.name, level_backup.name);
2057 strcpy(level.author, level_backup.author);
2059 // restore flag "use_custom_template"
2060 level.use_custom_template = level_backup.use_custom_template;
2064 static char *getLevelFilenameFromBasename(char *basename)
2066 static char *filename = NULL;
2068 checked_free(filename);
2070 filename = getPath2(getCurrentLevelDir(), basename);
2075 static int getFileTypeFromBasename(char *basename)
2077 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2079 static char *filename = NULL;
2080 struct stat file_status;
2082 // ---------- try to determine file type from filename ----------
2084 // check for typical filename of a Supaplex level package file
2085 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2086 return LEVEL_FILE_TYPE_SP;
2088 // check for typical filename of a Diamond Caves II level package file
2089 if (strSuffixLower(basename, ".dc") ||
2090 strSuffixLower(basename, ".dc2"))
2091 return LEVEL_FILE_TYPE_DC;
2093 // check for typical filename of a Sokoban level package file
2094 if (strSuffixLower(basename, ".xsb") &&
2095 strchr(basename, '%') == NULL)
2096 return LEVEL_FILE_TYPE_SB;
2098 // ---------- try to determine file type from filesize ----------
2100 checked_free(filename);
2101 filename = getPath2(getCurrentLevelDir(), basename);
2103 if (stat(filename, &file_status) == 0)
2105 // check for typical filesize of a Supaplex level package file
2106 if (file_status.st_size == 170496)
2107 return LEVEL_FILE_TYPE_SP;
2110 return LEVEL_FILE_TYPE_UNKNOWN;
2113 static int getFileTypeFromMagicBytes(char *filename, int type)
2117 if ((file = openFile(filename, MODE_READ)))
2119 char chunk_name[CHUNK_ID_LEN + 1];
2121 getFileChunkBE(file, chunk_name, NULL);
2123 if (strEqual(chunk_name, "MMII") ||
2124 strEqual(chunk_name, "MIRR"))
2125 type = LEVEL_FILE_TYPE_MM;
2133 static boolean checkForPackageFromBasename(char *basename)
2135 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2136 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2138 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2141 static char *getSingleLevelBasenameExt(int nr, char *extension)
2143 static char basename[MAX_FILENAME_LEN];
2146 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2148 sprintf(basename, "%03d.%s", nr, extension);
2153 static char *getSingleLevelBasename(int nr)
2155 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2158 static char *getPackedLevelBasename(int type)
2160 static char basename[MAX_FILENAME_LEN];
2161 char *directory = getCurrentLevelDir();
2163 DirectoryEntry *dir_entry;
2165 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2167 if ((dir = openDirectory(directory)) == NULL)
2169 Warn("cannot read current level directory '%s'", directory);
2174 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2176 char *entry_basename = dir_entry->basename;
2177 int entry_type = getFileTypeFromBasename(entry_basename);
2179 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2181 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2184 strcpy(basename, entry_basename);
2191 closeDirectory(dir);
2196 static char *getSingleLevelFilename(int nr)
2198 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2201 #if ENABLE_UNUSED_CODE
2202 static char *getPackedLevelFilename(int type)
2204 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2208 char *getDefaultLevelFilename(int nr)
2210 return getSingleLevelFilename(nr);
2213 #if ENABLE_UNUSED_CODE
2214 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2218 lfi->packed = FALSE;
2220 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2221 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2225 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2226 int type, char *format, ...)
2228 static char basename[MAX_FILENAME_LEN];
2231 va_start(ap, format);
2232 vsprintf(basename, format, ap);
2236 lfi->packed = FALSE;
2238 setString(&lfi->basename, basename);
2239 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2242 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2248 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2249 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2252 static int getFiletypeFromID(char *filetype_id)
2254 char *filetype_id_lower;
2255 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2258 if (filetype_id == NULL)
2259 return LEVEL_FILE_TYPE_UNKNOWN;
2261 filetype_id_lower = getStringToLower(filetype_id);
2263 for (i = 0; filetype_id_list[i].id != NULL; i++)
2265 char *id_lower = getStringToLower(filetype_id_list[i].id);
2267 if (strEqual(filetype_id_lower, id_lower))
2268 filetype = filetype_id_list[i].filetype;
2272 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2276 free(filetype_id_lower);
2281 char *getLocalLevelTemplateFilename(void)
2283 return getDefaultLevelFilename(-1);
2286 char *getGlobalLevelTemplateFilename(void)
2288 // global variable "leveldir_current" must be modified in the loop below
2289 LevelDirTree *leveldir_current_last = leveldir_current;
2290 char *filename = NULL;
2292 // check for template level in path from current to topmost tree node
2294 while (leveldir_current != NULL)
2296 filename = getDefaultLevelFilename(-1);
2298 if (fileExists(filename))
2301 leveldir_current = leveldir_current->node_parent;
2304 // restore global variable "leveldir_current" modified in above loop
2305 leveldir_current = leveldir_current_last;
2310 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2314 // special case: level number is negative => check for level template file
2317 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2318 getSingleLevelBasename(-1));
2320 // replace local level template filename with global template filename
2321 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2323 // no fallback if template file not existing
2327 // special case: check for file name/pattern specified in "levelinfo.conf"
2328 if (leveldir_current->level_filename != NULL)
2330 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2332 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2333 leveldir_current->level_filename, nr);
2335 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2337 if (fileExists(lfi->filename))
2340 else if (leveldir_current->level_filetype != NULL)
2342 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2344 // check for specified native level file with standard file name
2345 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2346 "%03d.%s", nr, LEVELFILE_EXTENSION);
2347 if (fileExists(lfi->filename))
2351 // check for native Rocks'n'Diamonds level file
2352 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2353 "%03d.%s", nr, LEVELFILE_EXTENSION);
2354 if (fileExists(lfi->filename))
2357 // check for Emerald Mine level file (V1)
2358 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2359 'a' + (nr / 10) % 26, '0' + nr % 10);
2360 if (fileExists(lfi->filename))
2362 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2363 'A' + (nr / 10) % 26, '0' + nr % 10);
2364 if (fileExists(lfi->filename))
2367 // check for Emerald Mine level file (V2 to V5)
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2369 if (fileExists(lfi->filename))
2372 // check for Emerald Mine level file (V6 / single mode)
2373 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2374 if (fileExists(lfi->filename))
2376 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2377 if (fileExists(lfi->filename))
2380 // check for Emerald Mine level file (V6 / teamwork mode)
2381 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2382 if (fileExists(lfi->filename))
2384 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2385 if (fileExists(lfi->filename))
2388 // check for various packed level file formats
2389 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2390 if (fileExists(lfi->filename))
2393 // no known level file found -- use default values (and fail later)
2394 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2395 "%03d.%s", nr, LEVELFILE_EXTENSION);
2398 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2400 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2401 lfi->type = getFileTypeFromBasename(lfi->basename);
2403 if (lfi->type == LEVEL_FILE_TYPE_RND)
2404 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2407 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2409 // always start with reliable default values
2410 setFileInfoToDefaults(level_file_info);
2412 level_file_info->nr = nr; // set requested level number
2414 determineLevelFileInfo_Filename(level_file_info);
2415 determineLevelFileInfo_Filetype(level_file_info);
2418 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2419 struct LevelFileInfo *lfi_to)
2421 lfi_to->nr = lfi_from->nr;
2422 lfi_to->type = lfi_from->type;
2423 lfi_to->packed = lfi_from->packed;
2425 setString(&lfi_to->basename, lfi_from->basename);
2426 setString(&lfi_to->filename, lfi_from->filename);
2429 // ----------------------------------------------------------------------------
2430 // functions for loading R'n'D level
2431 // ----------------------------------------------------------------------------
2433 int getMappedElement(int element)
2435 // remap some (historic, now obsolete) elements
2439 case EL_PLAYER_OBSOLETE:
2440 element = EL_PLAYER_1;
2443 case EL_KEY_OBSOLETE:
2447 case EL_EM_KEY_1_FILE_OBSOLETE:
2448 element = EL_EM_KEY_1;
2451 case EL_EM_KEY_2_FILE_OBSOLETE:
2452 element = EL_EM_KEY_2;
2455 case EL_EM_KEY_3_FILE_OBSOLETE:
2456 element = EL_EM_KEY_3;
2459 case EL_EM_KEY_4_FILE_OBSOLETE:
2460 element = EL_EM_KEY_4;
2463 case EL_ENVELOPE_OBSOLETE:
2464 element = EL_ENVELOPE_1;
2472 if (element >= NUM_FILE_ELEMENTS)
2474 Warn("invalid level element %d", element);
2476 element = EL_UNKNOWN;
2484 static int getMappedElementByVersion(int element, int game_version)
2486 // remap some elements due to certain game version
2488 if (game_version <= VERSION_IDENT(2,2,0,0))
2490 // map game font elements
2491 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2492 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2493 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2494 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2497 if (game_version < VERSION_IDENT(3,0,0,0))
2499 // map Supaplex gravity tube elements
2500 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2501 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2502 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2503 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2510 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2512 level->file_version = getFileVersion(file);
2513 level->game_version = getFileVersion(file);
2518 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2520 level->creation_date.year = getFile16BitBE(file);
2521 level->creation_date.month = getFile8Bit(file);
2522 level->creation_date.day = getFile8Bit(file);
2524 level->creation_date.src = DATE_SRC_LEVELFILE;
2529 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2531 int initial_player_stepsize;
2532 int initial_player_gravity;
2535 level->fieldx = getFile8Bit(file);
2536 level->fieldy = getFile8Bit(file);
2538 level->time = getFile16BitBE(file);
2539 level->gems_needed = getFile16BitBE(file);
2541 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2542 level->name[i] = getFile8Bit(file);
2543 level->name[MAX_LEVEL_NAME_LEN] = 0;
2545 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2546 level->score[i] = getFile8Bit(file);
2548 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2549 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2550 for (y = 0; y < 3; y++)
2551 for (x = 0; x < 3; x++)
2552 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2554 level->amoeba_speed = getFile8Bit(file);
2555 level->time_magic_wall = getFile8Bit(file);
2556 level->time_wheel = getFile8Bit(file);
2557 level->amoeba_content = getMappedElement(getFile8Bit(file));
2559 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2562 for (i = 0; i < MAX_PLAYERS; i++)
2563 level->initial_player_stepsize[i] = initial_player_stepsize;
2565 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2567 for (i = 0; i < MAX_PLAYERS; i++)
2568 level->initial_player_gravity[i] = initial_player_gravity;
2570 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2571 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2573 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2575 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2576 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2577 level->can_move_into_acid_bits = getFile32BitBE(file);
2578 level->dont_collide_with_bits = getFile8Bit(file);
2580 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2583 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2584 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2585 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2587 level->game_engine_type = getFile8Bit(file);
2589 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2594 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2598 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2599 level->name[i] = getFile8Bit(file);
2600 level->name[MAX_LEVEL_NAME_LEN] = 0;
2605 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2609 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2610 level->author[i] = getFile8Bit(file);
2611 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2616 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2619 int chunk_size_expected = level->fieldx * level->fieldy;
2621 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2622 stored with 16-bit encoding (and should be twice as big then).
2623 Even worse, playfield data was stored 16-bit when only yamyam content
2624 contained 16-bit elements and vice versa. */
2626 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2627 chunk_size_expected *= 2;
2629 if (chunk_size_expected != chunk_size)
2631 ReadUnusedBytesFromFile(file, chunk_size);
2632 return chunk_size_expected;
2635 for (y = 0; y < level->fieldy; y++)
2636 for (x = 0; x < level->fieldx; x++)
2637 level->field[x][y] =
2638 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2643 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2646 int header_size = 4;
2647 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2648 int chunk_size_expected = header_size + content_size;
2650 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2651 stored with 16-bit encoding (and should be twice as big then).
2652 Even worse, playfield data was stored 16-bit when only yamyam content
2653 contained 16-bit elements and vice versa. */
2655 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2656 chunk_size_expected += content_size;
2658 if (chunk_size_expected != chunk_size)
2660 ReadUnusedBytesFromFile(file, chunk_size);
2661 return chunk_size_expected;
2665 level->num_yamyam_contents = getFile8Bit(file);
2669 // correct invalid number of content fields -- should never happen
2670 if (level->num_yamyam_contents < 1 ||
2671 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2672 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2674 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2675 for (y = 0; y < 3; y++)
2676 for (x = 0; x < 3; x++)
2677 level->yamyam_content[i].e[x][y] =
2678 getMappedElement(level->encoding_16bit_field ?
2679 getFile16BitBE(file) : getFile8Bit(file));
2683 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2688 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2690 element = getMappedElement(getFile16BitBE(file));
2691 num_contents = getFile8Bit(file);
2693 getFile8Bit(file); // content x size (unused)
2694 getFile8Bit(file); // content y size (unused)
2696 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2698 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2699 for (y = 0; y < 3; y++)
2700 for (x = 0; x < 3; x++)
2701 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2703 // correct invalid number of content fields -- should never happen
2704 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2705 num_contents = STD_ELEMENT_CONTENTS;
2707 if (element == EL_YAMYAM)
2709 level->num_yamyam_contents = num_contents;
2711 for (i = 0; i < num_contents; i++)
2712 for (y = 0; y < 3; y++)
2713 for (x = 0; x < 3; x++)
2714 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2716 else if (element == EL_BD_AMOEBA)
2718 level->amoeba_content = content_array[0][0][0];
2722 Warn("cannot load content for element '%d'", element);
2728 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2734 int chunk_size_expected;
2736 element = getMappedElement(getFile16BitBE(file));
2737 if (!IS_ENVELOPE(element))
2738 element = EL_ENVELOPE_1;
2740 envelope_nr = element - EL_ENVELOPE_1;
2742 envelope_len = getFile16BitBE(file);
2744 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2745 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2747 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2749 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2750 if (chunk_size_expected != chunk_size)
2752 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2753 return chunk_size_expected;
2756 for (i = 0; i < envelope_len; i++)
2757 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2762 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2764 int num_changed_custom_elements = getFile16BitBE(file);
2765 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2768 if (chunk_size_expected != chunk_size)
2770 ReadUnusedBytesFromFile(file, chunk_size - 2);
2771 return chunk_size_expected;
2774 for (i = 0; i < num_changed_custom_elements; i++)
2776 int element = getMappedElement(getFile16BitBE(file));
2777 int properties = getFile32BitBE(file);
2779 if (IS_CUSTOM_ELEMENT(element))
2780 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2782 Warn("invalid custom element number %d", element);
2784 // older game versions that wrote level files with CUS1 chunks used
2785 // different default push delay values (not yet stored in level file)
2786 element_info[element].push_delay_fixed = 2;
2787 element_info[element].push_delay_random = 8;
2790 level->file_has_custom_elements = TRUE;
2795 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2797 int num_changed_custom_elements = getFile16BitBE(file);
2798 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
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 int custom_target_element = getMappedElement(getFile16BitBE(file));
2812 if (IS_CUSTOM_ELEMENT(element))
2813 element_info[element].change->target_element = custom_target_element;
2815 Warn("invalid custom element number %d", element);
2818 level->file_has_custom_elements = TRUE;
2823 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2825 int num_changed_custom_elements = getFile16BitBE(file);
2826 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2829 if (chunk_size_expected != chunk_size)
2831 ReadUnusedBytesFromFile(file, chunk_size - 2);
2832 return chunk_size_expected;
2835 for (i = 0; i < num_changed_custom_elements; i++)
2837 int element = getMappedElement(getFile16BitBE(file));
2838 struct ElementInfo *ei = &element_info[element];
2839 unsigned int event_bits;
2841 if (!IS_CUSTOM_ELEMENT(element))
2843 Warn("invalid custom element number %d", element);
2845 element = EL_INTERNAL_DUMMY;
2848 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2849 ei->description[j] = getFile8Bit(file);
2850 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2852 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2854 // some free bytes for future properties and padding
2855 ReadUnusedBytesFromFile(file, 7);
2857 ei->use_gfx_element = getFile8Bit(file);
2858 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2860 ei->collect_score_initial = getFile8Bit(file);
2861 ei->collect_count_initial = getFile8Bit(file);
2863 ei->push_delay_fixed = getFile16BitBE(file);
2864 ei->push_delay_random = getFile16BitBE(file);
2865 ei->move_delay_fixed = getFile16BitBE(file);
2866 ei->move_delay_random = getFile16BitBE(file);
2868 ei->move_pattern = getFile16BitBE(file);
2869 ei->move_direction_initial = getFile8Bit(file);
2870 ei->move_stepsize = getFile8Bit(file);
2872 for (y = 0; y < 3; y++)
2873 for (x = 0; x < 3; x++)
2874 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2876 // bits 0 - 31 of "has_event[]"
2877 event_bits = getFile32BitBE(file);
2878 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2879 if (event_bits & (1u << j))
2880 ei->change->has_event[j] = TRUE;
2882 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2884 ei->change->delay_fixed = getFile16BitBE(file);
2885 ei->change->delay_random = getFile16BitBE(file);
2886 ei->change->delay_frames = getFile16BitBE(file);
2888 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2890 ei->change->explode = getFile8Bit(file);
2891 ei->change->use_target_content = getFile8Bit(file);
2892 ei->change->only_if_complete = getFile8Bit(file);
2893 ei->change->use_random_replace = getFile8Bit(file);
2895 ei->change->random_percentage = getFile8Bit(file);
2896 ei->change->replace_when = getFile8Bit(file);
2898 for (y = 0; y < 3; y++)
2899 for (x = 0; x < 3; x++)
2900 ei->change->target_content.e[x][y] =
2901 getMappedElement(getFile16BitBE(file));
2903 ei->slippery_type = getFile8Bit(file);
2905 // some free bytes for future properties and padding
2906 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2908 // mark that this custom element has been modified
2909 ei->modified_settings = TRUE;
2912 level->file_has_custom_elements = TRUE;
2917 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2919 struct ElementInfo *ei;
2920 int chunk_size_expected;
2924 // ---------- custom element base property values (96 bytes) ----------------
2926 element = getMappedElement(getFile16BitBE(file));
2928 if (!IS_CUSTOM_ELEMENT(element))
2930 Warn("invalid custom element number %d", element);
2932 ReadUnusedBytesFromFile(file, chunk_size - 2);
2937 ei = &element_info[element];
2939 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2940 ei->description[i] = getFile8Bit(file);
2941 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2943 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2945 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2947 ei->num_change_pages = getFile8Bit(file);
2949 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2950 if (chunk_size_expected != chunk_size)
2952 ReadUnusedBytesFromFile(file, chunk_size - 43);
2953 return chunk_size_expected;
2956 ei->ce_value_fixed_initial = getFile16BitBE(file);
2957 ei->ce_value_random_initial = getFile16BitBE(file);
2958 ei->use_last_ce_value = getFile8Bit(file);
2960 ei->use_gfx_element = getFile8Bit(file);
2961 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2963 ei->collect_score_initial = getFile8Bit(file);
2964 ei->collect_count_initial = getFile8Bit(file);
2966 ei->drop_delay_fixed = getFile8Bit(file);
2967 ei->push_delay_fixed = getFile8Bit(file);
2968 ei->drop_delay_random = getFile8Bit(file);
2969 ei->push_delay_random = getFile8Bit(file);
2970 ei->move_delay_fixed = getFile16BitBE(file);
2971 ei->move_delay_random = getFile16BitBE(file);
2973 // bits 0 - 15 of "move_pattern" ...
2974 ei->move_pattern = getFile16BitBE(file);
2975 ei->move_direction_initial = getFile8Bit(file);
2976 ei->move_stepsize = getFile8Bit(file);
2978 ei->slippery_type = getFile8Bit(file);
2980 for (y = 0; y < 3; y++)
2981 for (x = 0; x < 3; x++)
2982 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2984 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2985 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2986 ei->move_leave_type = getFile8Bit(file);
2988 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2989 ei->move_pattern |= (getFile16BitBE(file) << 16);
2991 ei->access_direction = getFile8Bit(file);
2993 ei->explosion_delay = getFile8Bit(file);
2994 ei->ignition_delay = getFile8Bit(file);
2995 ei->explosion_type = getFile8Bit(file);
2997 // some free bytes for future custom property values and padding
2998 ReadUnusedBytesFromFile(file, 1);
3000 // ---------- change page property values (48 bytes) ------------------------
3002 setElementChangePages(ei, ei->num_change_pages);
3004 for (i = 0; i < ei->num_change_pages; i++)
3006 struct ElementChangeInfo *change = &ei->change_page[i];
3007 unsigned int event_bits;
3009 // always start with reliable default values
3010 setElementChangeInfoToDefaults(change);
3012 // bits 0 - 31 of "has_event[]" ...
3013 event_bits = getFile32BitBE(file);
3014 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3015 if (event_bits & (1u << j))
3016 change->has_event[j] = TRUE;
3018 change->target_element = getMappedElement(getFile16BitBE(file));
3020 change->delay_fixed = getFile16BitBE(file);
3021 change->delay_random = getFile16BitBE(file);
3022 change->delay_frames = getFile16BitBE(file);
3024 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3026 change->explode = getFile8Bit(file);
3027 change->use_target_content = getFile8Bit(file);
3028 change->only_if_complete = getFile8Bit(file);
3029 change->use_random_replace = getFile8Bit(file);
3031 change->random_percentage = getFile8Bit(file);
3032 change->replace_when = getFile8Bit(file);
3034 for (y = 0; y < 3; y++)
3035 for (x = 0; x < 3; x++)
3036 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3038 change->can_change = getFile8Bit(file);
3040 change->trigger_side = getFile8Bit(file);
3042 change->trigger_player = getFile8Bit(file);
3043 change->trigger_page = getFile8Bit(file);
3045 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3046 CH_PAGE_ANY : (1 << change->trigger_page));
3048 change->has_action = getFile8Bit(file);
3049 change->action_type = getFile8Bit(file);
3050 change->action_mode = getFile8Bit(file);
3051 change->action_arg = getFile16BitBE(file);
3053 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3054 event_bits = getFile8Bit(file);
3055 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3056 if (event_bits & (1u << (j - 32)))
3057 change->has_event[j] = TRUE;
3060 // mark this custom element as modified
3061 ei->modified_settings = TRUE;
3063 level->file_has_custom_elements = TRUE;
3068 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3070 struct ElementInfo *ei;
3071 struct ElementGroupInfo *group;
3075 element = getMappedElement(getFile16BitBE(file));
3077 if (!IS_GROUP_ELEMENT(element))
3079 Warn("invalid group element number %d", element);
3081 ReadUnusedBytesFromFile(file, chunk_size - 2);
3086 ei = &element_info[element];
3088 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3089 ei->description[i] = getFile8Bit(file);
3090 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3092 group = element_info[element].group;
3094 group->num_elements = getFile8Bit(file);
3096 ei->use_gfx_element = getFile8Bit(file);
3097 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3099 group->choice_mode = getFile8Bit(file);
3101 // some free bytes for future values and padding
3102 ReadUnusedBytesFromFile(file, 3);
3104 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3105 group->element[i] = getMappedElement(getFile16BitBE(file));
3107 // mark this group element as modified
3108 element_info[element].modified_settings = TRUE;
3110 level->file_has_custom_elements = TRUE;
3115 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3116 int element, int real_element)
3118 int micro_chunk_size = 0;
3119 int conf_type = getFile8Bit(file);
3120 int byte_mask = conf_type & CONF_MASK_BYTES;
3121 boolean element_found = FALSE;
3124 micro_chunk_size += 1;
3126 if (byte_mask == CONF_MASK_MULTI_BYTES)
3128 int num_bytes = getFile16BitBE(file);
3129 byte *buffer = checked_malloc(num_bytes);
3131 ReadBytesFromFile(file, buffer, num_bytes);
3133 for (i = 0; conf[i].data_type != -1; i++)
3135 if (conf[i].element == element &&
3136 conf[i].conf_type == conf_type)
3138 int data_type = conf[i].data_type;
3139 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3140 int max_num_entities = conf[i].max_num_entities;
3142 if (num_entities > max_num_entities)
3144 Warn("truncating number of entities for element %d from %d to %d",
3145 element, num_entities, max_num_entities);
3147 num_entities = max_num_entities;
3150 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3151 data_type == TYPE_CONTENT_LIST))
3153 // for element and content lists, zero entities are not allowed
3154 Warn("found empty list of entities for element %d", element);
3156 // do not set "num_entities" here to prevent reading behind buffer
3158 *(int *)(conf[i].num_entities) = 1; // at least one is required
3162 *(int *)(conf[i].num_entities) = num_entities;
3165 element_found = TRUE;
3167 if (data_type == TYPE_STRING)
3169 char *string = (char *)(conf[i].value);
3172 for (j = 0; j < max_num_entities; j++)
3173 string[j] = (j < num_entities ? buffer[j] : '\0');
3175 else if (data_type == TYPE_ELEMENT_LIST)
3177 int *element_array = (int *)(conf[i].value);
3180 for (j = 0; j < num_entities; j++)
3182 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3184 else if (data_type == TYPE_CONTENT_LIST)
3186 struct Content *content= (struct Content *)(conf[i].value);
3189 for (c = 0; c < num_entities; c++)
3190 for (y = 0; y < 3; y++)
3191 for (x = 0; x < 3; x++)
3192 content[c].e[x][y] =
3193 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3196 element_found = FALSE;
3202 checked_free(buffer);
3204 micro_chunk_size += 2 + num_bytes;
3206 else // constant size configuration data (1, 2 or 4 bytes)
3208 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3209 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3210 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3212 for (i = 0; conf[i].data_type != -1; i++)
3214 if (conf[i].element == element &&
3215 conf[i].conf_type == conf_type)
3217 int data_type = conf[i].data_type;
3219 if (data_type == TYPE_ELEMENT)
3220 value = getMappedElement(value);
3222 if (data_type == TYPE_BOOLEAN)
3223 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3225 *(int *) (conf[i].value) = value;
3227 element_found = TRUE;
3233 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3238 char *error_conf_chunk_bytes =
3239 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3240 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3241 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3242 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3243 int error_element = real_element;
3245 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3246 error_conf_chunk_bytes, error_conf_chunk_token,
3247 error_element, EL_NAME(error_element));
3250 return micro_chunk_size;
3253 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3255 int real_chunk_size = 0;
3257 li = *level; // copy level data into temporary buffer
3259 while (!checkEndOfFile(file))
3261 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3263 if (real_chunk_size >= chunk_size)
3267 *level = li; // copy temporary buffer back to level data
3269 return real_chunk_size;
3272 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3274 int real_chunk_size = 0;
3276 li = *level; // copy level data into temporary buffer
3278 while (!checkEndOfFile(file))
3280 int element = getMappedElement(getFile16BitBE(file));
3282 real_chunk_size += 2;
3283 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3285 if (real_chunk_size >= chunk_size)
3289 *level = li; // copy temporary buffer back to level data
3291 return real_chunk_size;
3294 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3296 int real_chunk_size = 0;
3298 li = *level; // copy level data into temporary buffer
3300 while (!checkEndOfFile(file))
3302 int element = getMappedElement(getFile16BitBE(file));
3304 real_chunk_size += 2;
3305 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3307 if (real_chunk_size >= chunk_size)
3311 *level = li; // copy temporary buffer back to level data
3313 return real_chunk_size;
3316 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3318 int element = getMappedElement(getFile16BitBE(file));
3319 int envelope_nr = element - EL_ENVELOPE_1;
3320 int real_chunk_size = 2;
3322 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3324 while (!checkEndOfFile(file))
3326 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3329 if (real_chunk_size >= chunk_size)
3333 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3335 return real_chunk_size;
3338 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3340 int element = getMappedElement(getFile16BitBE(file));
3341 int real_chunk_size = 2;
3342 struct ElementInfo *ei = &element_info[element];
3345 xx_ei = *ei; // copy element data into temporary buffer
3347 xx_ei.num_change_pages = -1;
3349 while (!checkEndOfFile(file))
3351 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3353 if (xx_ei.num_change_pages != -1)
3356 if (real_chunk_size >= chunk_size)
3362 if (ei->num_change_pages == -1)
3364 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3367 ei->num_change_pages = 1;
3369 setElementChangePages(ei, 1);
3370 setElementChangeInfoToDefaults(ei->change);
3372 return real_chunk_size;
3375 // initialize number of change pages stored for this custom element
3376 setElementChangePages(ei, ei->num_change_pages);
3377 for (i = 0; i < ei->num_change_pages; i++)
3378 setElementChangeInfoToDefaults(&ei->change_page[i]);
3380 // start with reading properties for the first change page
3381 xx_current_change_page = 0;
3383 while (!checkEndOfFile(file))
3385 // level file might contain invalid change page number
3386 if (xx_current_change_page >= ei->num_change_pages)
3389 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3391 xx_change = *change; // copy change data into temporary buffer
3393 resetEventBits(); // reset bits; change page might have changed
3395 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3398 *change = xx_change;
3400 setEventFlagsFromEventBits(change);
3402 if (real_chunk_size >= chunk_size)
3406 level->file_has_custom_elements = TRUE;
3408 return real_chunk_size;
3411 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3413 int element = getMappedElement(getFile16BitBE(file));
3414 int real_chunk_size = 2;
3415 struct ElementInfo *ei = &element_info[element];
3416 struct ElementGroupInfo *group = ei->group;
3421 xx_ei = *ei; // copy element data into temporary buffer
3422 xx_group = *group; // copy group data into temporary buffer
3424 while (!checkEndOfFile(file))
3426 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3429 if (real_chunk_size >= chunk_size)
3436 level->file_has_custom_elements = TRUE;
3438 return real_chunk_size;
3441 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3443 int element = getMappedElement(getFile16BitBE(file));
3444 int real_chunk_size = 2;
3445 struct ElementInfo *ei = &element_info[element];
3447 xx_ei = *ei; // copy element data into temporary buffer
3449 while (!checkEndOfFile(file))
3451 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3454 if (real_chunk_size >= chunk_size)
3460 level->file_has_custom_elements = TRUE;
3462 return real_chunk_size;
3465 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3466 struct LevelFileInfo *level_file_info,
3467 boolean level_info_only)
3469 char *filename = level_file_info->filename;
3470 char cookie[MAX_LINE_LEN];
3471 char chunk_name[CHUNK_ID_LEN + 1];
3475 if (!(file = openFile(filename, MODE_READ)))
3477 level->no_valid_file = TRUE;
3478 level->no_level_file = TRUE;
3480 if (level_info_only)
3483 Warn("cannot read level '%s' -- using empty level", filename);
3485 if (!setup.editor.use_template_for_new_levels)
3488 // if level file not found, try to initialize level data from template
3489 filename = getGlobalLevelTemplateFilename();
3491 if (!(file = openFile(filename, MODE_READ)))
3494 // default: for empty levels, use level template for custom elements
3495 level->use_custom_template = TRUE;
3497 level->no_valid_file = FALSE;
3500 getFileChunkBE(file, chunk_name, NULL);
3501 if (strEqual(chunk_name, "RND1"))
3503 getFile32BitBE(file); // not used
3505 getFileChunkBE(file, chunk_name, NULL);
3506 if (!strEqual(chunk_name, "CAVE"))
3508 level->no_valid_file = TRUE;
3510 Warn("unknown format of level file '%s'", filename);
3517 else // check for pre-2.0 file format with cookie string
3519 strcpy(cookie, chunk_name);
3520 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3522 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3523 cookie[strlen(cookie) - 1] = '\0';
3525 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3527 level->no_valid_file = TRUE;
3529 Warn("unknown format of level file '%s'", filename);
3536 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3538 level->no_valid_file = TRUE;
3540 Warn("unsupported version of level file '%s'", filename);
3547 // pre-2.0 level files have no game version, so use file version here
3548 level->game_version = level->file_version;
3551 if (level->file_version < FILE_VERSION_1_2)
3553 // level files from versions before 1.2.0 without chunk structure
3554 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3555 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3563 int (*loader)(File *, int, struct LevelInfo *);
3567 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3568 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3569 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3570 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3571 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3572 { "INFO", -1, LoadLevel_INFO },
3573 { "BODY", -1, LoadLevel_BODY },
3574 { "CONT", -1, LoadLevel_CONT },
3575 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3576 { "CNT3", -1, LoadLevel_CNT3 },
3577 { "CUS1", -1, LoadLevel_CUS1 },
3578 { "CUS2", -1, LoadLevel_CUS2 },
3579 { "CUS3", -1, LoadLevel_CUS3 },
3580 { "CUS4", -1, LoadLevel_CUS4 },
3581 { "GRP1", -1, LoadLevel_GRP1 },
3582 { "CONF", -1, LoadLevel_CONF },
3583 { "ELEM", -1, LoadLevel_ELEM },
3584 { "NOTE", -1, LoadLevel_NOTE },
3585 { "CUSX", -1, LoadLevel_CUSX },
3586 { "GRPX", -1, LoadLevel_GRPX },
3587 { "EMPX", -1, LoadLevel_EMPX },
3592 while (getFileChunkBE(file, chunk_name, &chunk_size))
3596 while (chunk_info[i].name != NULL &&
3597 !strEqual(chunk_name, chunk_info[i].name))
3600 if (chunk_info[i].name == NULL)
3602 Warn("unknown chunk '%s' in level file '%s'",
3603 chunk_name, filename);
3605 ReadUnusedBytesFromFile(file, chunk_size);
3607 else if (chunk_info[i].size != -1 &&
3608 chunk_info[i].size != chunk_size)
3610 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3611 chunk_size, chunk_name, filename);
3613 ReadUnusedBytesFromFile(file, chunk_size);
3617 // call function to load this level chunk
3618 int chunk_size_expected =
3619 (chunk_info[i].loader)(file, chunk_size, level);
3621 if (chunk_size_expected < 0)
3623 Warn("error reading chunk '%s' in level file '%s'",
3624 chunk_name, filename);
3629 // the size of some chunks cannot be checked before reading other
3630 // chunks first (like "HEAD" and "BODY") that contain some header
3631 // information, so check them here
3632 if (chunk_size_expected != chunk_size)
3634 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3635 chunk_size, chunk_name, filename);
3647 // ----------------------------------------------------------------------------
3648 // functions for loading EM level
3649 // ----------------------------------------------------------------------------
3651 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3653 static int ball_xy[8][2] =
3664 struct LevelInfo_EM *level_em = level->native_em_level;
3665 struct CAVE *cav = level_em->cav;
3668 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3669 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3671 cav->time_seconds = level->time;
3672 cav->gems_needed = level->gems_needed;
3674 cav->emerald_score = level->score[SC_EMERALD];
3675 cav->diamond_score = level->score[SC_DIAMOND];
3676 cav->alien_score = level->score[SC_ROBOT];
3677 cav->tank_score = level->score[SC_SPACESHIP];
3678 cav->bug_score = level->score[SC_BUG];
3679 cav->eater_score = level->score[SC_YAMYAM];
3680 cav->nut_score = level->score[SC_NUT];
3681 cav->dynamite_score = level->score[SC_DYNAMITE];
3682 cav->key_score = level->score[SC_KEY];
3683 cav->exit_score = level->score[SC_TIME_BONUS];
3685 cav->num_eater_arrays = level->num_yamyam_contents;
3687 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3688 for (y = 0; y < 3; y++)
3689 for (x = 0; x < 3; x++)
3690 cav->eater_array[i][y * 3 + x] =
3691 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3693 cav->amoeba_time = level->amoeba_speed;
3694 cav->wonderwall_time = level->time_magic_wall;
3695 cav->wheel_time = level->time_wheel;
3697 cav->android_move_time = level->android_move_time;
3698 cav->android_clone_time = level->android_clone_time;
3699 cav->ball_random = level->ball_random;
3700 cav->ball_active = level->ball_active_initial;
3701 cav->ball_time = level->ball_time;
3702 cav->num_ball_arrays = level->num_ball_contents;
3704 cav->lenses_score = level->lenses_score;
3705 cav->magnify_score = level->magnify_score;
3706 cav->slurp_score = level->slurp_score;
3708 cav->lenses_time = level->lenses_time;
3709 cav->magnify_time = level->magnify_time;
3711 cav->wind_direction =
3712 map_direction_RND_to_EM(level->wind_direction_initial);
3714 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3715 for (j = 0; j < 8; j++)
3716 cav->ball_array[i][j] =
3717 map_element_RND_to_EM_cave(level->ball_content[i].
3718 e[ball_xy[j][0]][ball_xy[j][1]]);
3720 map_android_clone_elements_RND_to_EM(level);
3722 // first fill the complete playfield with the empty space element
3723 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3724 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3725 cav->cave[x][y] = Cblank;
3727 // then copy the real level contents from level file into the playfield
3728 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3730 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3732 if (level->field[x][y] == EL_AMOEBA_DEAD)
3733 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3735 cav->cave[x][y] = new_element;
3738 for (i = 0; i < MAX_PLAYERS; i++)
3740 cav->player_x[i] = -1;
3741 cav->player_y[i] = -1;
3744 // initialize player positions and delete players from the playfield
3745 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3747 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3749 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3751 cav->player_x[player_nr] = x;
3752 cav->player_y[player_nr] = y;
3754 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3759 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3761 static int ball_xy[8][2] =
3772 struct LevelInfo_EM *level_em = level->native_em_level;
3773 struct CAVE *cav = level_em->cav;
3776 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3777 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3779 level->time = cav->time_seconds;
3780 level->gems_needed = cav->gems_needed;
3782 sprintf(level->name, "Level %d", level->file_info.nr);
3784 level->score[SC_EMERALD] = cav->emerald_score;
3785 level->score[SC_DIAMOND] = cav->diamond_score;
3786 level->score[SC_ROBOT] = cav->alien_score;
3787 level->score[SC_SPACESHIP] = cav->tank_score;
3788 level->score[SC_BUG] = cav->bug_score;
3789 level->score[SC_YAMYAM] = cav->eater_score;
3790 level->score[SC_NUT] = cav->nut_score;
3791 level->score[SC_DYNAMITE] = cav->dynamite_score;
3792 level->score[SC_KEY] = cav->key_score;
3793 level->score[SC_TIME_BONUS] = cav->exit_score;
3795 level->num_yamyam_contents = cav->num_eater_arrays;
3797 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3798 for (y = 0; y < 3; y++)
3799 for (x = 0; x < 3; x++)
3800 level->yamyam_content[i].e[x][y] =
3801 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3803 level->amoeba_speed = cav->amoeba_time;
3804 level->time_magic_wall = cav->wonderwall_time;
3805 level->time_wheel = cav->wheel_time;
3807 level->android_move_time = cav->android_move_time;
3808 level->android_clone_time = cav->android_clone_time;
3809 level->ball_random = cav->ball_random;
3810 level->ball_active_initial = cav->ball_active;
3811 level->ball_time = cav->ball_time;
3812 level->num_ball_contents = cav->num_ball_arrays;
3814 level->lenses_score = cav->lenses_score;
3815 level->magnify_score = cav->magnify_score;
3816 level->slurp_score = cav->slurp_score;
3818 level->lenses_time = cav->lenses_time;
3819 level->magnify_time = cav->magnify_time;
3821 level->wind_direction_initial =
3822 map_direction_EM_to_RND(cav->wind_direction);
3824 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3825 for (j = 0; j < 8; j++)
3826 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3827 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3829 map_android_clone_elements_EM_to_RND(level);
3831 // convert the playfield (some elements need special treatment)
3832 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3834 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3836 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3837 new_element = EL_AMOEBA_DEAD;
3839 level->field[x][y] = new_element;
3842 for (i = 0; i < MAX_PLAYERS; i++)
3844 // in case of all players set to the same field, use the first player
3845 int nr = MAX_PLAYERS - i - 1;
3846 int jx = cav->player_x[nr];
3847 int jy = cav->player_y[nr];
3849 if (jx != -1 && jy != -1)
3850 level->field[jx][jy] = EL_PLAYER_1 + nr;
3853 // time score is counted for each 10 seconds left in Emerald Mine levels
3854 level->time_score_base = 10;
3858 // ----------------------------------------------------------------------------
3859 // functions for loading SP level
3860 // ----------------------------------------------------------------------------
3862 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3864 struct LevelInfo_SP *level_sp = level->native_sp_level;
3865 LevelInfoType *header = &level_sp->header;
3868 level_sp->width = level->fieldx;
3869 level_sp->height = level->fieldy;
3871 for (x = 0; x < level->fieldx; x++)
3872 for (y = 0; y < level->fieldy; y++)
3873 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3875 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3877 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3878 header->LevelTitle[i] = level->name[i];
3879 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3881 header->InfotronsNeeded = level->gems_needed;
3883 header->SpecialPortCount = 0;
3885 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3887 boolean gravity_port_found = FALSE;
3888 boolean gravity_port_valid = FALSE;
3889 int gravity_port_flag;
3890 int gravity_port_base_element;
3891 int element = level->field[x][y];
3893 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3894 element <= EL_SP_GRAVITY_ON_PORT_UP)
3896 gravity_port_found = TRUE;
3897 gravity_port_valid = TRUE;
3898 gravity_port_flag = 1;
3899 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3901 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3902 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3904 gravity_port_found = TRUE;
3905 gravity_port_valid = TRUE;
3906 gravity_port_flag = 0;
3907 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3909 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3910 element <= EL_SP_GRAVITY_PORT_UP)
3912 // change R'n'D style gravity inverting special port to normal port
3913 // (there are no gravity inverting ports in native Supaplex engine)
3915 gravity_port_found = TRUE;
3916 gravity_port_valid = FALSE;
3917 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3920 if (gravity_port_found)
3922 if (gravity_port_valid &&
3923 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3925 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3927 port->PortLocation = (y * level->fieldx + x) * 2;
3928 port->Gravity = gravity_port_flag;
3930 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3932 header->SpecialPortCount++;
3936 // change special gravity port to normal port
3938 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3941 level_sp->playfield[x][y] = element - EL_SP_START;
3946 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3948 struct LevelInfo_SP *level_sp = level->native_sp_level;
3949 LevelInfoType *header = &level_sp->header;
3950 boolean num_invalid_elements = 0;
3953 level->fieldx = level_sp->width;
3954 level->fieldy = level_sp->height;
3956 for (x = 0; x < level->fieldx; x++)
3958 for (y = 0; y < level->fieldy; y++)
3960 int element_old = level_sp->playfield[x][y];
3961 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3963 if (element_new == EL_UNKNOWN)
3965 num_invalid_elements++;
3967 Debug("level:native:SP", "invalid element %d at position %d, %d",
3971 level->field[x][y] = element_new;
3975 if (num_invalid_elements > 0)
3976 Warn("found %d invalid elements%s", num_invalid_elements,
3977 (!options.debug ? " (use '--debug' for more details)" : ""));
3979 for (i = 0; i < MAX_PLAYERS; i++)
3980 level->initial_player_gravity[i] =
3981 (header->InitialGravity == 1 ? TRUE : FALSE);
3983 // skip leading spaces
3984 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3985 if (header->LevelTitle[i] != ' ')
3989 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3990 level->name[j] = header->LevelTitle[i];
3991 level->name[j] = '\0';
3993 // cut trailing spaces
3995 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3996 level->name[j - 1] = '\0';
3998 level->gems_needed = header->InfotronsNeeded;
4000 for (i = 0; i < header->SpecialPortCount; i++)
4002 SpecialPortType *port = &header->SpecialPort[i];
4003 int port_location = port->PortLocation;
4004 int gravity = port->Gravity;
4005 int port_x, port_y, port_element;
4007 port_x = (port_location / 2) % level->fieldx;
4008 port_y = (port_location / 2) / level->fieldx;
4010 if (port_x < 0 || port_x >= level->fieldx ||
4011 port_y < 0 || port_y >= level->fieldy)
4013 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4018 port_element = level->field[port_x][port_y];
4020 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4021 port_element > EL_SP_GRAVITY_PORT_UP)
4023 Warn("no special port at position (%d, %d)", port_x, port_y);
4028 // change previous (wrong) gravity inverting special port to either
4029 // gravity enabling special port or gravity disabling special port
4030 level->field[port_x][port_y] +=
4031 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4032 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4035 // change special gravity ports without database entries to normal ports
4036 for (x = 0; x < level->fieldx; x++)
4037 for (y = 0; y < level->fieldy; y++)
4038 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4039 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4040 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4042 level->time = 0; // no time limit
4043 level->amoeba_speed = 0;
4044 level->time_magic_wall = 0;
4045 level->time_wheel = 0;
4046 level->amoeba_content = EL_EMPTY;
4048 // original Supaplex does not use score values -- rate by playing time
4049 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4050 level->score[i] = 0;
4052 level->rate_time_over_score = TRUE;
4054 // there are no yamyams in supaplex levels
4055 for (i = 0; i < level->num_yamyam_contents; i++)
4056 for (x = 0; x < 3; x++)
4057 for (y = 0; y < 3; y++)
4058 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4061 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4063 struct LevelInfo_SP *level_sp = level->native_sp_level;
4064 struct DemoInfo_SP *demo = &level_sp->demo;
4067 // always start with reliable default values
4068 demo->is_available = FALSE;
4071 if (TAPE_IS_EMPTY(tape))
4074 demo->level_nr = tape.level_nr; // (currently not used)
4076 level_sp->header.DemoRandomSeed = tape.random_seed;
4080 for (i = 0; i < tape.length; i++)
4082 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4083 int demo_repeat = tape.pos[i].delay;
4084 int demo_entries = (demo_repeat + 15) / 16;
4086 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4088 Warn("tape truncated: size exceeds maximum SP demo size %d",
4094 for (j = 0; j < demo_repeat / 16; j++)
4095 demo->data[demo->length++] = 0xf0 | demo_action;
4097 if (demo_repeat % 16)
4098 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4101 demo->is_available = TRUE;
4104 static void setTapeInfoToDefaults(void);
4106 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4108 struct LevelInfo_SP *level_sp = level->native_sp_level;
4109 struct DemoInfo_SP *demo = &level_sp->demo;
4110 char *filename = level->file_info.filename;
4113 // always start with reliable default values
4114 setTapeInfoToDefaults();
4116 if (!demo->is_available)
4119 tape.level_nr = demo->level_nr; // (currently not used)
4120 tape.random_seed = level_sp->header.DemoRandomSeed;
4122 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4125 tape.pos[tape.counter].delay = 0;
4127 for (i = 0; i < demo->length; i++)
4129 int demo_action = demo->data[i] & 0x0f;
4130 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4131 int tape_action = map_key_SP_to_RND(demo_action);
4132 int tape_repeat = demo_repeat + 1;
4133 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4134 boolean success = 0;
4137 for (j = 0; j < tape_repeat; j++)
4138 success = TapeAddAction(action);
4142 Warn("SP demo truncated: size exceeds maximum tape size %d",
4149 TapeHaltRecording();
4153 // ----------------------------------------------------------------------------
4154 // functions for loading MM level
4155 // ----------------------------------------------------------------------------
4157 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4159 struct LevelInfo_MM *level_mm = level->native_mm_level;
4162 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4163 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4165 level_mm->time = level->time;
4166 level_mm->kettles_needed = level->gems_needed;
4167 level_mm->auto_count_kettles = level->auto_count_gems;
4169 level_mm->mm_laser_red = level->mm_laser_red;
4170 level_mm->mm_laser_green = level->mm_laser_green;
4171 level_mm->mm_laser_blue = level->mm_laser_blue;
4173 level_mm->df_laser_red = level->df_laser_red;
4174 level_mm->df_laser_green = level->df_laser_green;
4175 level_mm->df_laser_blue = level->df_laser_blue;
4177 strcpy(level_mm->name, level->name);
4178 strcpy(level_mm->author, level->author);
4180 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4181 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4182 level_mm->score[SC_KEY] = level->score[SC_KEY];
4183 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4184 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4186 level_mm->amoeba_speed = level->amoeba_speed;
4187 level_mm->time_fuse = level->mm_time_fuse;
4188 level_mm->time_bomb = level->mm_time_bomb;
4189 level_mm->time_ball = level->mm_time_ball;
4190 level_mm->time_block = level->mm_time_block;
4192 level_mm->num_ball_contents = level->num_mm_ball_contents;
4193 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4194 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4196 for (i = 0; i < level->num_mm_ball_contents; i++)
4197 level_mm->ball_content[i] =
4198 map_element_RND_to_MM(level->mm_ball_content[i]);
4200 for (x = 0; x < level->fieldx; x++)
4201 for (y = 0; y < level->fieldy; y++)
4203 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4206 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4208 struct LevelInfo_MM *level_mm = level->native_mm_level;
4211 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4212 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4214 level->time = level_mm->time;
4215 level->gems_needed = level_mm->kettles_needed;
4216 level->auto_count_gems = level_mm->auto_count_kettles;
4218 level->mm_laser_red = level_mm->mm_laser_red;
4219 level->mm_laser_green = level_mm->mm_laser_green;
4220 level->mm_laser_blue = level_mm->mm_laser_blue;
4222 level->df_laser_red = level_mm->df_laser_red;
4223 level->df_laser_green = level_mm->df_laser_green;
4224 level->df_laser_blue = level_mm->df_laser_blue;
4226 strcpy(level->name, level_mm->name);
4228 // only overwrite author from 'levelinfo.conf' if author defined in level
4229 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4230 strcpy(level->author, level_mm->author);
4232 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4233 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4234 level->score[SC_KEY] = level_mm->score[SC_KEY];
4235 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4236 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4238 level->amoeba_speed = level_mm->amoeba_speed;
4239 level->mm_time_fuse = level_mm->time_fuse;
4240 level->mm_time_bomb = level_mm->time_bomb;
4241 level->mm_time_ball = level_mm->time_ball;
4242 level->mm_time_block = level_mm->time_block;
4244 level->num_mm_ball_contents = level_mm->num_ball_contents;
4245 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4246 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4248 for (i = 0; i < level->num_mm_ball_contents; i++)
4249 level->mm_ball_content[i] =
4250 map_element_MM_to_RND(level_mm->ball_content[i]);
4252 for (x = 0; x < level->fieldx; x++)
4253 for (y = 0; y < level->fieldy; y++)
4254 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4258 // ----------------------------------------------------------------------------
4259 // functions for loading DC level
4260 // ----------------------------------------------------------------------------
4262 #define DC_LEVEL_HEADER_SIZE 344
4264 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4267 static int last_data_encoded;
4271 int diff_hi, diff_lo;
4272 int data_hi, data_lo;
4273 unsigned short data_decoded;
4277 last_data_encoded = 0;
4284 diff = data_encoded - last_data_encoded;
4285 diff_hi = diff & ~0xff;
4286 diff_lo = diff & 0xff;
4290 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4291 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4292 data_hi = data_hi & 0xff00;
4294 data_decoded = data_hi | data_lo;
4296 last_data_encoded = data_encoded;
4298 offset1 = (offset1 + 1) % 31;
4299 offset2 = offset2 & 0xff;
4301 return data_decoded;
4304 static int getMappedElement_DC(int element)
4312 // 0x0117 - 0x036e: (?)
4315 // 0x042d - 0x0684: (?)
4331 element = EL_CRYSTAL;
4334 case 0x0e77: // quicksand (boulder)
4335 element = EL_QUICKSAND_FAST_FULL;
4338 case 0x0e99: // slow quicksand (boulder)
4339 element = EL_QUICKSAND_FULL;
4343 element = EL_EM_EXIT_OPEN;
4347 element = EL_EM_EXIT_CLOSED;
4351 element = EL_EM_STEEL_EXIT_OPEN;
4355 element = EL_EM_STEEL_EXIT_CLOSED;
4358 case 0x0f4f: // dynamite (lit 1)
4359 element = EL_EM_DYNAMITE_ACTIVE;
4362 case 0x0f57: // dynamite (lit 2)
4363 element = EL_EM_DYNAMITE_ACTIVE;
4366 case 0x0f5f: // dynamite (lit 3)
4367 element = EL_EM_DYNAMITE_ACTIVE;
4370 case 0x0f67: // dynamite (lit 4)
4371 element = EL_EM_DYNAMITE_ACTIVE;
4378 element = EL_AMOEBA_WET;
4382 element = EL_AMOEBA_DROP;
4386 element = EL_DC_MAGIC_WALL;
4390 element = EL_SPACESHIP_UP;
4394 element = EL_SPACESHIP_DOWN;
4398 element = EL_SPACESHIP_LEFT;
4402 element = EL_SPACESHIP_RIGHT;
4406 element = EL_BUG_UP;
4410 element = EL_BUG_DOWN;
4414 element = EL_BUG_LEFT;
4418 element = EL_BUG_RIGHT;
4422 element = EL_MOLE_UP;
4426 element = EL_MOLE_DOWN;
4430 element = EL_MOLE_LEFT;
4434 element = EL_MOLE_RIGHT;
4442 element = EL_YAMYAM_UP;
4446 element = EL_SWITCHGATE_OPEN;
4450 element = EL_SWITCHGATE_CLOSED;
4454 element = EL_DC_SWITCHGATE_SWITCH_UP;
4458 element = EL_TIMEGATE_CLOSED;
4461 case 0x144c: // conveyor belt switch (green)
4462 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4465 case 0x144f: // conveyor belt switch (red)
4466 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4469 case 0x1452: // conveyor belt switch (blue)
4470 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4474 element = EL_CONVEYOR_BELT_3_MIDDLE;
4478 element = EL_CONVEYOR_BELT_3_LEFT;
4482 element = EL_CONVEYOR_BELT_3_RIGHT;
4486 element = EL_CONVEYOR_BELT_1_MIDDLE;
4490 element = EL_CONVEYOR_BELT_1_LEFT;
4494 element = EL_CONVEYOR_BELT_1_RIGHT;
4498 element = EL_CONVEYOR_BELT_4_MIDDLE;
4502 element = EL_CONVEYOR_BELT_4_LEFT;
4506 element = EL_CONVEYOR_BELT_4_RIGHT;
4510 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4514 element = EL_EXPANDABLE_WALL_VERTICAL;
4518 element = EL_EXPANDABLE_WALL_ANY;
4521 case 0x14ce: // growing steel wall (left/right)
4522 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4525 case 0x14df: // growing steel wall (up/down)
4526 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4529 case 0x14e8: // growing steel wall (up/down/left/right)
4530 element = EL_EXPANDABLE_STEELWALL_ANY;
4534 element = EL_SHIELD_DEADLY;
4538 element = EL_EXTRA_TIME;
4546 element = EL_EMPTY_SPACE;
4549 case 0x1578: // quicksand (empty)
4550 element = EL_QUICKSAND_FAST_EMPTY;
4553 case 0x1579: // slow quicksand (empty)
4554 element = EL_QUICKSAND_EMPTY;
4564 element = EL_EM_DYNAMITE;
4567 case 0x15a1: // key (red)
4568 element = EL_EM_KEY_1;
4571 case 0x15a2: // key (yellow)
4572 element = EL_EM_KEY_2;
4575 case 0x15a3: // key (blue)
4576 element = EL_EM_KEY_4;
4579 case 0x15a4: // key (green)
4580 element = EL_EM_KEY_3;
4583 case 0x15a5: // key (white)
4584 element = EL_DC_KEY_WHITE;
4588 element = EL_WALL_SLIPPERY;
4595 case 0x15a8: // wall (not round)
4599 case 0x15a9: // (blue)
4600 element = EL_CHAR_A;
4603 case 0x15aa: // (blue)
4604 element = EL_CHAR_B;
4607 case 0x15ab: // (blue)
4608 element = EL_CHAR_C;
4611 case 0x15ac: // (blue)
4612 element = EL_CHAR_D;
4615 case 0x15ad: // (blue)
4616 element = EL_CHAR_E;
4619 case 0x15ae: // (blue)
4620 element = EL_CHAR_F;
4623 case 0x15af: // (blue)
4624 element = EL_CHAR_G;
4627 case 0x15b0: // (blue)
4628 element = EL_CHAR_H;
4631 case 0x15b1: // (blue)
4632 element = EL_CHAR_I;
4635 case 0x15b2: // (blue)
4636 element = EL_CHAR_J;
4639 case 0x15b3: // (blue)
4640 element = EL_CHAR_K;
4643 case 0x15b4: // (blue)
4644 element = EL_CHAR_L;
4647 case 0x15b5: // (blue)
4648 element = EL_CHAR_M;
4651 case 0x15b6: // (blue)
4652 element = EL_CHAR_N;
4655 case 0x15b7: // (blue)
4656 element = EL_CHAR_O;
4659 case 0x15b8: // (blue)
4660 element = EL_CHAR_P;
4663 case 0x15b9: // (blue)
4664 element = EL_CHAR_Q;
4667 case 0x15ba: // (blue)
4668 element = EL_CHAR_R;
4671 case 0x15bb: // (blue)
4672 element = EL_CHAR_S;
4675 case 0x15bc: // (blue)
4676 element = EL_CHAR_T;
4679 case 0x15bd: // (blue)
4680 element = EL_CHAR_U;
4683 case 0x15be: // (blue)
4684 element = EL_CHAR_V;
4687 case 0x15bf: // (blue)
4688 element = EL_CHAR_W;
4691 case 0x15c0: // (blue)
4692 element = EL_CHAR_X;
4695 case 0x15c1: // (blue)
4696 element = EL_CHAR_Y;
4699 case 0x15c2: // (blue)
4700 element = EL_CHAR_Z;
4703 case 0x15c3: // (blue)
4704 element = EL_CHAR_AUMLAUT;
4707 case 0x15c4: // (blue)
4708 element = EL_CHAR_OUMLAUT;
4711 case 0x15c5: // (blue)
4712 element = EL_CHAR_UUMLAUT;
4715 case 0x15c6: // (blue)
4716 element = EL_CHAR_0;
4719 case 0x15c7: // (blue)
4720 element = EL_CHAR_1;
4723 case 0x15c8: // (blue)
4724 element = EL_CHAR_2;
4727 case 0x15c9: // (blue)
4728 element = EL_CHAR_3;
4731 case 0x15ca: // (blue)
4732 element = EL_CHAR_4;
4735 case 0x15cb: // (blue)
4736 element = EL_CHAR_5;
4739 case 0x15cc: // (blue)
4740 element = EL_CHAR_6;
4743 case 0x15cd: // (blue)
4744 element = EL_CHAR_7;
4747 case 0x15ce: // (blue)
4748 element = EL_CHAR_8;
4751 case 0x15cf: // (blue)
4752 element = EL_CHAR_9;
4755 case 0x15d0: // (blue)
4756 element = EL_CHAR_PERIOD;
4759 case 0x15d1: // (blue)
4760 element = EL_CHAR_EXCLAM;
4763 case 0x15d2: // (blue)
4764 element = EL_CHAR_COLON;
4767 case 0x15d3: // (blue)
4768 element = EL_CHAR_LESS;
4771 case 0x15d4: // (blue)
4772 element = EL_CHAR_GREATER;
4775 case 0x15d5: // (blue)
4776 element = EL_CHAR_QUESTION;
4779 case 0x15d6: // (blue)
4780 element = EL_CHAR_COPYRIGHT;
4783 case 0x15d7: // (blue)
4784 element = EL_CHAR_UP;
4787 case 0x15d8: // (blue)
4788 element = EL_CHAR_DOWN;
4791 case 0x15d9: // (blue)
4792 element = EL_CHAR_BUTTON;
4795 case 0x15da: // (blue)
4796 element = EL_CHAR_PLUS;
4799 case 0x15db: // (blue)
4800 element = EL_CHAR_MINUS;
4803 case 0x15dc: // (blue)
4804 element = EL_CHAR_APOSTROPHE;
4807 case 0x15dd: // (blue)
4808 element = EL_CHAR_PARENLEFT;
4811 case 0x15de: // (blue)
4812 element = EL_CHAR_PARENRIGHT;
4815 case 0x15df: // (green)
4816 element = EL_CHAR_A;
4819 case 0x15e0: // (green)
4820 element = EL_CHAR_B;
4823 case 0x15e1: // (green)
4824 element = EL_CHAR_C;
4827 case 0x15e2: // (green)
4828 element = EL_CHAR_D;
4831 case 0x15e3: // (green)
4832 element = EL_CHAR_E;
4835 case 0x15e4: // (green)
4836 element = EL_CHAR_F;
4839 case 0x15e5: // (green)
4840 element = EL_CHAR_G;
4843 case 0x15e6: // (green)
4844 element = EL_CHAR_H;
4847 case 0x15e7: // (green)
4848 element = EL_CHAR_I;
4851 case 0x15e8: // (green)
4852 element = EL_CHAR_J;
4855 case 0x15e9: // (green)
4856 element = EL_CHAR_K;
4859 case 0x15ea: // (green)
4860 element = EL_CHAR_L;
4863 case 0x15eb: // (green)
4864 element = EL_CHAR_M;
4867 case 0x15ec: // (green)
4868 element = EL_CHAR_N;
4871 case 0x15ed: // (green)
4872 element = EL_CHAR_O;
4875 case 0x15ee: // (green)
4876 element = EL_CHAR_P;
4879 case 0x15ef: // (green)
4880 element = EL_CHAR_Q;
4883 case 0x15f0: // (green)
4884 element = EL_CHAR_R;
4887 case 0x15f1: // (green)
4888 element = EL_CHAR_S;
4891 case 0x15f2: // (green)
4892 element = EL_CHAR_T;
4895 case 0x15f3: // (green)
4896 element = EL_CHAR_U;
4899 case 0x15f4: // (green)
4900 element = EL_CHAR_V;
4903 case 0x15f5: // (green)
4904 element = EL_CHAR_W;
4907 case 0x15f6: // (green)
4908 element = EL_CHAR_X;
4911 case 0x15f7: // (green)
4912 element = EL_CHAR_Y;
4915 case 0x15f8: // (green)
4916 element = EL_CHAR_Z;
4919 case 0x15f9: // (green)
4920 element = EL_CHAR_AUMLAUT;
4923 case 0x15fa: // (green)
4924 element = EL_CHAR_OUMLAUT;
4927 case 0x15fb: // (green)
4928 element = EL_CHAR_UUMLAUT;
4931 case 0x15fc: // (green)
4932 element = EL_CHAR_0;
4935 case 0x15fd: // (green)
4936 element = EL_CHAR_1;
4939 case 0x15fe: // (green)
4940 element = EL_CHAR_2;
4943 case 0x15ff: // (green)
4944 element = EL_CHAR_3;
4947 case 0x1600: // (green)
4948 element = EL_CHAR_4;
4951 case 0x1601: // (green)
4952 element = EL_CHAR_5;
4955 case 0x1602: // (green)
4956 element = EL_CHAR_6;
4959 case 0x1603: // (green)
4960 element = EL_CHAR_7;
4963 case 0x1604: // (green)
4964 element = EL_CHAR_8;
4967 case 0x1605: // (green)
4968 element = EL_CHAR_9;
4971 case 0x1606: // (green)
4972 element = EL_CHAR_PERIOD;
4975 case 0x1607: // (green)
4976 element = EL_CHAR_EXCLAM;
4979 case 0x1608: // (green)
4980 element = EL_CHAR_COLON;
4983 case 0x1609: // (green)
4984 element = EL_CHAR_LESS;
4987 case 0x160a: // (green)
4988 element = EL_CHAR_GREATER;
4991 case 0x160b: // (green)
4992 element = EL_CHAR_QUESTION;
4995 case 0x160c: // (green)
4996 element = EL_CHAR_COPYRIGHT;
4999 case 0x160d: // (green)
5000 element = EL_CHAR_UP;
5003 case 0x160e: // (green)
5004 element = EL_CHAR_DOWN;
5007 case 0x160f: // (green)
5008 element = EL_CHAR_BUTTON;
5011 case 0x1610: // (green)
5012 element = EL_CHAR_PLUS;
5015 case 0x1611: // (green)
5016 element = EL_CHAR_MINUS;
5019 case 0x1612: // (green)
5020 element = EL_CHAR_APOSTROPHE;
5023 case 0x1613: // (green)
5024 element = EL_CHAR_PARENLEFT;
5027 case 0x1614: // (green)
5028 element = EL_CHAR_PARENRIGHT;
5031 case 0x1615: // (blue steel)
5032 element = EL_STEEL_CHAR_A;
5035 case 0x1616: // (blue steel)
5036 element = EL_STEEL_CHAR_B;
5039 case 0x1617: // (blue steel)
5040 element = EL_STEEL_CHAR_C;
5043 case 0x1618: // (blue steel)
5044 element = EL_STEEL_CHAR_D;
5047 case 0x1619: // (blue steel)
5048 element = EL_STEEL_CHAR_E;
5051 case 0x161a: // (blue steel)
5052 element = EL_STEEL_CHAR_F;
5055 case 0x161b: // (blue steel)
5056 element = EL_STEEL_CHAR_G;
5059 case 0x161c: // (blue steel)
5060 element = EL_STEEL_CHAR_H;
5063 case 0x161d: // (blue steel)
5064 element = EL_STEEL_CHAR_I;
5067 case 0x161e: // (blue steel)
5068 element = EL_STEEL_CHAR_J;
5071 case 0x161f: // (blue steel)
5072 element = EL_STEEL_CHAR_K;
5075 case 0x1620: // (blue steel)
5076 element = EL_STEEL_CHAR_L;
5079 case 0x1621: // (blue steel)
5080 element = EL_STEEL_CHAR_M;
5083 case 0x1622: // (blue steel)
5084 element = EL_STEEL_CHAR_N;
5087 case 0x1623: // (blue steel)
5088 element = EL_STEEL_CHAR_O;
5091 case 0x1624: // (blue steel)
5092 element = EL_STEEL_CHAR_P;
5095 case 0x1625: // (blue steel)
5096 element = EL_STEEL_CHAR_Q;
5099 case 0x1626: // (blue steel)
5100 element = EL_STEEL_CHAR_R;
5103 case 0x1627: // (blue steel)
5104 element = EL_STEEL_CHAR_S;
5107 case 0x1628: // (blue steel)
5108 element = EL_STEEL_CHAR_T;
5111 case 0x1629: // (blue steel)
5112 element = EL_STEEL_CHAR_U;
5115 case 0x162a: // (blue steel)
5116 element = EL_STEEL_CHAR_V;
5119 case 0x162b: // (blue steel)
5120 element = EL_STEEL_CHAR_W;
5123 case 0x162c: // (blue steel)
5124 element = EL_STEEL_CHAR_X;
5127 case 0x162d: // (blue steel)
5128 element = EL_STEEL_CHAR_Y;
5131 case 0x162e: // (blue steel)
5132 element = EL_STEEL_CHAR_Z;
5135 case 0x162f: // (blue steel)
5136 element = EL_STEEL_CHAR_AUMLAUT;
5139 case 0x1630: // (blue steel)
5140 element = EL_STEEL_CHAR_OUMLAUT;
5143 case 0x1631: // (blue steel)
5144 element = EL_STEEL_CHAR_UUMLAUT;
5147 case 0x1632: // (blue steel)
5148 element = EL_STEEL_CHAR_0;
5151 case 0x1633: // (blue steel)
5152 element = EL_STEEL_CHAR_1;
5155 case 0x1634: // (blue steel)
5156 element = EL_STEEL_CHAR_2;
5159 case 0x1635: // (blue steel)
5160 element = EL_STEEL_CHAR_3;
5163 case 0x1636: // (blue steel)
5164 element = EL_STEEL_CHAR_4;
5167 case 0x1637: // (blue steel)
5168 element = EL_STEEL_CHAR_5;
5171 case 0x1638: // (blue steel)
5172 element = EL_STEEL_CHAR_6;
5175 case 0x1639: // (blue steel)
5176 element = EL_STEEL_CHAR_7;
5179 case 0x163a: // (blue steel)
5180 element = EL_STEEL_CHAR_8;
5183 case 0x163b: // (blue steel)
5184 element = EL_STEEL_CHAR_9;
5187 case 0x163c: // (blue steel)
5188 element = EL_STEEL_CHAR_PERIOD;
5191 case 0x163d: // (blue steel)
5192 element = EL_STEEL_CHAR_EXCLAM;
5195 case 0x163e: // (blue steel)
5196 element = EL_STEEL_CHAR_COLON;
5199 case 0x163f: // (blue steel)
5200 element = EL_STEEL_CHAR_LESS;
5203 case 0x1640: // (blue steel)
5204 element = EL_STEEL_CHAR_GREATER;
5207 case 0x1641: // (blue steel)
5208 element = EL_STEEL_CHAR_QUESTION;
5211 case 0x1642: // (blue steel)
5212 element = EL_STEEL_CHAR_COPYRIGHT;
5215 case 0x1643: // (blue steel)
5216 element = EL_STEEL_CHAR_UP;
5219 case 0x1644: // (blue steel)
5220 element = EL_STEEL_CHAR_DOWN;
5223 case 0x1645: // (blue steel)
5224 element = EL_STEEL_CHAR_BUTTON;
5227 case 0x1646: // (blue steel)
5228 element = EL_STEEL_CHAR_PLUS;
5231 case 0x1647: // (blue steel)
5232 element = EL_STEEL_CHAR_MINUS;
5235 case 0x1648: // (blue steel)
5236 element = EL_STEEL_CHAR_APOSTROPHE;
5239 case 0x1649: // (blue steel)
5240 element = EL_STEEL_CHAR_PARENLEFT;
5243 case 0x164a: // (blue steel)
5244 element = EL_STEEL_CHAR_PARENRIGHT;
5247 case 0x164b: // (green steel)
5248 element = EL_STEEL_CHAR_A;
5251 case 0x164c: // (green steel)
5252 element = EL_STEEL_CHAR_B;
5255 case 0x164d: // (green steel)
5256 element = EL_STEEL_CHAR_C;
5259 case 0x164e: // (green steel)
5260 element = EL_STEEL_CHAR_D;
5263 case 0x164f: // (green steel)
5264 element = EL_STEEL_CHAR_E;
5267 case 0x1650: // (green steel)
5268 element = EL_STEEL_CHAR_F;
5271 case 0x1651: // (green steel)
5272 element = EL_STEEL_CHAR_G;
5275 case 0x1652: // (green steel)
5276 element = EL_STEEL_CHAR_H;
5279 case 0x1653: // (green steel)
5280 element = EL_STEEL_CHAR_I;
5283 case 0x1654: // (green steel)
5284 element = EL_STEEL_CHAR_J;
5287 case 0x1655: // (green steel)
5288 element = EL_STEEL_CHAR_K;
5291 case 0x1656: // (green steel)
5292 element = EL_STEEL_CHAR_L;
5295 case 0x1657: // (green steel)
5296 element = EL_STEEL_CHAR_M;
5299 case 0x1658: // (green steel)
5300 element = EL_STEEL_CHAR_N;
5303 case 0x1659: // (green steel)
5304 element = EL_STEEL_CHAR_O;
5307 case 0x165a: // (green steel)
5308 element = EL_STEEL_CHAR_P;
5311 case 0x165b: // (green steel)
5312 element = EL_STEEL_CHAR_Q;
5315 case 0x165c: // (green steel)
5316 element = EL_STEEL_CHAR_R;
5319 case 0x165d: // (green steel)
5320 element = EL_STEEL_CHAR_S;
5323 case 0x165e: // (green steel)
5324 element = EL_STEEL_CHAR_T;
5327 case 0x165f: // (green steel)
5328 element = EL_STEEL_CHAR_U;
5331 case 0x1660: // (green steel)
5332 element = EL_STEEL_CHAR_V;
5335 case 0x1661: // (green steel)
5336 element = EL_STEEL_CHAR_W;
5339 case 0x1662: // (green steel)
5340 element = EL_STEEL_CHAR_X;
5343 case 0x1663: // (green steel)
5344 element = EL_STEEL_CHAR_Y;
5347 case 0x1664: // (green steel)
5348 element = EL_STEEL_CHAR_Z;
5351 case 0x1665: // (green steel)
5352 element = EL_STEEL_CHAR_AUMLAUT;
5355 case 0x1666: // (green steel)
5356 element = EL_STEEL_CHAR_OUMLAUT;
5359 case 0x1667: // (green steel)
5360 element = EL_STEEL_CHAR_UUMLAUT;
5363 case 0x1668: // (green steel)
5364 element = EL_STEEL_CHAR_0;
5367 case 0x1669: // (green steel)
5368 element = EL_STEEL_CHAR_1;
5371 case 0x166a: // (green steel)
5372 element = EL_STEEL_CHAR_2;
5375 case 0x166b: // (green steel)
5376 element = EL_STEEL_CHAR_3;
5379 case 0x166c: // (green steel)
5380 element = EL_STEEL_CHAR_4;
5383 case 0x166d: // (green steel)
5384 element = EL_STEEL_CHAR_5;
5387 case 0x166e: // (green steel)
5388 element = EL_STEEL_CHAR_6;
5391 case 0x166f: // (green steel)
5392 element = EL_STEEL_CHAR_7;
5395 case 0x1670: // (green steel)
5396 element = EL_STEEL_CHAR_8;
5399 case 0x1671: // (green steel)
5400 element = EL_STEEL_CHAR_9;
5403 case 0x1672: // (green steel)
5404 element = EL_STEEL_CHAR_PERIOD;
5407 case 0x1673: // (green steel)
5408 element = EL_STEEL_CHAR_EXCLAM;
5411 case 0x1674: // (green steel)
5412 element = EL_STEEL_CHAR_COLON;
5415 case 0x1675: // (green steel)
5416 element = EL_STEEL_CHAR_LESS;
5419 case 0x1676: // (green steel)
5420 element = EL_STEEL_CHAR_GREATER;
5423 case 0x1677: // (green steel)
5424 element = EL_STEEL_CHAR_QUESTION;
5427 case 0x1678: // (green steel)
5428 element = EL_STEEL_CHAR_COPYRIGHT;
5431 case 0x1679: // (green steel)
5432 element = EL_STEEL_CHAR_UP;
5435 case 0x167a: // (green steel)
5436 element = EL_STEEL_CHAR_DOWN;
5439 case 0x167b: // (green steel)
5440 element = EL_STEEL_CHAR_BUTTON;
5443 case 0x167c: // (green steel)
5444 element = EL_STEEL_CHAR_PLUS;
5447 case 0x167d: // (green steel)
5448 element = EL_STEEL_CHAR_MINUS;
5451 case 0x167e: // (green steel)
5452 element = EL_STEEL_CHAR_APOSTROPHE;
5455 case 0x167f: // (green steel)
5456 element = EL_STEEL_CHAR_PARENLEFT;
5459 case 0x1680: // (green steel)
5460 element = EL_STEEL_CHAR_PARENRIGHT;
5463 case 0x1681: // gate (red)
5464 element = EL_EM_GATE_1;
5467 case 0x1682: // secret gate (red)
5468 element = EL_EM_GATE_1_GRAY;
5471 case 0x1683: // gate (yellow)
5472 element = EL_EM_GATE_2;
5475 case 0x1684: // secret gate (yellow)
5476 element = EL_EM_GATE_2_GRAY;
5479 case 0x1685: // gate (blue)
5480 element = EL_EM_GATE_4;
5483 case 0x1686: // secret gate (blue)
5484 element = EL_EM_GATE_4_GRAY;
5487 case 0x1687: // gate (green)
5488 element = EL_EM_GATE_3;
5491 case 0x1688: // secret gate (green)
5492 element = EL_EM_GATE_3_GRAY;
5495 case 0x1689: // gate (white)
5496 element = EL_DC_GATE_WHITE;
5499 case 0x168a: // secret gate (white)
5500 element = EL_DC_GATE_WHITE_GRAY;
5503 case 0x168b: // secret gate (no key)
5504 element = EL_DC_GATE_FAKE_GRAY;
5508 element = EL_ROBOT_WHEEL;
5512 element = EL_DC_TIMEGATE_SWITCH;
5516 element = EL_ACID_POOL_BOTTOM;
5520 element = EL_ACID_POOL_TOPLEFT;
5524 element = EL_ACID_POOL_TOPRIGHT;
5528 element = EL_ACID_POOL_BOTTOMLEFT;
5532 element = EL_ACID_POOL_BOTTOMRIGHT;
5536 element = EL_STEELWALL;
5540 element = EL_STEELWALL_SLIPPERY;
5543 case 0x1695: // steel wall (not round)
5544 element = EL_STEELWALL;
5547 case 0x1696: // steel wall (left)
5548 element = EL_DC_STEELWALL_1_LEFT;
5551 case 0x1697: // steel wall (bottom)
5552 element = EL_DC_STEELWALL_1_BOTTOM;
5555 case 0x1698: // steel wall (right)
5556 element = EL_DC_STEELWALL_1_RIGHT;
5559 case 0x1699: // steel wall (top)
5560 element = EL_DC_STEELWALL_1_TOP;
5563 case 0x169a: // steel wall (left/bottom)
5564 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5567 case 0x169b: // steel wall (right/bottom)
5568 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5571 case 0x169c: // steel wall (right/top)
5572 element = EL_DC_STEELWALL_1_TOPRIGHT;
5575 case 0x169d: // steel wall (left/top)
5576 element = EL_DC_STEELWALL_1_TOPLEFT;
5579 case 0x169e: // steel wall (right/bottom small)
5580 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5583 case 0x169f: // steel wall (left/bottom small)
5584 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5587 case 0x16a0: // steel wall (right/top small)
5588 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5591 case 0x16a1: // steel wall (left/top small)
5592 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5595 case 0x16a2: // steel wall (left/right)
5596 element = EL_DC_STEELWALL_1_VERTICAL;
5599 case 0x16a3: // steel wall (top/bottom)
5600 element = EL_DC_STEELWALL_1_HORIZONTAL;
5603 case 0x16a4: // steel wall 2 (left end)
5604 element = EL_DC_STEELWALL_2_LEFT;
5607 case 0x16a5: // steel wall 2 (right end)
5608 element = EL_DC_STEELWALL_2_RIGHT;
5611 case 0x16a6: // steel wall 2 (top end)
5612 element = EL_DC_STEELWALL_2_TOP;
5615 case 0x16a7: // steel wall 2 (bottom end)
5616 element = EL_DC_STEELWALL_2_BOTTOM;
5619 case 0x16a8: // steel wall 2 (left/right)
5620 element = EL_DC_STEELWALL_2_HORIZONTAL;
5623 case 0x16a9: // steel wall 2 (up/down)
5624 element = EL_DC_STEELWALL_2_VERTICAL;
5627 case 0x16aa: // steel wall 2 (mid)
5628 element = EL_DC_STEELWALL_2_MIDDLE;
5632 element = EL_SIGN_EXCLAMATION;
5636 element = EL_SIGN_RADIOACTIVITY;
5640 element = EL_SIGN_STOP;
5644 element = EL_SIGN_WHEELCHAIR;
5648 element = EL_SIGN_PARKING;
5652 element = EL_SIGN_NO_ENTRY;
5656 element = EL_SIGN_HEART;
5660 element = EL_SIGN_GIVE_WAY;
5664 element = EL_SIGN_ENTRY_FORBIDDEN;
5668 element = EL_SIGN_EMERGENCY_EXIT;
5672 element = EL_SIGN_YIN_YANG;
5676 element = EL_WALL_EMERALD;
5680 element = EL_WALL_DIAMOND;
5684 element = EL_WALL_PEARL;
5688 element = EL_WALL_CRYSTAL;
5692 element = EL_INVISIBLE_WALL;
5696 element = EL_INVISIBLE_STEELWALL;
5700 // EL_INVISIBLE_SAND
5703 element = EL_LIGHT_SWITCH;
5707 element = EL_ENVELOPE_1;
5711 if (element >= 0x0117 && element <= 0x036e) // (?)
5712 element = EL_DIAMOND;
5713 else if (element >= 0x042d && element <= 0x0684) // (?)
5714 element = EL_EMERALD;
5715 else if (element >= 0x157c && element <= 0x158b)
5717 else if (element >= 0x1590 && element <= 0x159f)
5718 element = EL_DC_LANDMINE;
5719 else if (element >= 0x16bc && element <= 0x16cb)
5720 element = EL_INVISIBLE_SAND;
5723 Warn("unknown Diamond Caves element 0x%04x", element);
5725 element = EL_UNKNOWN;
5730 return getMappedElement(element);
5733 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5735 byte header[DC_LEVEL_HEADER_SIZE];
5737 int envelope_header_pos = 62;
5738 int envelope_content_pos = 94;
5739 int level_name_pos = 251;
5740 int level_author_pos = 292;
5741 int envelope_header_len;
5742 int envelope_content_len;
5744 int level_author_len;
5746 int num_yamyam_contents;
5749 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5751 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5753 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5755 header[i * 2 + 0] = header_word >> 8;
5756 header[i * 2 + 1] = header_word & 0xff;
5759 // read some values from level header to check level decoding integrity
5760 fieldx = header[6] | (header[7] << 8);
5761 fieldy = header[8] | (header[9] << 8);
5762 num_yamyam_contents = header[60] | (header[61] << 8);
5764 // do some simple sanity checks to ensure that level was correctly decoded
5765 if (fieldx < 1 || fieldx > 256 ||
5766 fieldy < 1 || fieldy > 256 ||
5767 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5769 level->no_valid_file = TRUE;
5771 Warn("cannot decode level from stream -- using empty level");
5776 // maximum envelope header size is 31 bytes
5777 envelope_header_len = header[envelope_header_pos];
5778 // maximum envelope content size is 110 (156?) bytes
5779 envelope_content_len = header[envelope_content_pos];
5781 // maximum level title size is 40 bytes
5782 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5783 // maximum level author size is 30 (51?) bytes
5784 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5788 for (i = 0; i < envelope_header_len; i++)
5789 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5790 level->envelope[0].text[envelope_size++] =
5791 header[envelope_header_pos + 1 + i];
5793 if (envelope_header_len > 0 && envelope_content_len > 0)
5795 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5796 level->envelope[0].text[envelope_size++] = '\n';
5797 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5798 level->envelope[0].text[envelope_size++] = '\n';
5801 for (i = 0; i < envelope_content_len; i++)
5802 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5803 level->envelope[0].text[envelope_size++] =
5804 header[envelope_content_pos + 1 + i];
5806 level->envelope[0].text[envelope_size] = '\0';
5808 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5809 level->envelope[0].ysize = 10;
5810 level->envelope[0].autowrap = TRUE;
5811 level->envelope[0].centered = TRUE;
5813 for (i = 0; i < level_name_len; i++)
5814 level->name[i] = header[level_name_pos + 1 + i];
5815 level->name[level_name_len] = '\0';
5817 for (i = 0; i < level_author_len; i++)
5818 level->author[i] = header[level_author_pos + 1 + i];
5819 level->author[level_author_len] = '\0';
5821 num_yamyam_contents = header[60] | (header[61] << 8);
5822 level->num_yamyam_contents =
5823 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5825 for (i = 0; i < num_yamyam_contents; i++)
5827 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5829 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5830 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5832 if (i < MAX_ELEMENT_CONTENTS)
5833 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5837 fieldx = header[6] | (header[7] << 8);
5838 fieldy = header[8] | (header[9] << 8);
5839 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5840 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5842 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5844 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5845 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5847 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5848 level->field[x][y] = getMappedElement_DC(element_dc);
5851 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5852 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5853 level->field[x][y] = EL_PLAYER_1;
5855 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5856 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5857 level->field[x][y] = EL_PLAYER_2;
5859 level->gems_needed = header[18] | (header[19] << 8);
5861 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5862 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5863 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5864 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5865 level->score[SC_NUT] = header[28] | (header[29] << 8);
5866 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5867 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5868 level->score[SC_BUG] = header[34] | (header[35] << 8);
5869 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5870 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5871 level->score[SC_KEY] = header[40] | (header[41] << 8);
5872 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5874 level->time = header[44] | (header[45] << 8);
5876 level->amoeba_speed = header[46] | (header[47] << 8);
5877 level->time_light = header[48] | (header[49] << 8);
5878 level->time_timegate = header[50] | (header[51] << 8);
5879 level->time_wheel = header[52] | (header[53] << 8);
5880 level->time_magic_wall = header[54] | (header[55] << 8);
5881 level->extra_time = header[56] | (header[57] << 8);
5882 level->shield_normal_time = header[58] | (header[59] << 8);
5884 // shield and extra time elements do not have a score
5885 level->score[SC_SHIELD] = 0;
5886 level->extra_time_score = 0;
5888 // set time for normal and deadly shields to the same value
5889 level->shield_deadly_time = level->shield_normal_time;
5891 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5892 // can slip down from flat walls, like normal walls and steel walls
5893 level->em_slippery_gems = TRUE;
5895 // time score is counted for each 10 seconds left in Diamond Caves levels
5896 level->time_score_base = 10;
5899 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5900 struct LevelFileInfo *level_file_info,
5901 boolean level_info_only)
5903 char *filename = level_file_info->filename;
5905 int num_magic_bytes = 8;
5906 char magic_bytes[num_magic_bytes + 1];
5907 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5909 if (!(file = openFile(filename, MODE_READ)))
5911 level->no_valid_file = TRUE;
5913 if (!level_info_only)
5914 Warn("cannot read level '%s' -- using empty level", filename);
5919 // fseek(file, 0x0000, SEEK_SET);
5921 if (level_file_info->packed)
5923 // read "magic bytes" from start of file
5924 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5925 magic_bytes[0] = '\0';
5927 // check "magic bytes" for correct file format
5928 if (!strPrefix(magic_bytes, "DC2"))
5930 level->no_valid_file = TRUE;
5932 Warn("unknown DC level file '%s' -- using empty level", filename);
5937 if (strPrefix(magic_bytes, "DC2Win95") ||
5938 strPrefix(magic_bytes, "DC2Win98"))
5940 int position_first_level = 0x00fa;
5941 int extra_bytes = 4;
5944 // advance file stream to first level inside the level package
5945 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5947 // each block of level data is followed by block of non-level data
5948 num_levels_to_skip *= 2;
5950 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5951 while (num_levels_to_skip >= 0)
5953 // advance file stream to next level inside the level package
5954 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5956 level->no_valid_file = TRUE;
5958 Warn("cannot fseek in file '%s' -- using empty level", filename);
5963 // skip apparently unused extra bytes following each level
5964 ReadUnusedBytesFromFile(file, extra_bytes);
5966 // read size of next level in level package
5967 skip_bytes = getFile32BitLE(file);
5969 num_levels_to_skip--;
5974 level->no_valid_file = TRUE;
5976 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5982 LoadLevelFromFileStream_DC(file, level);
5988 // ----------------------------------------------------------------------------
5989 // functions for loading SB level
5990 // ----------------------------------------------------------------------------
5992 int getMappedElement_SB(int element_ascii, boolean use_ces)
6000 sb_element_mapping[] =
6002 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6003 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6004 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6005 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6006 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6007 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6008 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6009 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6016 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6017 if (element_ascii == sb_element_mapping[i].ascii)
6018 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6020 return EL_UNDEFINED;
6023 static void SetLevelSettings_SB(struct LevelInfo *level)
6027 level->use_step_counter = TRUE;
6030 level->score[SC_TIME_BONUS] = 0;
6031 level->time_score_base = 1;
6032 level->rate_time_over_score = TRUE;
6035 level->auto_exit_sokoban = TRUE;
6038 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6039 struct LevelFileInfo *level_file_info,
6040 boolean level_info_only)
6042 char *filename = level_file_info->filename;
6043 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6044 char last_comment[MAX_LINE_LEN];
6045 char level_name[MAX_LINE_LEN];
6048 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6049 boolean read_continued_line = FALSE;
6050 boolean reading_playfield = FALSE;
6051 boolean got_valid_playfield_line = FALSE;
6052 boolean invalid_playfield_char = FALSE;
6053 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6054 int file_level_nr = 0;
6056 int x = 0, y = 0; // initialized to make compilers happy
6058 last_comment[0] = '\0';
6059 level_name[0] = '\0';
6061 if (!(file = openFile(filename, MODE_READ)))
6063 level->no_valid_file = TRUE;
6065 if (!level_info_only)
6066 Warn("cannot read level '%s' -- using empty level", filename);
6071 while (!checkEndOfFile(file))
6073 // level successfully read, but next level may follow here
6074 if (!got_valid_playfield_line && reading_playfield)
6076 // read playfield from single level file -- skip remaining file
6077 if (!level_file_info->packed)
6080 if (file_level_nr >= num_levels_to_skip)
6085 last_comment[0] = '\0';
6086 level_name[0] = '\0';
6088 reading_playfield = FALSE;
6091 got_valid_playfield_line = FALSE;
6093 // read next line of input file
6094 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6097 // check if line was completely read and is terminated by line break
6098 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6101 // cut trailing line break (this can be newline and/or carriage return)
6102 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6103 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6106 // copy raw input line for later use (mainly debugging output)
6107 strcpy(line_raw, line);
6109 if (read_continued_line)
6111 // append new line to existing line, if there is enough space
6112 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6113 strcat(previous_line, line_ptr);
6115 strcpy(line, previous_line); // copy storage buffer to line
6117 read_continued_line = FALSE;
6120 // if the last character is '\', continue at next line
6121 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6123 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6124 strcpy(previous_line, line); // copy line to storage buffer
6126 read_continued_line = TRUE;
6132 if (line[0] == '\0')
6135 // extract comment text from comment line
6138 for (line_ptr = line; *line_ptr; line_ptr++)
6139 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6142 strcpy(last_comment, line_ptr);
6147 // extract level title text from line containing level title
6148 if (line[0] == '\'')
6150 strcpy(level_name, &line[1]);
6152 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6153 level_name[strlen(level_name) - 1] = '\0';
6158 // skip lines containing only spaces (or empty lines)
6159 for (line_ptr = line; *line_ptr; line_ptr++)
6160 if (*line_ptr != ' ')
6162 if (*line_ptr == '\0')
6165 // at this point, we have found a line containing part of a playfield
6167 got_valid_playfield_line = TRUE;
6169 if (!reading_playfield)
6171 reading_playfield = TRUE;
6172 invalid_playfield_char = FALSE;
6174 for (x = 0; x < MAX_LEV_FIELDX; x++)
6175 for (y = 0; y < MAX_LEV_FIELDY; y++)
6176 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6181 // start with topmost tile row
6185 // skip playfield line if larger row than allowed
6186 if (y >= MAX_LEV_FIELDY)
6189 // start with leftmost tile column
6192 // read playfield elements from line
6193 for (line_ptr = line; *line_ptr; line_ptr++)
6195 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6197 // stop parsing playfield line if larger column than allowed
6198 if (x >= MAX_LEV_FIELDX)
6201 if (mapped_sb_element == EL_UNDEFINED)
6203 invalid_playfield_char = TRUE;
6208 level->field[x][y] = mapped_sb_element;
6210 // continue with next tile column
6213 level->fieldx = MAX(x, level->fieldx);
6216 if (invalid_playfield_char)
6218 // if first playfield line, treat invalid lines as comment lines
6220 reading_playfield = FALSE;
6225 // continue with next tile row
6233 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6234 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6236 if (!reading_playfield)
6238 level->no_valid_file = TRUE;
6240 Warn("cannot read level '%s' -- using empty level", filename);
6245 if (*level_name != '\0')
6247 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6248 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6250 else if (*last_comment != '\0')
6252 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6253 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6257 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6260 // set all empty fields beyond the border walls to invisible steel wall
6261 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6263 if ((x == 0 || x == level->fieldx - 1 ||
6264 y == 0 || y == level->fieldy - 1) &&
6265 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6266 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6267 level->field, level->fieldx, level->fieldy);
6270 // set special level settings for Sokoban levels
6271 SetLevelSettings_SB(level);
6273 if (load_xsb_to_ces)
6275 // special global settings can now be set in level template
6276 level->use_custom_template = TRUE;
6281 // -------------------------------------------------------------------------
6282 // functions for handling native levels
6283 // -------------------------------------------------------------------------
6285 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6286 struct LevelFileInfo *level_file_info,
6287 boolean level_info_only)
6289 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6290 level->no_valid_file = TRUE;
6293 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6294 struct LevelFileInfo *level_file_info,
6295 boolean level_info_only)
6299 // determine position of requested level inside level package
6300 if (level_file_info->packed)
6301 pos = level_file_info->nr - leveldir_current->first_level;
6303 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6304 level->no_valid_file = TRUE;
6307 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6308 struct LevelFileInfo *level_file_info,
6309 boolean level_info_only)
6311 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6312 level->no_valid_file = TRUE;
6315 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6317 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6318 CopyNativeLevel_RND_to_EM(level);
6319 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6320 CopyNativeLevel_RND_to_SP(level);
6321 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6322 CopyNativeLevel_RND_to_MM(level);
6325 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6327 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6328 CopyNativeLevel_EM_to_RND(level);
6329 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6330 CopyNativeLevel_SP_to_RND(level);
6331 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6332 CopyNativeLevel_MM_to_RND(level);
6335 void SaveNativeLevel(struct LevelInfo *level)
6337 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6339 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6340 char *filename = getLevelFilenameFromBasename(basename);
6342 CopyNativeLevel_RND_to_SP(level);
6343 CopyNativeTape_RND_to_SP(level);
6345 SaveNativeLevel_SP(filename);
6350 // ----------------------------------------------------------------------------
6351 // functions for loading generic level
6352 // ----------------------------------------------------------------------------
6354 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6355 struct LevelFileInfo *level_file_info,
6356 boolean level_info_only)
6358 // always start with reliable default values
6359 setLevelInfoToDefaults(level, level_info_only, TRUE);
6361 switch (level_file_info->type)
6363 case LEVEL_FILE_TYPE_RND:
6364 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6367 case LEVEL_FILE_TYPE_EM:
6368 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6369 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6372 case LEVEL_FILE_TYPE_SP:
6373 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6374 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6377 case LEVEL_FILE_TYPE_MM:
6378 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6379 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6382 case LEVEL_FILE_TYPE_DC:
6383 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6386 case LEVEL_FILE_TYPE_SB:
6387 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6391 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6395 // if level file is invalid, restore level structure to default values
6396 if (level->no_valid_file)
6397 setLevelInfoToDefaults(level, level_info_only, FALSE);
6399 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6400 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6402 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6403 CopyNativeLevel_Native_to_RND(level);
6406 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6408 static struct LevelFileInfo level_file_info;
6410 // always start with reliable default values
6411 setFileInfoToDefaults(&level_file_info);
6413 level_file_info.nr = 0; // unknown level number
6414 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6416 setString(&level_file_info.filename, filename);
6418 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6421 static void LoadLevel_InitVersion(struct LevelInfo *level)
6425 if (leveldir_current == NULL) // only when dumping level
6428 // all engine modifications also valid for levels which use latest engine
6429 if (level->game_version < VERSION_IDENT(3,2,0,5))
6431 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6432 level->time_score_base = 10;
6435 if (leveldir_current->latest_engine)
6437 // ---------- use latest game engine --------------------------------------
6439 /* For all levels which are forced to use the latest game engine version
6440 (normally all but user contributed, private and undefined levels), set
6441 the game engine version to the actual version; this allows for actual
6442 corrections in the game engine to take effect for existing, converted
6443 levels (from "classic" or other existing games) to make the emulation
6444 of the corresponding game more accurate, while (hopefully) not breaking
6445 existing levels created from other players. */
6447 level->game_version = GAME_VERSION_ACTUAL;
6449 /* Set special EM style gems behaviour: EM style gems slip down from
6450 normal, steel and growing wall. As this is a more fundamental change,
6451 it seems better to set the default behaviour to "off" (as it is more
6452 natural) and make it configurable in the level editor (as a property
6453 of gem style elements). Already existing converted levels (neither
6454 private nor contributed levels) are changed to the new behaviour. */
6456 if (level->file_version < FILE_VERSION_2_0)
6457 level->em_slippery_gems = TRUE;
6462 // ---------- use game engine the level was created with --------------------
6464 /* For all levels which are not forced to use the latest game engine
6465 version (normally user contributed, private and undefined levels),
6466 use the version of the game engine the levels were created for.
6468 Since 2.0.1, the game engine version is now directly stored
6469 in the level file (chunk "VERS"), so there is no need anymore
6470 to set the game version from the file version (except for old,
6471 pre-2.0 levels, where the game version is still taken from the
6472 file format version used to store the level -- see above). */
6474 // player was faster than enemies in 1.0.0 and before
6475 if (level->file_version == FILE_VERSION_1_0)
6476 for (i = 0; i < MAX_PLAYERS; i++)
6477 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6479 // default behaviour for EM style gems was "slippery" only in 2.0.1
6480 if (level->game_version == VERSION_IDENT(2,0,1,0))
6481 level->em_slippery_gems = TRUE;
6483 // springs could be pushed over pits before (pre-release version) 2.2.0
6484 if (level->game_version < VERSION_IDENT(2,2,0,0))
6485 level->use_spring_bug = TRUE;
6487 if (level->game_version < VERSION_IDENT(3,2,0,5))
6489 // time orb caused limited time in endless time levels before 3.2.0-5
6490 level->use_time_orb_bug = TRUE;
6492 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6493 level->block_snap_field = FALSE;
6495 // extra time score was same value as time left score before 3.2.0-5
6496 level->extra_time_score = level->score[SC_TIME_BONUS];
6499 if (level->game_version < VERSION_IDENT(3,2,0,7))
6501 // default behaviour for snapping was "not continuous" before 3.2.0-7
6502 level->continuous_snapping = FALSE;
6505 // only few elements were able to actively move into acid before 3.1.0
6506 // trigger settings did not exist before 3.1.0; set to default "any"
6507 if (level->game_version < VERSION_IDENT(3,1,0,0))
6509 // correct "can move into acid" settings (all zero in old levels)
6511 level->can_move_into_acid_bits = 0; // nothing can move into acid
6512 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6514 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6515 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6516 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6517 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6519 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6520 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6522 // correct trigger settings (stored as zero == "none" in old levels)
6524 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6526 int element = EL_CUSTOM_START + i;
6527 struct ElementInfo *ei = &element_info[element];
6529 for (j = 0; j < ei->num_change_pages; j++)
6531 struct ElementChangeInfo *change = &ei->change_page[j];
6533 change->trigger_player = CH_PLAYER_ANY;
6534 change->trigger_page = CH_PAGE_ANY;
6539 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6541 int element = EL_CUSTOM_256;
6542 struct ElementInfo *ei = &element_info[element];
6543 struct ElementChangeInfo *change = &ei->change_page[0];
6545 /* This is needed to fix a problem that was caused by a bugfix in function
6546 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6547 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6548 not replace walkable elements, but instead just placed the player on it,
6549 without placing the Sokoban field under the player). Unfortunately, this
6550 breaks "Snake Bite" style levels when the snake is halfway through a door
6551 that just closes (the snake head is still alive and can be moved in this
6552 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6553 player (without Sokoban element) which then gets killed as designed). */
6555 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6556 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6557 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6558 change->target_element = EL_PLAYER_1;
6561 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6562 if (level->game_version < VERSION_IDENT(3,2,5,0))
6564 /* This is needed to fix a problem that was caused by a bugfix in function
6565 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6566 corrects the behaviour when a custom element changes to another custom
6567 element with a higher element number that has change actions defined.
6568 Normally, only one change per frame is allowed for custom elements.
6569 Therefore, it is checked if a custom element already changed in the
6570 current frame; if it did, subsequent changes are suppressed.
6571 Unfortunately, this is only checked for element changes, but not for
6572 change actions, which are still executed. As the function above loops
6573 through all custom elements from lower to higher, an element change
6574 resulting in a lower CE number won't be checked again, while a target
6575 element with a higher number will also be checked, and potential change
6576 actions will get executed for this CE, too (which is wrong), while
6577 further changes are ignored (which is correct). As this bugfix breaks
6578 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6579 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6580 behaviour for existing levels and tapes that make use of this bug */
6582 level->use_action_after_change_bug = TRUE;
6585 // not centering level after relocating player was default only in 3.2.3
6586 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6587 level->shifted_relocation = TRUE;
6589 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6590 if (level->game_version < VERSION_IDENT(3,2,6,0))
6591 level->em_explodes_by_fire = TRUE;
6593 // levels were solved by the first player entering an exit up to 4.1.0.0
6594 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6595 level->solved_by_one_player = TRUE;
6597 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6598 if (level->game_version < VERSION_IDENT(4,1,1,1))
6599 level->use_life_bugs = TRUE;
6601 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6602 if (level->game_version < VERSION_IDENT(4,1,1,1))
6603 level->sb_objects_needed = FALSE;
6605 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6606 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6607 level->finish_dig_collect = FALSE;
6609 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6610 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6611 level->keep_walkable_ce = TRUE;
6614 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6616 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6619 // check if this level is (not) a Sokoban level
6620 for (y = 0; y < level->fieldy; y++)
6621 for (x = 0; x < level->fieldx; x++)
6622 if (!IS_SB_ELEMENT(Tile[x][y]))
6623 is_sokoban_level = FALSE;
6625 if (is_sokoban_level)
6627 // set special level settings for Sokoban levels
6628 SetLevelSettings_SB(level);
6632 static void LoadLevel_InitSettings(struct LevelInfo *level)
6634 // adjust level settings for (non-native) Sokoban-style levels
6635 LoadLevel_InitSettings_SB(level);
6637 // rename levels with title "nameless level" or if renaming is forced
6638 if (leveldir_current->empty_level_name != NULL &&
6639 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6640 leveldir_current->force_level_name))
6641 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6642 leveldir_current->empty_level_name, level_nr);
6645 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6649 // map elements that have changed in newer versions
6650 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6651 level->game_version);
6652 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6653 for (x = 0; x < 3; x++)
6654 for (y = 0; y < 3; y++)
6655 level->yamyam_content[i].e[x][y] =
6656 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6657 level->game_version);
6661 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6665 // map custom element change events that have changed in newer versions
6666 // (these following values were accidentally changed in version 3.0.1)
6667 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6668 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6670 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6672 int element = EL_CUSTOM_START + i;
6674 // order of checking and copying events to be mapped is important
6675 // (do not change the start and end value -- they are constant)
6676 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6678 if (HAS_CHANGE_EVENT(element, j - 2))
6680 SET_CHANGE_EVENT(element, j - 2, FALSE);
6681 SET_CHANGE_EVENT(element, j, TRUE);
6685 // order of checking and copying events to be mapped is important
6686 // (do not change the start and end value -- they are constant)
6687 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6689 if (HAS_CHANGE_EVENT(element, j - 1))
6691 SET_CHANGE_EVENT(element, j - 1, FALSE);
6692 SET_CHANGE_EVENT(element, j, TRUE);
6698 // initialize "can_change" field for old levels with only one change page
6699 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6701 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6703 int element = EL_CUSTOM_START + i;
6705 if (CAN_CHANGE(element))
6706 element_info[element].change->can_change = TRUE;
6710 // correct custom element values (for old levels without these options)
6711 if (level->game_version < VERSION_IDENT(3,1,1,0))
6713 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6715 int element = EL_CUSTOM_START + i;
6716 struct ElementInfo *ei = &element_info[element];
6718 if (ei->access_direction == MV_NO_DIRECTION)
6719 ei->access_direction = MV_ALL_DIRECTIONS;
6723 // correct custom element values (fix invalid values for all versions)
6726 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6728 int element = EL_CUSTOM_START + i;
6729 struct ElementInfo *ei = &element_info[element];
6731 for (j = 0; j < ei->num_change_pages; j++)
6733 struct ElementChangeInfo *change = &ei->change_page[j];
6735 if (change->trigger_player == CH_PLAYER_NONE)
6736 change->trigger_player = CH_PLAYER_ANY;
6738 if (change->trigger_side == CH_SIDE_NONE)
6739 change->trigger_side = CH_SIDE_ANY;
6744 // initialize "can_explode" field for old levels which did not store this
6745 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6746 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6748 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6750 int element = EL_CUSTOM_START + i;
6752 if (EXPLODES_1X1_OLD(element))
6753 element_info[element].explosion_type = EXPLODES_1X1;
6755 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6756 EXPLODES_SMASHED(element) ||
6757 EXPLODES_IMPACT(element)));
6761 // correct previously hard-coded move delay values for maze runner style
6762 if (level->game_version < VERSION_IDENT(3,1,1,0))
6764 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6766 int element = EL_CUSTOM_START + i;
6768 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6770 // previously hard-coded and therefore ignored
6771 element_info[element].move_delay_fixed = 9;
6772 element_info[element].move_delay_random = 0;
6777 // set some other uninitialized values of custom elements in older levels
6778 if (level->game_version < VERSION_IDENT(3,1,0,0))
6780 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6782 int element = EL_CUSTOM_START + i;
6784 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6786 element_info[element].explosion_delay = 17;
6787 element_info[element].ignition_delay = 8;
6791 // set mouse click change events to work for left/middle/right mouse button
6792 if (level->game_version < VERSION_IDENT(4,2,3,0))
6794 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6796 int element = EL_CUSTOM_START + i;
6797 struct ElementInfo *ei = &element_info[element];
6799 for (j = 0; j < ei->num_change_pages; j++)
6801 struct ElementChangeInfo *change = &ei->change_page[j];
6803 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6804 change->has_event[CE_PRESSED_BY_MOUSE] ||
6805 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6806 change->has_event[CE_MOUSE_PRESSED_ON_X])
6807 change->trigger_side = CH_SIDE_ANY;
6813 static void LoadLevel_InitElements(struct LevelInfo *level)
6815 LoadLevel_InitStandardElements(level);
6817 if (level->file_has_custom_elements)
6818 LoadLevel_InitCustomElements(level);
6820 // initialize element properties for level editor etc.
6821 InitElementPropertiesEngine(level->game_version);
6822 InitElementPropertiesGfxElement();
6825 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6829 // map elements that have changed in newer versions
6830 for (y = 0; y < level->fieldy; y++)
6831 for (x = 0; x < level->fieldx; x++)
6832 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6833 level->game_version);
6835 // clear unused playfield data (nicer if level gets resized in editor)
6836 for (x = 0; x < MAX_LEV_FIELDX; x++)
6837 for (y = 0; y < MAX_LEV_FIELDY; y++)
6838 if (x >= level->fieldx || y >= level->fieldy)
6839 level->field[x][y] = EL_EMPTY;
6841 // copy elements to runtime playfield array
6842 for (x = 0; x < MAX_LEV_FIELDX; x++)
6843 for (y = 0; y < MAX_LEV_FIELDY; y++)
6844 Tile[x][y] = level->field[x][y];
6846 // initialize level size variables for faster access
6847 lev_fieldx = level->fieldx;
6848 lev_fieldy = level->fieldy;
6850 // determine border element for this level
6851 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6852 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6857 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6859 struct LevelFileInfo *level_file_info = &level->file_info;
6861 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6862 CopyNativeLevel_RND_to_Native(level);
6865 static void LoadLevelTemplate_LoadAndInit(void)
6867 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6869 LoadLevel_InitVersion(&level_template);
6870 LoadLevel_InitElements(&level_template);
6871 LoadLevel_InitSettings(&level_template);
6873 ActivateLevelTemplate();
6876 void LoadLevelTemplate(int nr)
6878 if (!fileExists(getGlobalLevelTemplateFilename()))
6880 Warn("no level template found for this level");
6885 setLevelFileInfo(&level_template.file_info, nr);
6887 LoadLevelTemplate_LoadAndInit();
6890 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6892 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6894 LoadLevelTemplate_LoadAndInit();
6897 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6899 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6901 if (level.use_custom_template)
6903 if (network_level != NULL)
6904 LoadNetworkLevelTemplate(network_level);
6906 LoadLevelTemplate(-1);
6909 LoadLevel_InitVersion(&level);
6910 LoadLevel_InitElements(&level);
6911 LoadLevel_InitPlayfield(&level);
6912 LoadLevel_InitSettings(&level);
6914 LoadLevel_InitNativeEngines(&level);
6917 void LoadLevel(int nr)
6919 SetLevelSetInfo(leveldir_current->identifier, nr);
6921 setLevelFileInfo(&level.file_info, nr);
6923 LoadLevel_LoadAndInit(NULL);
6926 void LoadLevelInfoOnly(int nr)
6928 setLevelFileInfo(&level.file_info, nr);
6930 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6933 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6935 SetLevelSetInfo(network_level->leveldir_identifier,
6936 network_level->file_info.nr);
6938 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6940 LoadLevel_LoadAndInit(network_level);
6943 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6947 chunk_size += putFileVersion(file, level->file_version);
6948 chunk_size += putFileVersion(file, level->game_version);
6953 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6957 chunk_size += putFile16BitBE(file, level->creation_date.year);
6958 chunk_size += putFile8Bit(file, level->creation_date.month);
6959 chunk_size += putFile8Bit(file, level->creation_date.day);
6964 #if ENABLE_HISTORIC_CHUNKS
6965 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6969 putFile8Bit(file, level->fieldx);
6970 putFile8Bit(file, level->fieldy);
6972 putFile16BitBE(file, level->time);
6973 putFile16BitBE(file, level->gems_needed);
6975 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6976 putFile8Bit(file, level->name[i]);
6978 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6979 putFile8Bit(file, level->score[i]);
6981 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6982 for (y = 0; y < 3; y++)
6983 for (x = 0; x < 3; x++)
6984 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6985 level->yamyam_content[i].e[x][y]));
6986 putFile8Bit(file, level->amoeba_speed);
6987 putFile8Bit(file, level->time_magic_wall);
6988 putFile8Bit(file, level->time_wheel);
6989 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6990 level->amoeba_content));
6991 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6992 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6993 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6994 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6996 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6998 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6999 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7000 putFile32BitBE(file, level->can_move_into_acid_bits);
7001 putFile8Bit(file, level->dont_collide_with_bits);
7003 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7004 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7006 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7007 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7008 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7010 putFile8Bit(file, level->game_engine_type);
7012 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7016 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7021 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7022 chunk_size += putFile8Bit(file, level->name[i]);
7027 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7032 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7033 chunk_size += putFile8Bit(file, level->author[i]);
7038 #if ENABLE_HISTORIC_CHUNKS
7039 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7044 for (y = 0; y < level->fieldy; y++)
7045 for (x = 0; x < level->fieldx; x++)
7046 if (level->encoding_16bit_field)
7047 chunk_size += putFile16BitBE(file, level->field[x][y]);
7049 chunk_size += putFile8Bit(file, level->field[x][y]);
7055 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7060 for (y = 0; y < level->fieldy; y++)
7061 for (x = 0; x < level->fieldx; x++)
7062 chunk_size += putFile16BitBE(file, level->field[x][y]);
7067 #if ENABLE_HISTORIC_CHUNKS
7068 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7072 putFile8Bit(file, EL_YAMYAM);
7073 putFile8Bit(file, level->num_yamyam_contents);
7074 putFile8Bit(file, 0);
7075 putFile8Bit(file, 0);
7077 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7078 for (y = 0; y < 3; y++)
7079 for (x = 0; x < 3; x++)
7080 if (level->encoding_16bit_field)
7081 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7083 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7087 #if ENABLE_HISTORIC_CHUNKS
7088 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7091 int num_contents, content_xsize, content_ysize;
7092 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7094 if (element == EL_YAMYAM)
7096 num_contents = level->num_yamyam_contents;
7100 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7101 for (y = 0; y < 3; y++)
7102 for (x = 0; x < 3; x++)
7103 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7105 else if (element == EL_BD_AMOEBA)
7111 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7112 for (y = 0; y < 3; y++)
7113 for (x = 0; x < 3; x++)
7114 content_array[i][x][y] = EL_EMPTY;
7115 content_array[0][0][0] = level->amoeba_content;
7119 // chunk header already written -- write empty chunk data
7120 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7122 Warn("cannot save content for element '%d'", element);
7127 putFile16BitBE(file, element);
7128 putFile8Bit(file, num_contents);
7129 putFile8Bit(file, content_xsize);
7130 putFile8Bit(file, content_ysize);
7132 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7134 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7135 for (y = 0; y < 3; y++)
7136 for (x = 0; x < 3; x++)
7137 putFile16BitBE(file, content_array[i][x][y]);
7141 #if ENABLE_HISTORIC_CHUNKS
7142 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7144 int envelope_nr = element - EL_ENVELOPE_1;
7145 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7149 chunk_size += putFile16BitBE(file, element);
7150 chunk_size += putFile16BitBE(file, envelope_len);
7151 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7152 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7154 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7155 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7157 for (i = 0; i < envelope_len; i++)
7158 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7164 #if ENABLE_HISTORIC_CHUNKS
7165 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7166 int num_changed_custom_elements)
7170 putFile16BitBE(file, num_changed_custom_elements);
7172 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7174 int element = EL_CUSTOM_START + i;
7176 struct ElementInfo *ei = &element_info[element];
7178 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7180 if (check < num_changed_custom_elements)
7182 putFile16BitBE(file, element);
7183 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7190 if (check != num_changed_custom_elements) // should not happen
7191 Warn("inconsistent number of custom element properties");
7195 #if ENABLE_HISTORIC_CHUNKS
7196 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7197 int num_changed_custom_elements)
7201 putFile16BitBE(file, num_changed_custom_elements);
7203 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7205 int element = EL_CUSTOM_START + i;
7207 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7209 if (check < num_changed_custom_elements)
7211 putFile16BitBE(file, element);
7212 putFile16BitBE(file, element_info[element].change->target_element);
7219 if (check != num_changed_custom_elements) // should not happen
7220 Warn("inconsistent number of custom target elements");
7224 #if ENABLE_HISTORIC_CHUNKS
7225 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7226 int num_changed_custom_elements)
7228 int i, j, x, y, check = 0;
7230 putFile16BitBE(file, num_changed_custom_elements);
7232 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7234 int element = EL_CUSTOM_START + i;
7235 struct ElementInfo *ei = &element_info[element];
7237 if (ei->modified_settings)
7239 if (check < num_changed_custom_elements)
7241 putFile16BitBE(file, element);
7243 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7244 putFile8Bit(file, ei->description[j]);
7246 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7248 // some free bytes for future properties and padding
7249 WriteUnusedBytesToFile(file, 7);
7251 putFile8Bit(file, ei->use_gfx_element);
7252 putFile16BitBE(file, ei->gfx_element_initial);
7254 putFile8Bit(file, ei->collect_score_initial);
7255 putFile8Bit(file, ei->collect_count_initial);
7257 putFile16BitBE(file, ei->push_delay_fixed);
7258 putFile16BitBE(file, ei->push_delay_random);
7259 putFile16BitBE(file, ei->move_delay_fixed);
7260 putFile16BitBE(file, ei->move_delay_random);
7262 putFile16BitBE(file, ei->move_pattern);
7263 putFile8Bit(file, ei->move_direction_initial);
7264 putFile8Bit(file, ei->move_stepsize);
7266 for (y = 0; y < 3; y++)
7267 for (x = 0; x < 3; x++)
7268 putFile16BitBE(file, ei->content.e[x][y]);
7270 putFile32BitBE(file, ei->change->events);
7272 putFile16BitBE(file, ei->change->target_element);
7274 putFile16BitBE(file, ei->change->delay_fixed);
7275 putFile16BitBE(file, ei->change->delay_random);
7276 putFile16BitBE(file, ei->change->delay_frames);
7278 putFile16BitBE(file, ei->change->initial_trigger_element);
7280 putFile8Bit(file, ei->change->explode);
7281 putFile8Bit(file, ei->change->use_target_content);
7282 putFile8Bit(file, ei->change->only_if_complete);
7283 putFile8Bit(file, ei->change->use_random_replace);
7285 putFile8Bit(file, ei->change->random_percentage);
7286 putFile8Bit(file, ei->change->replace_when);
7288 for (y = 0; y < 3; y++)
7289 for (x = 0; x < 3; x++)
7290 putFile16BitBE(file, ei->change->content.e[x][y]);
7292 putFile8Bit(file, ei->slippery_type);
7294 // some free bytes for future properties and padding
7295 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7302 if (check != num_changed_custom_elements) // should not happen
7303 Warn("inconsistent number of custom element properties");
7307 #if ENABLE_HISTORIC_CHUNKS
7308 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7310 struct ElementInfo *ei = &element_info[element];
7313 // ---------- custom element base property values (96 bytes) ----------------
7315 putFile16BitBE(file, element);
7317 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7318 putFile8Bit(file, ei->description[i]);
7320 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7322 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7324 putFile8Bit(file, ei->num_change_pages);
7326 putFile16BitBE(file, ei->ce_value_fixed_initial);
7327 putFile16BitBE(file, ei->ce_value_random_initial);
7328 putFile8Bit(file, ei->use_last_ce_value);
7330 putFile8Bit(file, ei->use_gfx_element);
7331 putFile16BitBE(file, ei->gfx_element_initial);
7333 putFile8Bit(file, ei->collect_score_initial);
7334 putFile8Bit(file, ei->collect_count_initial);
7336 putFile8Bit(file, ei->drop_delay_fixed);
7337 putFile8Bit(file, ei->push_delay_fixed);
7338 putFile8Bit(file, ei->drop_delay_random);
7339 putFile8Bit(file, ei->push_delay_random);
7340 putFile16BitBE(file, ei->move_delay_fixed);
7341 putFile16BitBE(file, ei->move_delay_random);
7343 // bits 0 - 15 of "move_pattern" ...
7344 putFile16BitBE(file, ei->move_pattern & 0xffff);
7345 putFile8Bit(file, ei->move_direction_initial);
7346 putFile8Bit(file, ei->move_stepsize);
7348 putFile8Bit(file, ei->slippery_type);
7350 for (y = 0; y < 3; y++)
7351 for (x = 0; x < 3; x++)
7352 putFile16BitBE(file, ei->content.e[x][y]);
7354 putFile16BitBE(file, ei->move_enter_element);
7355 putFile16BitBE(file, ei->move_leave_element);
7356 putFile8Bit(file, ei->move_leave_type);
7358 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7359 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7361 putFile8Bit(file, ei->access_direction);
7363 putFile8Bit(file, ei->explosion_delay);
7364 putFile8Bit(file, ei->ignition_delay);
7365 putFile8Bit(file, ei->explosion_type);
7367 // some free bytes for future custom property values and padding
7368 WriteUnusedBytesToFile(file, 1);
7370 // ---------- change page property values (48 bytes) ------------------------
7372 for (i = 0; i < ei->num_change_pages; i++)
7374 struct ElementChangeInfo *change = &ei->change_page[i];
7375 unsigned int event_bits;
7377 // bits 0 - 31 of "has_event[]" ...
7379 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7380 if (change->has_event[j])
7381 event_bits |= (1u << j);
7382 putFile32BitBE(file, event_bits);
7384 putFile16BitBE(file, change->target_element);
7386 putFile16BitBE(file, change->delay_fixed);
7387 putFile16BitBE(file, change->delay_random);
7388 putFile16BitBE(file, change->delay_frames);
7390 putFile16BitBE(file, change->initial_trigger_element);
7392 putFile8Bit(file, change->explode);
7393 putFile8Bit(file, change->use_target_content);
7394 putFile8Bit(file, change->only_if_complete);
7395 putFile8Bit(file, change->use_random_replace);
7397 putFile8Bit(file, change->random_percentage);
7398 putFile8Bit(file, change->replace_when);
7400 for (y = 0; y < 3; y++)
7401 for (x = 0; x < 3; x++)
7402 putFile16BitBE(file, change->target_content.e[x][y]);
7404 putFile8Bit(file, change->can_change);
7406 putFile8Bit(file, change->trigger_side);
7408 putFile8Bit(file, change->trigger_player);
7409 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7410 log_2(change->trigger_page)));
7412 putFile8Bit(file, change->has_action);
7413 putFile8Bit(file, change->action_type);
7414 putFile8Bit(file, change->action_mode);
7415 putFile16BitBE(file, change->action_arg);
7417 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7419 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7420 if (change->has_event[j])
7421 event_bits |= (1u << (j - 32));
7422 putFile8Bit(file, event_bits);
7427 #if ENABLE_HISTORIC_CHUNKS
7428 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7430 struct ElementInfo *ei = &element_info[element];
7431 struct ElementGroupInfo *group = ei->group;
7434 putFile16BitBE(file, element);
7436 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7437 putFile8Bit(file, ei->description[i]);
7439 putFile8Bit(file, group->num_elements);
7441 putFile8Bit(file, ei->use_gfx_element);
7442 putFile16BitBE(file, ei->gfx_element_initial);
7444 putFile8Bit(file, group->choice_mode);
7446 // some free bytes for future values and padding
7447 WriteUnusedBytesToFile(file, 3);
7449 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7450 putFile16BitBE(file, group->element[i]);
7454 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7455 boolean write_element)
7457 int save_type = entry->save_type;
7458 int data_type = entry->data_type;
7459 int conf_type = entry->conf_type;
7460 int byte_mask = conf_type & CONF_MASK_BYTES;
7461 int element = entry->element;
7462 int default_value = entry->default_value;
7464 boolean modified = FALSE;
7466 if (byte_mask != CONF_MASK_MULTI_BYTES)
7468 void *value_ptr = entry->value;
7469 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7472 // check if any settings have been modified before saving them
7473 if (value != default_value)
7476 // do not save if explicitly told or if unmodified default settings
7477 if ((save_type == SAVE_CONF_NEVER) ||
7478 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7482 num_bytes += putFile16BitBE(file, element);
7484 num_bytes += putFile8Bit(file, conf_type);
7485 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7486 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7487 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7490 else if (data_type == TYPE_STRING)
7492 char *default_string = entry->default_string;
7493 char *string = (char *)(entry->value);
7494 int string_length = strlen(string);
7497 // check if any settings have been modified before saving them
7498 if (!strEqual(string, default_string))
7501 // do not save if explicitly told or if unmodified default settings
7502 if ((save_type == SAVE_CONF_NEVER) ||
7503 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7507 num_bytes += putFile16BitBE(file, element);
7509 num_bytes += putFile8Bit(file, conf_type);
7510 num_bytes += putFile16BitBE(file, string_length);
7512 for (i = 0; i < string_length; i++)
7513 num_bytes += putFile8Bit(file, string[i]);
7515 else if (data_type == TYPE_ELEMENT_LIST)
7517 int *element_array = (int *)(entry->value);
7518 int num_elements = *(int *)(entry->num_entities);
7521 // check if any settings have been modified before saving them
7522 for (i = 0; i < num_elements; i++)
7523 if (element_array[i] != default_value)
7526 // do not save if explicitly told or if unmodified default settings
7527 if ((save_type == SAVE_CONF_NEVER) ||
7528 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7532 num_bytes += putFile16BitBE(file, element);
7534 num_bytes += putFile8Bit(file, conf_type);
7535 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7537 for (i = 0; i < num_elements; i++)
7538 num_bytes += putFile16BitBE(file, element_array[i]);
7540 else if (data_type == TYPE_CONTENT_LIST)
7542 struct Content *content = (struct Content *)(entry->value);
7543 int num_contents = *(int *)(entry->num_entities);
7546 // check if any settings have been modified before saving them
7547 for (i = 0; i < num_contents; i++)
7548 for (y = 0; y < 3; y++)
7549 for (x = 0; x < 3; x++)
7550 if (content[i].e[x][y] != default_value)
7553 // do not save if explicitly told or if unmodified default settings
7554 if ((save_type == SAVE_CONF_NEVER) ||
7555 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7559 num_bytes += putFile16BitBE(file, element);
7561 num_bytes += putFile8Bit(file, conf_type);
7562 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7564 for (i = 0; i < num_contents; i++)
7565 for (y = 0; y < 3; y++)
7566 for (x = 0; x < 3; x++)
7567 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7573 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7578 li = *level; // copy level data into temporary buffer
7580 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7581 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7586 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7591 li = *level; // copy level data into temporary buffer
7593 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7594 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7599 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7601 int envelope_nr = element - EL_ENVELOPE_1;
7605 chunk_size += putFile16BitBE(file, element);
7607 // copy envelope data into temporary buffer
7608 xx_envelope = level->envelope[envelope_nr];
7610 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7611 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7616 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7618 struct ElementInfo *ei = &element_info[element];
7622 chunk_size += putFile16BitBE(file, element);
7624 xx_ei = *ei; // copy element data into temporary buffer
7626 // set default description string for this specific element
7627 strcpy(xx_default_description, getDefaultElementDescription(ei));
7629 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7630 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7632 for (i = 0; i < ei->num_change_pages; i++)
7634 struct ElementChangeInfo *change = &ei->change_page[i];
7636 xx_current_change_page = i;
7638 xx_change = *change; // copy change data into temporary buffer
7641 setEventBitsFromEventFlags(change);
7643 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7644 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7651 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7653 struct ElementInfo *ei = &element_info[element];
7654 struct ElementGroupInfo *group = ei->group;
7658 chunk_size += putFile16BitBE(file, element);
7660 xx_ei = *ei; // copy element data into temporary buffer
7661 xx_group = *group; // copy group data into temporary buffer
7663 // set default description string for this specific element
7664 strcpy(xx_default_description, getDefaultElementDescription(ei));
7666 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7667 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7672 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7674 struct ElementInfo *ei = &element_info[element];
7678 chunk_size += putFile16BitBE(file, element);
7680 xx_ei = *ei; // copy element data into temporary buffer
7682 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7683 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7688 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7689 boolean save_as_template)
7695 if (!(file = fopen(filename, MODE_WRITE)))
7697 Warn("cannot save level file '%s'", filename);
7702 level->file_version = FILE_VERSION_ACTUAL;
7703 level->game_version = GAME_VERSION_ACTUAL;
7705 level->creation_date = getCurrentDate();
7707 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7708 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7710 chunk_size = SaveLevel_VERS(NULL, level);
7711 putFileChunkBE(file, "VERS", chunk_size);
7712 SaveLevel_VERS(file, level);
7714 chunk_size = SaveLevel_DATE(NULL, level);
7715 putFileChunkBE(file, "DATE", chunk_size);
7716 SaveLevel_DATE(file, level);
7718 chunk_size = SaveLevel_NAME(NULL, level);
7719 putFileChunkBE(file, "NAME", chunk_size);
7720 SaveLevel_NAME(file, level);
7722 chunk_size = SaveLevel_AUTH(NULL, level);
7723 putFileChunkBE(file, "AUTH", chunk_size);
7724 SaveLevel_AUTH(file, level);
7726 chunk_size = SaveLevel_INFO(NULL, level);
7727 putFileChunkBE(file, "INFO", chunk_size);
7728 SaveLevel_INFO(file, level);
7730 chunk_size = SaveLevel_BODY(NULL, level);
7731 putFileChunkBE(file, "BODY", chunk_size);
7732 SaveLevel_BODY(file, level);
7734 chunk_size = SaveLevel_ELEM(NULL, level);
7735 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7737 putFileChunkBE(file, "ELEM", chunk_size);
7738 SaveLevel_ELEM(file, level);
7741 for (i = 0; i < NUM_ENVELOPES; i++)
7743 int element = EL_ENVELOPE_1 + i;
7745 chunk_size = SaveLevel_NOTE(NULL, level, element);
7746 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7748 putFileChunkBE(file, "NOTE", chunk_size);
7749 SaveLevel_NOTE(file, level, element);
7753 // if not using template level, check for non-default custom/group elements
7754 if (!level->use_custom_template || save_as_template)
7756 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7758 int element = EL_CUSTOM_START + i;
7760 chunk_size = SaveLevel_CUSX(NULL, level, element);
7761 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7763 putFileChunkBE(file, "CUSX", chunk_size);
7764 SaveLevel_CUSX(file, level, element);
7768 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7770 int element = EL_GROUP_START + i;
7772 chunk_size = SaveLevel_GRPX(NULL, level, element);
7773 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7775 putFileChunkBE(file, "GRPX", chunk_size);
7776 SaveLevel_GRPX(file, level, element);
7780 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7782 int element = GET_EMPTY_ELEMENT(i);
7784 chunk_size = SaveLevel_EMPX(NULL, level, element);
7785 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7787 putFileChunkBE(file, "EMPX", chunk_size);
7788 SaveLevel_EMPX(file, level, element);
7795 SetFilePermissions(filename, PERMS_PRIVATE);
7798 void SaveLevel(int nr)
7800 char *filename = getDefaultLevelFilename(nr);
7802 SaveLevelFromFilename(&level, filename, FALSE);
7805 void SaveLevelTemplate(void)
7807 char *filename = getLocalLevelTemplateFilename();
7809 SaveLevelFromFilename(&level, filename, TRUE);
7812 boolean SaveLevelChecked(int nr)
7814 char *filename = getDefaultLevelFilename(nr);
7815 boolean new_level = !fileExists(filename);
7816 boolean level_saved = FALSE;
7818 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7823 Request("Level saved!", REQ_CONFIRM);
7831 void DumpLevel(struct LevelInfo *level)
7833 if (level->no_level_file || level->no_valid_file)
7835 Warn("cannot dump -- no valid level file found");
7841 Print("Level xxx (file version %08d, game version %08d)\n",
7842 level->file_version, level->game_version);
7845 Print("Level author: '%s'\n", level->author);
7846 Print("Level title: '%s'\n", level->name);
7848 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7850 Print("Level time: %d seconds\n", level->time);
7851 Print("Gems needed: %d\n", level->gems_needed);
7853 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7854 Print("Time for wheel: %d seconds\n", level->time_wheel);
7855 Print("Time for light: %d seconds\n", level->time_light);
7856 Print("Time for timegate: %d seconds\n", level->time_timegate);
7858 Print("Amoeba speed: %d\n", level->amoeba_speed);
7861 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7862 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7863 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7864 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7865 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7866 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7872 for (i = 0; i < NUM_ENVELOPES; i++)
7874 char *text = level->envelope[i].text;
7875 int text_len = strlen(text);
7876 boolean has_text = FALSE;
7878 for (j = 0; j < text_len; j++)
7879 if (text[j] != ' ' && text[j] != '\n')
7885 Print("Envelope %d:\n'%s'\n", i + 1, text);
7893 void DumpLevels(void)
7895 static LevelDirTree *dumplevel_leveldir = NULL;
7897 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7898 global.dumplevel_leveldir);
7900 if (dumplevel_leveldir == NULL)
7901 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7903 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7904 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7905 Fail("no such level number: %d", global.dumplevel_level_nr);
7907 leveldir_current = dumplevel_leveldir;
7909 LoadLevel(global.dumplevel_level_nr);
7916 // ============================================================================
7917 // tape file functions
7918 // ============================================================================
7920 static void setTapeInfoToDefaults(void)
7924 // always start with reliable default values (empty tape)
7927 // default values (also for pre-1.2 tapes) with only the first player
7928 tape.player_participates[0] = TRUE;
7929 for (i = 1; i < MAX_PLAYERS; i++)
7930 tape.player_participates[i] = FALSE;
7932 // at least one (default: the first) player participates in every tape
7933 tape.num_participating_players = 1;
7935 tape.property_bits = TAPE_PROPERTY_NONE;
7937 tape.level_nr = level_nr;
7939 tape.changed = FALSE;
7940 tape.solved = FALSE;
7942 tape.recording = FALSE;
7943 tape.playing = FALSE;
7944 tape.pausing = FALSE;
7946 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7947 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7949 tape.no_info_chunk = TRUE;
7950 tape.no_valid_file = FALSE;
7953 static int getTapePosSize(struct TapeInfo *tape)
7955 int tape_pos_size = 0;
7957 if (tape->use_key_actions)
7958 tape_pos_size += tape->num_participating_players;
7960 if (tape->use_mouse_actions)
7961 tape_pos_size += 3; // x and y position and mouse button mask
7963 tape_pos_size += 1; // tape action delay value
7965 return tape_pos_size;
7968 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7970 tape->use_key_actions = FALSE;
7971 tape->use_mouse_actions = FALSE;
7973 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7974 tape->use_key_actions = TRUE;
7976 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7977 tape->use_mouse_actions = TRUE;
7980 static int getTapeActionValue(struct TapeInfo *tape)
7982 return (tape->use_key_actions &&
7983 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7984 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7985 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7986 TAPE_ACTIONS_DEFAULT);
7989 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7991 tape->file_version = getFileVersion(file);
7992 tape->game_version = getFileVersion(file);
7997 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8001 tape->random_seed = getFile32BitBE(file);
8002 tape->date = getFile32BitBE(file);
8003 tape->length = getFile32BitBE(file);
8005 // read header fields that are new since version 1.2
8006 if (tape->file_version >= FILE_VERSION_1_2)
8008 byte store_participating_players = getFile8Bit(file);
8011 // since version 1.2, tapes store which players participate in the tape
8012 tape->num_participating_players = 0;
8013 for (i = 0; i < MAX_PLAYERS; i++)
8015 tape->player_participates[i] = FALSE;
8017 if (store_participating_players & (1 << i))
8019 tape->player_participates[i] = TRUE;
8020 tape->num_participating_players++;
8024 setTapeActionFlags(tape, getFile8Bit(file));
8026 tape->property_bits = getFile8Bit(file);
8027 tape->solved = getFile8Bit(file);
8029 engine_version = getFileVersion(file);
8030 if (engine_version > 0)
8031 tape->engine_version = engine_version;
8033 tape->engine_version = tape->game_version;
8039 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8041 tape->scr_fieldx = getFile8Bit(file);
8042 tape->scr_fieldy = getFile8Bit(file);
8047 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8049 char *level_identifier = NULL;
8050 int level_identifier_size;
8053 tape->no_info_chunk = FALSE;
8055 level_identifier_size = getFile16BitBE(file);
8057 level_identifier = checked_malloc(level_identifier_size);
8059 for (i = 0; i < level_identifier_size; i++)
8060 level_identifier[i] = getFile8Bit(file);
8062 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8063 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8065 checked_free(level_identifier);
8067 tape->level_nr = getFile16BitBE(file);
8069 chunk_size = 2 + level_identifier_size + 2;
8074 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8077 int tape_pos_size = getTapePosSize(tape);
8078 int chunk_size_expected = tape_pos_size * tape->length;
8080 if (chunk_size_expected != chunk_size)
8082 ReadUnusedBytesFromFile(file, chunk_size);
8083 return chunk_size_expected;
8086 for (i = 0; i < tape->length; i++)
8088 if (i >= MAX_TAPE_LEN)
8090 Warn("tape truncated -- size exceeds maximum tape size %d",
8093 // tape too large; read and ignore remaining tape data from this chunk
8094 for (;i < tape->length; i++)
8095 ReadUnusedBytesFromFile(file, tape_pos_size);
8100 if (tape->use_key_actions)
8102 for (j = 0; j < MAX_PLAYERS; j++)
8104 tape->pos[i].action[j] = MV_NONE;
8106 if (tape->player_participates[j])
8107 tape->pos[i].action[j] = getFile8Bit(file);
8111 if (tape->use_mouse_actions)
8113 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8114 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8115 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8118 tape->pos[i].delay = getFile8Bit(file);
8120 if (tape->file_version == FILE_VERSION_1_0)
8122 // eliminate possible diagonal moves in old tapes
8123 // this is only for backward compatibility
8125 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8126 byte action = tape->pos[i].action[0];
8127 int k, num_moves = 0;
8129 for (k = 0; k<4; k++)
8131 if (action & joy_dir[k])
8133 tape->pos[i + num_moves].action[0] = joy_dir[k];
8135 tape->pos[i + num_moves].delay = 0;
8144 tape->length += num_moves;
8147 else if (tape->file_version < FILE_VERSION_2_0)
8149 // convert pre-2.0 tapes to new tape format
8151 if (tape->pos[i].delay > 1)
8154 tape->pos[i + 1] = tape->pos[i];
8155 tape->pos[i + 1].delay = 1;
8158 for (j = 0; j < MAX_PLAYERS; j++)
8159 tape->pos[i].action[j] = MV_NONE;
8160 tape->pos[i].delay--;
8167 if (checkEndOfFile(file))
8171 if (i != tape->length)
8172 chunk_size = tape_pos_size * i;
8177 static void LoadTape_SokobanSolution(char *filename)
8180 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8182 if (!(file = openFile(filename, MODE_READ)))
8184 tape.no_valid_file = TRUE;
8189 while (!checkEndOfFile(file))
8191 unsigned char c = getByteFromFile(file);
8193 if (checkEndOfFile(file))
8200 tape.pos[tape.length].action[0] = MV_UP;
8201 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8207 tape.pos[tape.length].action[0] = MV_DOWN;
8208 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8214 tape.pos[tape.length].action[0] = MV_LEFT;
8215 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8221 tape.pos[tape.length].action[0] = MV_RIGHT;
8222 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8230 // ignore white-space characters
8234 tape.no_valid_file = TRUE;
8236 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8244 if (tape.no_valid_file)
8247 tape.length_frames = GetTapeLengthFrames();
8248 tape.length_seconds = GetTapeLengthSeconds();
8251 void LoadTapeFromFilename(char *filename)
8253 char cookie[MAX_LINE_LEN];
8254 char chunk_name[CHUNK_ID_LEN + 1];
8258 // always start with reliable default values
8259 setTapeInfoToDefaults();
8261 if (strSuffix(filename, ".sln"))
8263 LoadTape_SokobanSolution(filename);
8268 if (!(file = openFile(filename, MODE_READ)))
8270 tape.no_valid_file = TRUE;
8275 getFileChunkBE(file, chunk_name, NULL);
8276 if (strEqual(chunk_name, "RND1"))
8278 getFile32BitBE(file); // not used
8280 getFileChunkBE(file, chunk_name, NULL);
8281 if (!strEqual(chunk_name, "TAPE"))
8283 tape.no_valid_file = TRUE;
8285 Warn("unknown format of tape file '%s'", filename);
8292 else // check for pre-2.0 file format with cookie string
8294 strcpy(cookie, chunk_name);
8295 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8297 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8298 cookie[strlen(cookie) - 1] = '\0';
8300 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8302 tape.no_valid_file = TRUE;
8304 Warn("unknown format of tape file '%s'", filename);
8311 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8313 tape.no_valid_file = TRUE;
8315 Warn("unsupported version of tape file '%s'", filename);
8322 // pre-2.0 tape files have no game version, so use file version here
8323 tape.game_version = tape.file_version;
8326 if (tape.file_version < FILE_VERSION_1_2)
8328 // tape files from versions before 1.2.0 without chunk structure
8329 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8330 LoadTape_BODY(file, 2 * tape.length, &tape);
8338 int (*loader)(File *, int, struct TapeInfo *);
8342 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8343 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8344 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8345 { "INFO", -1, LoadTape_INFO },
8346 { "BODY", -1, LoadTape_BODY },
8350 while (getFileChunkBE(file, chunk_name, &chunk_size))
8354 while (chunk_info[i].name != NULL &&
8355 !strEqual(chunk_name, chunk_info[i].name))
8358 if (chunk_info[i].name == NULL)
8360 Warn("unknown chunk '%s' in tape file '%s'",
8361 chunk_name, filename);
8363 ReadUnusedBytesFromFile(file, chunk_size);
8365 else if (chunk_info[i].size != -1 &&
8366 chunk_info[i].size != chunk_size)
8368 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8369 chunk_size, chunk_name, filename);
8371 ReadUnusedBytesFromFile(file, chunk_size);
8375 // call function to load this tape chunk
8376 int chunk_size_expected =
8377 (chunk_info[i].loader)(file, chunk_size, &tape);
8379 // the size of some chunks cannot be checked before reading other
8380 // chunks first (like "HEAD" and "BODY") that contain some header
8381 // information, so check them here
8382 if (chunk_size_expected != chunk_size)
8384 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8385 chunk_size, chunk_name, filename);
8393 tape.length_frames = GetTapeLengthFrames();
8394 tape.length_seconds = GetTapeLengthSeconds();
8397 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8399 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8401 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8402 tape.engine_version);
8406 void LoadTape(int nr)
8408 char *filename = getTapeFilename(nr);
8410 LoadTapeFromFilename(filename);
8413 void LoadSolutionTape(int nr)
8415 char *filename = getSolutionTapeFilename(nr);
8417 LoadTapeFromFilename(filename);
8419 if (TAPE_IS_EMPTY(tape) &&
8420 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8421 level.native_sp_level->demo.is_available)
8422 CopyNativeTape_SP_to_RND(&level);
8425 void LoadScoreTape(char *score_tape_basename, int nr)
8427 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8429 LoadTapeFromFilename(filename);
8432 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8434 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8436 LoadTapeFromFilename(filename);
8439 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8441 // chunk required for team mode tapes with non-default screen size
8442 return (tape->num_participating_players > 1 &&
8443 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8444 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8447 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8449 putFileVersion(file, tape->file_version);
8450 putFileVersion(file, tape->game_version);
8453 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8456 byte store_participating_players = 0;
8458 // set bits for participating players for compact storage
8459 for (i = 0; i < MAX_PLAYERS; i++)
8460 if (tape->player_participates[i])
8461 store_participating_players |= (1 << i);
8463 putFile32BitBE(file, tape->random_seed);
8464 putFile32BitBE(file, tape->date);
8465 putFile32BitBE(file, tape->length);
8467 putFile8Bit(file, store_participating_players);
8469 putFile8Bit(file, getTapeActionValue(tape));
8471 putFile8Bit(file, tape->property_bits);
8472 putFile8Bit(file, tape->solved);
8474 putFileVersion(file, tape->engine_version);
8477 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8479 putFile8Bit(file, tape->scr_fieldx);
8480 putFile8Bit(file, tape->scr_fieldy);
8483 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8485 int level_identifier_size = strlen(tape->level_identifier) + 1;
8488 putFile16BitBE(file, level_identifier_size);
8490 for (i = 0; i < level_identifier_size; i++)
8491 putFile8Bit(file, tape->level_identifier[i]);
8493 putFile16BitBE(file, tape->level_nr);
8496 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8500 for (i = 0; i < tape->length; i++)
8502 if (tape->use_key_actions)
8504 for (j = 0; j < MAX_PLAYERS; j++)
8505 if (tape->player_participates[j])
8506 putFile8Bit(file, tape->pos[i].action[j]);
8509 if (tape->use_mouse_actions)
8511 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8512 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8513 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8516 putFile8Bit(file, tape->pos[i].delay);
8520 void SaveTapeToFilename(char *filename)
8524 int info_chunk_size;
8525 int body_chunk_size;
8527 if (!(file = fopen(filename, MODE_WRITE)))
8529 Warn("cannot save level recording file '%s'", filename);
8534 tape_pos_size = getTapePosSize(&tape);
8536 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8537 body_chunk_size = tape_pos_size * tape.length;
8539 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8540 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8542 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8543 SaveTape_VERS(file, &tape);
8545 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8546 SaveTape_HEAD(file, &tape);
8548 if (checkSaveTape_SCRN(&tape))
8550 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8551 SaveTape_SCRN(file, &tape);
8554 putFileChunkBE(file, "INFO", info_chunk_size);
8555 SaveTape_INFO(file, &tape);
8557 putFileChunkBE(file, "BODY", body_chunk_size);
8558 SaveTape_BODY(file, &tape);
8562 SetFilePermissions(filename, PERMS_PRIVATE);
8565 static void SaveTapeExt(char *filename)
8569 tape.file_version = FILE_VERSION_ACTUAL;
8570 tape.game_version = GAME_VERSION_ACTUAL;
8572 tape.num_participating_players = 0;
8574 // count number of participating players
8575 for (i = 0; i < MAX_PLAYERS; i++)
8576 if (tape.player_participates[i])
8577 tape.num_participating_players++;
8579 SaveTapeToFilename(filename);
8581 tape.changed = FALSE;
8584 void SaveTape(int nr)
8586 char *filename = getTapeFilename(nr);
8588 InitTapeDirectory(leveldir_current->subdir);
8590 SaveTapeExt(filename);
8593 void SaveScoreTape(int nr)
8595 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8597 // used instead of "leveldir_current->subdir" (for network games)
8598 InitScoreTapeDirectory(levelset.identifier, nr);
8600 SaveTapeExt(filename);
8603 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8604 unsigned int req_state_added)
8606 char *filename = getTapeFilename(nr);
8607 boolean new_tape = !fileExists(filename);
8608 boolean tape_saved = FALSE;
8610 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8615 Request(msg_saved, REQ_CONFIRM | req_state_added);
8623 boolean SaveTapeChecked(int nr)
8625 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8628 boolean SaveTapeChecked_LevelSolved(int nr)
8630 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8631 "Level solved! Tape saved!", REQ_STAY_OPEN);
8634 void DumpTape(struct TapeInfo *tape)
8636 int tape_frame_counter;
8639 if (tape->no_valid_file)
8641 Warn("cannot dump -- no valid tape file found");
8648 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8649 tape->level_nr, tape->file_version, tape->game_version);
8650 Print(" (effective engine version %08d)\n",
8651 tape->engine_version);
8652 Print("Level series identifier: '%s'\n", tape->level_identifier);
8654 Print("Solution tape: %s\n",
8655 tape->solved ? "yes" :
8656 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8658 Print("Special tape properties: ");
8659 if (tape->property_bits == TAPE_PROPERTY_NONE)
8661 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8662 Print("[em_random_bug]");
8663 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8664 Print("[game_speed]");
8665 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8667 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8668 Print("[single_step]");
8669 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8670 Print("[snapshot]");
8671 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8672 Print("[replayed]");
8673 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8674 Print("[tas_keys]");
8675 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8676 Print("[small_graphics]");
8679 int year2 = tape->date / 10000;
8680 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8681 int month_index_raw = (tape->date / 100) % 100;
8682 int month_index = month_index_raw % 12; // prevent invalid index
8683 int month = month_index + 1;
8684 int day = tape->date % 100;
8686 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8690 tape_frame_counter = 0;
8692 for (i = 0; i < tape->length; i++)
8694 if (i >= MAX_TAPE_LEN)
8699 for (j = 0; j < MAX_PLAYERS; j++)
8701 if (tape->player_participates[j])
8703 int action = tape->pos[i].action[j];
8705 Print("%d:%02x ", j, action);
8706 Print("[%c%c%c%c|%c%c] - ",
8707 (action & JOY_LEFT ? '<' : ' '),
8708 (action & JOY_RIGHT ? '>' : ' '),
8709 (action & JOY_UP ? '^' : ' '),
8710 (action & JOY_DOWN ? 'v' : ' '),
8711 (action & JOY_BUTTON_1 ? '1' : ' '),
8712 (action & JOY_BUTTON_2 ? '2' : ' '));
8716 Print("(%03d) ", tape->pos[i].delay);
8717 Print("[%05d]\n", tape_frame_counter);
8719 tape_frame_counter += tape->pos[i].delay;
8725 void DumpTapes(void)
8727 static LevelDirTree *dumptape_leveldir = NULL;
8729 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8730 global.dumptape_leveldir);
8732 if (dumptape_leveldir == NULL)
8733 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8735 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8736 global.dumptape_level_nr > dumptape_leveldir->last_level)
8737 Fail("no such level number: %d", global.dumptape_level_nr);
8739 leveldir_current = dumptape_leveldir;
8741 if (options.mytapes)
8742 LoadTape(global.dumptape_level_nr);
8744 LoadSolutionTape(global.dumptape_level_nr);
8752 // ============================================================================
8753 // score file functions
8754 // ============================================================================
8756 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8760 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8762 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8763 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8764 scores->entry[i].score = 0;
8765 scores->entry[i].time = 0;
8767 scores->entry[i].id = -1;
8768 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8769 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8770 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8771 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8772 strcpy(scores->entry[i].country_code, "??");
8775 scores->num_entries = 0;
8776 scores->last_added = -1;
8777 scores->last_added_local = -1;
8779 scores->updated = FALSE;
8780 scores->uploaded = FALSE;
8781 scores->tape_downloaded = FALSE;
8782 scores->force_last_added = FALSE;
8784 // The following values are intentionally not reset here:
8788 // - continue_playing
8789 // - continue_on_return
8792 static void setScoreInfoToDefaults(void)
8794 setScoreInfoToDefaultsExt(&scores);
8797 static void setServerScoreInfoToDefaults(void)
8799 setScoreInfoToDefaultsExt(&server_scores);
8802 static void LoadScore_OLD(int nr)
8805 char *filename = getScoreFilename(nr);
8806 char cookie[MAX_LINE_LEN];
8807 char line[MAX_LINE_LEN];
8811 if (!(file = fopen(filename, MODE_READ)))
8814 // check file identifier
8815 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8817 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8818 cookie[strlen(cookie) - 1] = '\0';
8820 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8822 Warn("unknown format of score file '%s'", filename);
8829 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8831 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8832 Warn("fscanf() failed; %s", strerror(errno));
8834 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8837 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8838 line[strlen(line) - 1] = '\0';
8840 for (line_ptr = line; *line_ptr; line_ptr++)
8842 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8844 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8845 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8854 static void ConvertScore_OLD(void)
8856 // only convert score to time for levels that rate playing time over score
8857 if (!level.rate_time_over_score)
8860 // convert old score to playing time for score-less levels (like Supaplex)
8861 int time_final_max = 999;
8864 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8866 int score = scores.entry[i].score;
8868 if (score > 0 && score < time_final_max)
8869 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8873 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8875 scores->file_version = getFileVersion(file);
8876 scores->game_version = getFileVersion(file);
8881 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8883 char *level_identifier = NULL;
8884 int level_identifier_size;
8887 level_identifier_size = getFile16BitBE(file);
8889 level_identifier = checked_malloc(level_identifier_size);
8891 for (i = 0; i < level_identifier_size; i++)
8892 level_identifier[i] = getFile8Bit(file);
8894 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8895 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8897 checked_free(level_identifier);
8899 scores->level_nr = getFile16BitBE(file);
8900 scores->num_entries = getFile16BitBE(file);
8902 chunk_size = 2 + level_identifier_size + 2 + 2;
8907 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8911 for (i = 0; i < scores->num_entries; i++)
8913 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8914 scores->entry[i].name[j] = getFile8Bit(file);
8916 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8919 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8924 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8928 for (i = 0; i < scores->num_entries; i++)
8929 scores->entry[i].score = getFile16BitBE(file);
8931 chunk_size = scores->num_entries * 2;
8936 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8940 for (i = 0; i < scores->num_entries; i++)
8941 scores->entry[i].score = getFile32BitBE(file);
8943 chunk_size = scores->num_entries * 4;
8948 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8952 for (i = 0; i < scores->num_entries; i++)
8953 scores->entry[i].time = getFile32BitBE(file);
8955 chunk_size = scores->num_entries * 4;
8960 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8964 for (i = 0; i < scores->num_entries; i++)
8966 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8967 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8969 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8972 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8977 void LoadScore(int nr)
8979 char *filename = getScoreFilename(nr);
8980 char cookie[MAX_LINE_LEN];
8981 char chunk_name[CHUNK_ID_LEN + 1];
8983 boolean old_score_file_format = FALSE;
8986 // always start with reliable default values
8987 setScoreInfoToDefaults();
8989 if (!(file = openFile(filename, MODE_READ)))
8992 getFileChunkBE(file, chunk_name, NULL);
8993 if (strEqual(chunk_name, "RND1"))
8995 getFile32BitBE(file); // not used
8997 getFileChunkBE(file, chunk_name, NULL);
8998 if (!strEqual(chunk_name, "SCOR"))
9000 Warn("unknown format of score file '%s'", filename);
9007 else // check for old file format with cookie string
9009 strcpy(cookie, chunk_name);
9010 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9012 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9013 cookie[strlen(cookie) - 1] = '\0';
9015 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9017 Warn("unknown format of score file '%s'", filename);
9024 old_score_file_format = TRUE;
9027 if (old_score_file_format)
9029 // score files from versions before 4.2.4.0 without chunk structure
9032 // convert score to time, if possible (mainly for Supaplex levels)
9041 int (*loader)(File *, int, struct ScoreInfo *);
9045 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9046 { "INFO", -1, LoadScore_INFO },
9047 { "NAME", -1, LoadScore_NAME },
9048 { "SCOR", -1, LoadScore_SCOR },
9049 { "SC4R", -1, LoadScore_SC4R },
9050 { "TIME", -1, LoadScore_TIME },
9051 { "TAPE", -1, LoadScore_TAPE },
9056 while (getFileChunkBE(file, chunk_name, &chunk_size))
9060 while (chunk_info[i].name != NULL &&
9061 !strEqual(chunk_name, chunk_info[i].name))
9064 if (chunk_info[i].name == NULL)
9066 Warn("unknown chunk '%s' in score file '%s'",
9067 chunk_name, filename);
9069 ReadUnusedBytesFromFile(file, chunk_size);
9071 else if (chunk_info[i].size != -1 &&
9072 chunk_info[i].size != chunk_size)
9074 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9075 chunk_size, chunk_name, filename);
9077 ReadUnusedBytesFromFile(file, chunk_size);
9081 // call function to load this score chunk
9082 int chunk_size_expected =
9083 (chunk_info[i].loader)(file, chunk_size, &scores);
9085 // the size of some chunks cannot be checked before reading other
9086 // chunks first (like "HEAD" and "BODY") that contain some header
9087 // information, so check them here
9088 if (chunk_size_expected != chunk_size)
9090 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9091 chunk_size, chunk_name, filename);
9100 #if ENABLE_HISTORIC_CHUNKS
9101 void SaveScore_OLD(int nr)
9104 char *filename = getScoreFilename(nr);
9107 // used instead of "leveldir_current->subdir" (for network games)
9108 InitScoreDirectory(levelset.identifier);
9110 if (!(file = fopen(filename, MODE_WRITE)))
9112 Warn("cannot save score for level %d", nr);
9117 fprintf(file, "%s\n\n", SCORE_COOKIE);
9119 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9120 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9124 SetFilePermissions(filename, PERMS_PRIVATE);
9128 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9130 putFileVersion(file, scores->file_version);
9131 putFileVersion(file, scores->game_version);
9134 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9136 int level_identifier_size = strlen(scores->level_identifier) + 1;
9139 putFile16BitBE(file, level_identifier_size);
9141 for (i = 0; i < level_identifier_size; i++)
9142 putFile8Bit(file, scores->level_identifier[i]);
9144 putFile16BitBE(file, scores->level_nr);
9145 putFile16BitBE(file, scores->num_entries);
9148 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9152 for (i = 0; i < scores->num_entries; i++)
9154 int name_size = strlen(scores->entry[i].name);
9156 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9157 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9161 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9165 for (i = 0; i < scores->num_entries; i++)
9166 putFile16BitBE(file, scores->entry[i].score);
9169 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9173 for (i = 0; i < scores->num_entries; i++)
9174 putFile32BitBE(file, scores->entry[i].score);
9177 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9181 for (i = 0; i < scores->num_entries; i++)
9182 putFile32BitBE(file, scores->entry[i].time);
9185 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9189 for (i = 0; i < scores->num_entries; i++)
9191 int size = strlen(scores->entry[i].tape_basename);
9193 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9194 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9198 static void SaveScoreToFilename(char *filename)
9201 int info_chunk_size;
9202 int name_chunk_size;
9203 int scor_chunk_size;
9204 int sc4r_chunk_size;
9205 int time_chunk_size;
9206 int tape_chunk_size;
9207 boolean has_large_score_values;
9210 if (!(file = fopen(filename, MODE_WRITE)))
9212 Warn("cannot save score file '%s'", filename);
9217 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9218 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9219 scor_chunk_size = scores.num_entries * 2;
9220 sc4r_chunk_size = scores.num_entries * 4;
9221 time_chunk_size = scores.num_entries * 4;
9222 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9224 has_large_score_values = FALSE;
9225 for (i = 0; i < scores.num_entries; i++)
9226 if (scores.entry[i].score > 0xffff)
9227 has_large_score_values = TRUE;
9229 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9230 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9232 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9233 SaveScore_VERS(file, &scores);
9235 putFileChunkBE(file, "INFO", info_chunk_size);
9236 SaveScore_INFO(file, &scores);
9238 putFileChunkBE(file, "NAME", name_chunk_size);
9239 SaveScore_NAME(file, &scores);
9241 if (has_large_score_values)
9243 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9244 SaveScore_SC4R(file, &scores);
9248 putFileChunkBE(file, "SCOR", scor_chunk_size);
9249 SaveScore_SCOR(file, &scores);
9252 putFileChunkBE(file, "TIME", time_chunk_size);
9253 SaveScore_TIME(file, &scores);
9255 putFileChunkBE(file, "TAPE", tape_chunk_size);
9256 SaveScore_TAPE(file, &scores);
9260 SetFilePermissions(filename, PERMS_PRIVATE);
9263 void SaveScore(int nr)
9265 char *filename = getScoreFilename(nr);
9268 // used instead of "leveldir_current->subdir" (for network games)
9269 InitScoreDirectory(levelset.identifier);
9271 scores.file_version = FILE_VERSION_ACTUAL;
9272 scores.game_version = GAME_VERSION_ACTUAL;
9274 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9275 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9276 scores.level_nr = level_nr;
9278 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9279 if (scores.entry[i].score == 0 &&
9280 scores.entry[i].time == 0 &&
9281 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9284 scores.num_entries = i;
9286 if (scores.num_entries == 0)
9289 SaveScoreToFilename(filename);
9292 static void LoadServerScoreFromCache(int nr)
9294 struct ScoreEntry score_entry;
9303 { &score_entry.score, FALSE, 0 },
9304 { &score_entry.time, FALSE, 0 },
9305 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9306 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9307 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9308 { &score_entry.id, FALSE, 0 },
9309 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9310 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9311 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9312 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9316 char *filename = getScoreCacheFilename(nr);
9317 SetupFileHash *score_hash = loadSetupFileHash(filename);
9320 server_scores.num_entries = 0;
9322 if (score_hash == NULL)
9325 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9327 score_entry = server_scores.entry[i];
9329 for (j = 0; score_mapping[j].value != NULL; j++)
9333 sprintf(token, "%02d.%d", i, j);
9335 char *value = getHashEntry(score_hash, token);
9340 if (score_mapping[j].is_string)
9342 char *score_value = (char *)score_mapping[j].value;
9343 int value_size = score_mapping[j].string_size;
9345 strncpy(score_value, value, value_size);
9346 score_value[value_size] = '\0';
9350 int *score_value = (int *)score_mapping[j].value;
9352 *score_value = atoi(value);
9355 server_scores.num_entries = i + 1;
9358 server_scores.entry[i] = score_entry;
9361 freeSetupFileHash(score_hash);
9364 void LoadServerScore(int nr, boolean download_score)
9366 if (!setup.use_api_server)
9369 // always start with reliable default values
9370 setServerScoreInfoToDefaults();
9372 // 1st step: load server scores from cache file (which may not exist)
9373 // (this should prevent reading it while the thread is writing to it)
9374 LoadServerScoreFromCache(nr);
9376 if (download_score && runtime.use_api_server)
9378 // 2nd step: download server scores from score server to cache file
9379 // (as thread, as it might time out if the server is not reachable)
9380 ApiGetScoreAsThread(nr);
9384 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9386 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9388 // if score tape not uploaded, ask for uploading missing tapes later
9389 if (!setup.has_remaining_tapes)
9390 setup.ask_for_remaining_tapes = TRUE;
9392 setup.provide_uploading_tapes = TRUE;
9393 setup.has_remaining_tapes = TRUE;
9395 SaveSetup_ServerSetup();
9398 void SaveServerScore(int nr, boolean tape_saved)
9400 if (!runtime.use_api_server)
9402 PrepareScoreTapesForUpload(leveldir_current->subdir);
9407 ApiAddScoreAsThread(nr, tape_saved, NULL);
9410 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9411 char *score_tape_filename)
9413 if (!runtime.use_api_server)
9416 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9419 void LoadLocalAndServerScore(int nr, boolean download_score)
9421 int last_added_local = scores.last_added_local;
9422 boolean force_last_added = scores.force_last_added;
9424 // needed if only showing server scores
9425 setScoreInfoToDefaults();
9427 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9430 // restore last added local score entry (before merging server scores)
9431 scores.last_added = scores.last_added_local = last_added_local;
9433 if (setup.use_api_server &&
9434 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9436 // load server scores from cache file and trigger update from server
9437 LoadServerScore(nr, download_score);
9439 // merge local scores with scores from server
9443 if (force_last_added)
9444 scores.force_last_added = force_last_added;
9448 // ============================================================================
9449 // setup file functions
9450 // ============================================================================
9452 #define TOKEN_STR_PLAYER_PREFIX "player_"
9455 static struct TokenInfo global_setup_tokens[] =
9459 &setup.player_name, "player_name"
9463 &setup.multiple_users, "multiple_users"
9467 &setup.sound, "sound"
9471 &setup.sound_loops, "repeating_sound_loops"
9475 &setup.sound_music, "background_music"
9479 &setup.sound_simple, "simple_sound_effects"
9483 &setup.toons, "toons"
9487 &setup.scroll_delay, "scroll_delay"
9491 &setup.forced_scroll_delay, "forced_scroll_delay"
9495 &setup.scroll_delay_value, "scroll_delay_value"
9499 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9503 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9507 &setup.fade_screens, "fade_screens"
9511 &setup.autorecord, "automatic_tape_recording"
9515 &setup.autorecord_after_replay, "autorecord_after_replay"
9519 &setup.auto_pause_on_start, "auto_pause_on_start"
9523 &setup.show_titlescreen, "show_titlescreen"
9527 &setup.quick_doors, "quick_doors"
9531 &setup.team_mode, "team_mode"
9535 &setup.handicap, "handicap"
9539 &setup.skip_levels, "skip_levels"
9543 &setup.increment_levels, "increment_levels"
9547 &setup.auto_play_next_level, "auto_play_next_level"
9551 &setup.count_score_after_game, "count_score_after_game"
9555 &setup.show_scores_after_game, "show_scores_after_game"
9559 &setup.time_limit, "time_limit"
9563 &setup.fullscreen, "fullscreen"
9567 &setup.window_scaling_percent, "window_scaling_percent"
9571 &setup.window_scaling_quality, "window_scaling_quality"
9575 &setup.screen_rendering_mode, "screen_rendering_mode"
9579 &setup.vsync_mode, "vsync_mode"
9583 &setup.ask_on_escape, "ask_on_escape"
9587 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9591 &setup.ask_on_game_over, "ask_on_game_over"
9595 &setup.ask_on_quit_game, "ask_on_quit_game"
9599 &setup.ask_on_quit_program, "ask_on_quit_program"
9603 &setup.quick_switch, "quick_player_switch"
9607 &setup.input_on_focus, "input_on_focus"
9611 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9615 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9619 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9623 &setup.game_speed_extended, "game_speed_extended"
9627 &setup.game_frame_delay, "game_frame_delay"
9631 &setup.sp_show_border_elements, "sp_show_border_elements"
9635 &setup.small_game_graphics, "small_game_graphics"
9639 &setup.show_load_save_buttons, "show_load_save_buttons"
9643 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9647 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9651 &setup.graphics_set, "graphics_set"
9655 &setup.sounds_set, "sounds_set"
9659 &setup.music_set, "music_set"
9663 &setup.override_level_graphics, "override_level_graphics"
9667 &setup.override_level_sounds, "override_level_sounds"
9671 &setup.override_level_music, "override_level_music"
9675 &setup.volume_simple, "volume_simple"
9679 &setup.volume_loops, "volume_loops"
9683 &setup.volume_music, "volume_music"
9687 &setup.network_mode, "network_mode"
9691 &setup.network_player_nr, "network_player"
9695 &setup.network_server_hostname, "network_server_hostname"
9699 &setup.touch.control_type, "touch.control_type"
9703 &setup.touch.move_distance, "touch.move_distance"
9707 &setup.touch.drop_distance, "touch.drop_distance"
9711 &setup.touch.transparency, "touch.transparency"
9715 &setup.touch.draw_outlined, "touch.draw_outlined"
9719 &setup.touch.draw_pressed, "touch.draw_pressed"
9723 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9727 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9731 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9735 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9739 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9743 static struct TokenInfo auto_setup_tokens[] =
9747 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9751 static struct TokenInfo server_setup_tokens[] =
9755 &setup.player_uuid, "player_uuid"
9759 &setup.player_version, "player_version"
9763 &setup.use_api_server, TEST_PREFIX "use_api_server"
9767 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9771 &setup.api_server_password, TEST_PREFIX "api_server_password"
9775 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9779 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9783 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9787 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9791 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9795 static struct TokenInfo editor_setup_tokens[] =
9799 &setup.editor.el_classic, "editor.el_classic"
9803 &setup.editor.el_custom, "editor.el_custom"
9807 &setup.editor.el_user_defined, "editor.el_user_defined"
9811 &setup.editor.el_dynamic, "editor.el_dynamic"
9815 &setup.editor.el_headlines, "editor.el_headlines"
9819 &setup.editor.show_element_token, "editor.show_element_token"
9823 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9827 static struct TokenInfo editor_cascade_setup_tokens[] =
9831 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9835 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9839 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9843 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9847 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9851 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9855 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9859 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9863 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9867 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9871 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9875 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9879 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9883 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9887 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9891 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9895 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9899 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9903 static struct TokenInfo shortcut_setup_tokens[] =
9907 &setup.shortcut.save_game, "shortcut.save_game"
9911 &setup.shortcut.load_game, "shortcut.load_game"
9915 &setup.shortcut.restart_game, "shortcut.restart_game"
9919 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9923 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9927 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9931 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9935 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9939 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9943 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9947 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9951 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9955 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9959 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9963 &setup.shortcut.tape_record, "shortcut.tape_record"
9967 &setup.shortcut.tape_play, "shortcut.tape_play"
9971 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9975 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9979 &setup.shortcut.sound_music, "shortcut.sound_music"
9983 &setup.shortcut.snap_left, "shortcut.snap_left"
9987 &setup.shortcut.snap_right, "shortcut.snap_right"
9991 &setup.shortcut.snap_up, "shortcut.snap_up"
9995 &setup.shortcut.snap_down, "shortcut.snap_down"
9999 static struct SetupInputInfo setup_input;
10000 static struct TokenInfo player_setup_tokens[] =
10004 &setup_input.use_joystick, ".use_joystick"
10008 &setup_input.joy.device_name, ".joy.device_name"
10012 &setup_input.joy.xleft, ".joy.xleft"
10016 &setup_input.joy.xmiddle, ".joy.xmiddle"
10020 &setup_input.joy.xright, ".joy.xright"
10024 &setup_input.joy.yupper, ".joy.yupper"
10028 &setup_input.joy.ymiddle, ".joy.ymiddle"
10032 &setup_input.joy.ylower, ".joy.ylower"
10036 &setup_input.joy.snap, ".joy.snap_field"
10040 &setup_input.joy.drop, ".joy.place_bomb"
10044 &setup_input.key.left, ".key.move_left"
10048 &setup_input.key.right, ".key.move_right"
10052 &setup_input.key.up, ".key.move_up"
10056 &setup_input.key.down, ".key.move_down"
10060 &setup_input.key.snap, ".key.snap_field"
10064 &setup_input.key.drop, ".key.place_bomb"
10068 static struct TokenInfo system_setup_tokens[] =
10072 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10076 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10080 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10084 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10088 static struct TokenInfo internal_setup_tokens[] =
10092 &setup.internal.program_title, "program_title"
10096 &setup.internal.program_version, "program_version"
10100 &setup.internal.program_author, "program_author"
10104 &setup.internal.program_email, "program_email"
10108 &setup.internal.program_website, "program_website"
10112 &setup.internal.program_copyright, "program_copyright"
10116 &setup.internal.program_company, "program_company"
10120 &setup.internal.program_icon_file, "program_icon_file"
10124 &setup.internal.default_graphics_set, "default_graphics_set"
10128 &setup.internal.default_sounds_set, "default_sounds_set"
10132 &setup.internal.default_music_set, "default_music_set"
10136 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10140 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10144 &setup.internal.fallback_music_file, "fallback_music_file"
10148 &setup.internal.default_level_series, "default_level_series"
10152 &setup.internal.default_window_width, "default_window_width"
10156 &setup.internal.default_window_height, "default_window_height"
10160 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10164 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10168 &setup.internal.create_user_levelset, "create_user_levelset"
10172 &setup.internal.info_screens_from_main, "info_screens_from_main"
10176 &setup.internal.menu_game, "menu_game"
10180 &setup.internal.menu_engines, "menu_engines"
10184 &setup.internal.menu_editor, "menu_editor"
10188 &setup.internal.menu_graphics, "menu_graphics"
10192 &setup.internal.menu_sound, "menu_sound"
10196 &setup.internal.menu_artwork, "menu_artwork"
10200 &setup.internal.menu_input, "menu_input"
10204 &setup.internal.menu_touch, "menu_touch"
10208 &setup.internal.menu_shortcuts, "menu_shortcuts"
10212 &setup.internal.menu_exit, "menu_exit"
10216 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10220 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10224 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10228 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10232 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10236 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10240 &setup.internal.info_title, "info_title"
10244 &setup.internal.info_elements, "info_elements"
10248 &setup.internal.info_music, "info_music"
10252 &setup.internal.info_credits, "info_credits"
10256 &setup.internal.info_program, "info_program"
10260 &setup.internal.info_version, "info_version"
10264 &setup.internal.info_levelset, "info_levelset"
10268 &setup.internal.info_exit, "info_exit"
10272 static struct TokenInfo debug_setup_tokens[] =
10276 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10280 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10284 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10288 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10292 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10296 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10300 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10304 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10308 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10312 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10316 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10320 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10324 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10328 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10332 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10336 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10340 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10344 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10348 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10352 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10356 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10359 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10363 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10367 &setup.debug.xsn_mode, "debug.xsn_mode"
10371 &setup.debug.xsn_percent, "debug.xsn_percent"
10375 static struct TokenInfo options_setup_tokens[] =
10379 &setup.options.verbose, "options.verbose"
10383 &setup.options.debug, "options.debug"
10387 &setup.options.debug_mode, "options.debug_mode"
10391 static void setSetupInfoToDefaults(struct SetupInfo *si)
10395 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10397 si->multiple_users = TRUE;
10400 si->sound_loops = TRUE;
10401 si->sound_music = TRUE;
10402 si->sound_simple = TRUE;
10404 si->scroll_delay = TRUE;
10405 si->forced_scroll_delay = FALSE;
10406 si->scroll_delay_value = STD_SCROLL_DELAY;
10407 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10408 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10409 si->fade_screens = TRUE;
10410 si->autorecord = TRUE;
10411 si->autorecord_after_replay = TRUE;
10412 si->auto_pause_on_start = FALSE;
10413 si->show_titlescreen = TRUE;
10414 si->quick_doors = FALSE;
10415 si->team_mode = FALSE;
10416 si->handicap = TRUE;
10417 si->skip_levels = TRUE;
10418 si->increment_levels = TRUE;
10419 si->auto_play_next_level = TRUE;
10420 si->count_score_after_game = TRUE;
10421 si->show_scores_after_game = TRUE;
10422 si->time_limit = TRUE;
10423 si->fullscreen = FALSE;
10424 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10425 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10426 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10427 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10428 si->ask_on_escape = TRUE;
10429 si->ask_on_escape_editor = TRUE;
10430 si->ask_on_game_over = TRUE;
10431 si->ask_on_quit_game = TRUE;
10432 si->ask_on_quit_program = TRUE;
10433 si->quick_switch = FALSE;
10434 si->input_on_focus = FALSE;
10435 si->prefer_aga_graphics = TRUE;
10436 si->prefer_lowpass_sounds = FALSE;
10437 si->prefer_extra_panel_items = TRUE;
10438 si->game_speed_extended = FALSE;
10439 si->game_frame_delay = GAME_FRAME_DELAY;
10440 si->sp_show_border_elements = FALSE;
10441 si->small_game_graphics = FALSE;
10442 si->show_load_save_buttons = FALSE;
10443 si->show_undo_redo_buttons = FALSE;
10444 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10446 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10447 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10448 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10450 si->override_level_graphics = FALSE;
10451 si->override_level_sounds = FALSE;
10452 si->override_level_music = FALSE;
10454 si->volume_simple = 100; // percent
10455 si->volume_loops = 100; // percent
10456 si->volume_music = 100; // percent
10458 si->network_mode = FALSE;
10459 si->network_player_nr = 0; // first player
10460 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10462 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10463 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10464 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10465 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10466 si->touch.draw_outlined = TRUE;
10467 si->touch.draw_pressed = TRUE;
10469 for (i = 0; i < 2; i++)
10471 char *default_grid_button[6][2] =
10477 { "111222", " vv " },
10478 { "111222", " vv " }
10480 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10481 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10482 int min_xsize = MIN(6, grid_xsize);
10483 int min_ysize = MIN(6, grid_ysize);
10484 int startx = grid_xsize - min_xsize;
10485 int starty = grid_ysize - min_ysize;
10488 // virtual buttons grid can only be set to defaults if video is initialized
10489 // (this will be repeated if virtual buttons are not loaded from setup file)
10490 if (video.initialized)
10492 si->touch.grid_xsize[i] = grid_xsize;
10493 si->touch.grid_ysize[i] = grid_ysize;
10497 si->touch.grid_xsize[i] = -1;
10498 si->touch.grid_ysize[i] = -1;
10501 for (x = 0; x < MAX_GRID_XSIZE; x++)
10502 for (y = 0; y < MAX_GRID_YSIZE; y++)
10503 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10505 for (x = 0; x < min_xsize; x++)
10506 for (y = 0; y < min_ysize; y++)
10507 si->touch.grid_button[i][x][starty + y] =
10508 default_grid_button[y][0][x];
10510 for (x = 0; x < min_xsize; x++)
10511 for (y = 0; y < min_ysize; y++)
10512 si->touch.grid_button[i][startx + x][starty + y] =
10513 default_grid_button[y][1][x];
10516 si->touch.grid_initialized = video.initialized;
10518 si->touch.overlay_buttons = FALSE;
10520 si->editor.el_boulderdash = TRUE;
10521 si->editor.el_emerald_mine = TRUE;
10522 si->editor.el_emerald_mine_club = TRUE;
10523 si->editor.el_more = TRUE;
10524 si->editor.el_sokoban = TRUE;
10525 si->editor.el_supaplex = TRUE;
10526 si->editor.el_diamond_caves = TRUE;
10527 si->editor.el_dx_boulderdash = TRUE;
10529 si->editor.el_mirror_magic = TRUE;
10530 si->editor.el_deflektor = TRUE;
10532 si->editor.el_chars = TRUE;
10533 si->editor.el_steel_chars = TRUE;
10535 si->editor.el_classic = TRUE;
10536 si->editor.el_custom = TRUE;
10538 si->editor.el_user_defined = FALSE;
10539 si->editor.el_dynamic = TRUE;
10541 si->editor.el_headlines = TRUE;
10543 si->editor.show_element_token = FALSE;
10545 si->editor.show_read_only_warning = TRUE;
10547 si->editor.use_template_for_new_levels = TRUE;
10549 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10550 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10551 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10552 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10553 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10555 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10556 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10557 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10558 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10559 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10561 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10562 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10563 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10564 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10565 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10566 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10568 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10569 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10570 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10572 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10573 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10574 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10575 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10577 for (i = 0; i < MAX_PLAYERS; i++)
10579 si->input[i].use_joystick = FALSE;
10580 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10581 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10582 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10583 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10584 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10585 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10586 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10587 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10588 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10589 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10590 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10591 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10592 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10593 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10594 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10597 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10598 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10599 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10600 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10602 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10603 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10604 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10605 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10606 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10607 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10608 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10610 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10612 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10613 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10614 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10616 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10617 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10618 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10620 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10621 si->internal.choose_from_top_leveldir = FALSE;
10622 si->internal.show_scaling_in_title = TRUE;
10623 si->internal.create_user_levelset = TRUE;
10624 si->internal.info_screens_from_main = FALSE;
10626 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10627 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10629 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10630 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10631 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10632 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10633 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10634 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10635 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10636 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10637 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10638 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10640 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10641 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10642 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10643 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10644 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10645 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10646 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10647 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10648 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10649 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10651 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10652 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10654 si->debug.show_frames_per_second = FALSE;
10656 si->debug.xsn_mode = AUTO;
10657 si->debug.xsn_percent = 0;
10659 si->options.verbose = FALSE;
10660 si->options.debug = FALSE;
10661 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10663 #if defined(PLATFORM_ANDROID)
10664 si->fullscreen = TRUE;
10665 si->touch.overlay_buttons = TRUE;
10668 setHideSetupEntry(&setup.debug.xsn_mode);
10671 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10673 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10676 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10678 si->player_uuid = NULL; // (will be set later)
10679 si->player_version = 1; // (will be set later)
10681 si->use_api_server = TRUE;
10682 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10683 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10684 si->ask_for_uploading_tapes = TRUE;
10685 si->ask_for_remaining_tapes = FALSE;
10686 si->provide_uploading_tapes = TRUE;
10687 si->ask_for_using_api_server = TRUE;
10688 si->has_remaining_tapes = FALSE;
10691 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10693 si->editor_cascade.el_bd = TRUE;
10694 si->editor_cascade.el_em = TRUE;
10695 si->editor_cascade.el_emc = TRUE;
10696 si->editor_cascade.el_rnd = TRUE;
10697 si->editor_cascade.el_sb = TRUE;
10698 si->editor_cascade.el_sp = TRUE;
10699 si->editor_cascade.el_dc = TRUE;
10700 si->editor_cascade.el_dx = TRUE;
10702 si->editor_cascade.el_mm = TRUE;
10703 si->editor_cascade.el_df = TRUE;
10705 si->editor_cascade.el_chars = FALSE;
10706 si->editor_cascade.el_steel_chars = FALSE;
10707 si->editor_cascade.el_ce = FALSE;
10708 si->editor_cascade.el_ge = FALSE;
10709 si->editor_cascade.el_es = FALSE;
10710 si->editor_cascade.el_ref = FALSE;
10711 si->editor_cascade.el_user = FALSE;
10712 si->editor_cascade.el_dynamic = FALSE;
10715 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10717 static char *getHideSetupToken(void *setup_value)
10719 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10721 if (setup_value != NULL)
10722 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10724 return hide_setup_token;
10727 void setHideSetupEntry(void *setup_value)
10729 char *hide_setup_token = getHideSetupToken(setup_value);
10731 if (hide_setup_hash == NULL)
10732 hide_setup_hash = newSetupFileHash();
10734 if (setup_value != NULL)
10735 setHashEntry(hide_setup_hash, hide_setup_token, "");
10738 void removeHideSetupEntry(void *setup_value)
10740 char *hide_setup_token = getHideSetupToken(setup_value);
10742 if (setup_value != NULL)
10743 removeHashEntry(hide_setup_hash, hide_setup_token);
10746 boolean hideSetupEntry(void *setup_value)
10748 char *hide_setup_token = getHideSetupToken(setup_value);
10750 return (setup_value != NULL &&
10751 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10754 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10755 struct TokenInfo *token_info,
10756 int token_nr, char *token_text)
10758 char *token_hide_text = getStringCat2(token_text, ".hide");
10759 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10761 // set the value of this setup option in the setup option structure
10762 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10764 // check if this setup option should be hidden in the setup menu
10765 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10766 setHideSetupEntry(token_info[token_nr].value);
10768 free(token_hide_text);
10771 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10772 struct TokenInfo *token_info,
10775 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10776 token_info[token_nr].text);
10779 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10783 if (!setup_file_hash)
10786 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10787 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10789 setup.touch.grid_initialized = TRUE;
10790 for (i = 0; i < 2; i++)
10792 int grid_xsize = setup.touch.grid_xsize[i];
10793 int grid_ysize = setup.touch.grid_ysize[i];
10796 // if virtual buttons are not loaded from setup file, repeat initializing
10797 // virtual buttons grid with default values later when video is initialized
10798 if (grid_xsize == -1 ||
10801 setup.touch.grid_initialized = FALSE;
10806 for (y = 0; y < grid_ysize; y++)
10808 char token_string[MAX_LINE_LEN];
10810 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10812 char *value_string = getHashEntry(setup_file_hash, token_string);
10814 if (value_string == NULL)
10817 for (x = 0; x < grid_xsize; x++)
10819 char c = value_string[x];
10821 setup.touch.grid_button[i][x][y] =
10822 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10827 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10828 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10830 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10831 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10833 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10837 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10839 setup_input = setup.input[pnr];
10840 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10842 char full_token[100];
10844 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10845 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10848 setup.input[pnr] = setup_input;
10851 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10852 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10854 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10855 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10857 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10858 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10860 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10861 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10863 setHideRelatedSetupEntries();
10866 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10870 if (!setup_file_hash)
10873 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10874 setSetupInfo(auto_setup_tokens, i,
10875 getHashEntry(setup_file_hash,
10876 auto_setup_tokens[i].text));
10879 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10883 if (!setup_file_hash)
10886 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10887 setSetupInfo(server_setup_tokens, i,
10888 getHashEntry(setup_file_hash,
10889 server_setup_tokens[i].text));
10892 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10896 if (!setup_file_hash)
10899 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10900 setSetupInfo(editor_cascade_setup_tokens, i,
10901 getHashEntry(setup_file_hash,
10902 editor_cascade_setup_tokens[i].text));
10905 void LoadUserNames(void)
10907 int last_user_nr = user.nr;
10910 if (global.user_names != NULL)
10912 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10913 checked_free(global.user_names[i]);
10915 checked_free(global.user_names);
10918 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10920 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10924 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10926 if (setup_file_hash)
10928 char *player_name = getHashEntry(setup_file_hash, "player_name");
10930 global.user_names[i] = getFixedUserName(player_name);
10932 freeSetupFileHash(setup_file_hash);
10935 if (global.user_names[i] == NULL)
10936 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10939 user.nr = last_user_nr;
10942 void LoadSetupFromFilename(char *filename)
10944 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10946 if (setup_file_hash)
10948 decodeSetupFileHash_Default(setup_file_hash);
10950 freeSetupFileHash(setup_file_hash);
10954 Debug("setup", "using default setup values");
10958 static void LoadSetup_SpecialPostProcessing(void)
10960 char *player_name_new;
10962 // needed to work around problems with fixed length strings
10963 player_name_new = getFixedUserName(setup.player_name);
10964 free(setup.player_name);
10965 setup.player_name = player_name_new;
10967 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10968 if (setup.scroll_delay == FALSE)
10970 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10971 setup.scroll_delay = TRUE; // now always "on"
10974 // make sure that scroll delay value stays inside valid range
10975 setup.scroll_delay_value =
10976 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10979 void LoadSetup_Default(void)
10983 // always start with reliable default values
10984 setSetupInfoToDefaults(&setup);
10986 // try to load setup values from default setup file
10987 filename = getDefaultSetupFilename();
10989 if (fileExists(filename))
10990 LoadSetupFromFilename(filename);
10992 // try to load setup values from platform setup file
10993 filename = getPlatformSetupFilename();
10995 if (fileExists(filename))
10996 LoadSetupFromFilename(filename);
10998 // try to load setup values from user setup file
10999 filename = getSetupFilename();
11001 LoadSetupFromFilename(filename);
11003 LoadSetup_SpecialPostProcessing();
11006 void LoadSetup_AutoSetup(void)
11008 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11009 SetupFileHash *setup_file_hash = NULL;
11011 // always start with reliable default values
11012 setSetupInfoToDefaults_AutoSetup(&setup);
11014 setup_file_hash = loadSetupFileHash(filename);
11016 if (setup_file_hash)
11018 decodeSetupFileHash_AutoSetup(setup_file_hash);
11020 freeSetupFileHash(setup_file_hash);
11026 void LoadSetup_ServerSetup(void)
11028 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11029 SetupFileHash *setup_file_hash = NULL;
11031 // always start with reliable default values
11032 setSetupInfoToDefaults_ServerSetup(&setup);
11034 setup_file_hash = loadSetupFileHash(filename);
11036 if (setup_file_hash)
11038 decodeSetupFileHash_ServerSetup(setup_file_hash);
11040 freeSetupFileHash(setup_file_hash);
11045 if (setup.player_uuid == NULL)
11047 // player UUID does not yet exist in setup file
11048 setup.player_uuid = getStringCopy(getUUID());
11049 setup.player_version = 2;
11051 SaveSetup_ServerSetup();
11055 void LoadSetup_EditorCascade(void)
11057 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11058 SetupFileHash *setup_file_hash = NULL;
11060 // always start with reliable default values
11061 setSetupInfoToDefaults_EditorCascade(&setup);
11063 setup_file_hash = loadSetupFileHash(filename);
11065 if (setup_file_hash)
11067 decodeSetupFileHash_EditorCascade(setup_file_hash);
11069 freeSetupFileHash(setup_file_hash);
11075 void LoadSetup(void)
11077 LoadSetup_Default();
11078 LoadSetup_AutoSetup();
11079 LoadSetup_ServerSetup();
11080 LoadSetup_EditorCascade();
11083 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11084 char *mapping_line)
11086 char mapping_guid[MAX_LINE_LEN];
11087 char *mapping_start, *mapping_end;
11089 // get GUID from game controller mapping line: copy complete line
11090 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11091 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11093 // get GUID from game controller mapping line: cut after GUID part
11094 mapping_start = strchr(mapping_guid, ',');
11095 if (mapping_start != NULL)
11096 *mapping_start = '\0';
11098 // cut newline from game controller mapping line
11099 mapping_end = strchr(mapping_line, '\n');
11100 if (mapping_end != NULL)
11101 *mapping_end = '\0';
11103 // add mapping entry to game controller mappings hash
11104 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11107 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11112 if (!(file = fopen(filename, MODE_READ)))
11114 Warn("cannot read game controller mappings file '%s'", filename);
11119 while (!feof(file))
11121 char line[MAX_LINE_LEN];
11123 if (!fgets(line, MAX_LINE_LEN, file))
11126 addGameControllerMappingToHash(mappings_hash, line);
11132 void SaveSetup_Default(void)
11134 char *filename = getSetupFilename();
11138 InitUserDataDirectory();
11140 if (!(file = fopen(filename, MODE_WRITE)))
11142 Warn("cannot write setup file '%s'", filename);
11147 fprintFileHeader(file, SETUP_FILENAME);
11149 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11151 // just to make things nicer :)
11152 if (global_setup_tokens[i].value == &setup.multiple_users ||
11153 global_setup_tokens[i].value == &setup.sound ||
11154 global_setup_tokens[i].value == &setup.graphics_set ||
11155 global_setup_tokens[i].value == &setup.volume_simple ||
11156 global_setup_tokens[i].value == &setup.network_mode ||
11157 global_setup_tokens[i].value == &setup.touch.control_type ||
11158 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11159 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11160 fprintf(file, "\n");
11162 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11165 for (i = 0; i < 2; i++)
11167 int grid_xsize = setup.touch.grid_xsize[i];
11168 int grid_ysize = setup.touch.grid_ysize[i];
11171 fprintf(file, "\n");
11173 for (y = 0; y < grid_ysize; y++)
11175 char token_string[MAX_LINE_LEN];
11176 char value_string[MAX_LINE_LEN];
11178 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11180 for (x = 0; x < grid_xsize; x++)
11182 char c = setup.touch.grid_button[i][x][y];
11184 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11187 value_string[grid_xsize] = '\0';
11189 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11193 fprintf(file, "\n");
11194 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11195 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11197 fprintf(file, "\n");
11198 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11199 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11201 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11205 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11206 fprintf(file, "\n");
11208 setup_input = setup.input[pnr];
11209 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11210 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11213 fprintf(file, "\n");
11214 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11215 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11217 // (internal setup values not saved to user setup file)
11219 fprintf(file, "\n");
11220 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11221 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11222 setup.debug.xsn_mode != AUTO)
11223 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11225 fprintf(file, "\n");
11226 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11227 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11231 SetFilePermissions(filename, PERMS_PRIVATE);
11234 void SaveSetup_AutoSetup(void)
11236 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11240 InitUserDataDirectory();
11242 if (!(file = fopen(filename, MODE_WRITE)))
11244 Warn("cannot write auto setup file '%s'", filename);
11251 fprintFileHeader(file, AUTOSETUP_FILENAME);
11253 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11254 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11258 SetFilePermissions(filename, PERMS_PRIVATE);
11263 void SaveSetup_ServerSetup(void)
11265 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11269 InitUserDataDirectory();
11271 if (!(file = fopen(filename, MODE_WRITE)))
11273 Warn("cannot write server setup file '%s'", filename);
11280 fprintFileHeader(file, SERVERSETUP_FILENAME);
11282 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11284 // just to make things nicer :)
11285 if (server_setup_tokens[i].value == &setup.use_api_server)
11286 fprintf(file, "\n");
11288 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11293 SetFilePermissions(filename, PERMS_PRIVATE);
11298 void SaveSetup_EditorCascade(void)
11300 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11304 InitUserDataDirectory();
11306 if (!(file = fopen(filename, MODE_WRITE)))
11308 Warn("cannot write editor cascade state file '%s'", filename);
11315 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11317 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11318 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11322 SetFilePermissions(filename, PERMS_PRIVATE);
11327 void SaveSetup(void)
11329 SaveSetup_Default();
11330 SaveSetup_AutoSetup();
11331 SaveSetup_ServerSetup();
11332 SaveSetup_EditorCascade();
11335 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11340 if (!(file = fopen(filename, MODE_WRITE)))
11342 Warn("cannot write game controller mappings file '%s'", filename);
11347 BEGIN_HASH_ITERATION(mappings_hash, itr)
11349 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11351 END_HASH_ITERATION(mappings_hash, itr)
11356 void SaveSetup_AddGameControllerMapping(char *mapping)
11358 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11359 SetupFileHash *mappings_hash = newSetupFileHash();
11361 InitUserDataDirectory();
11363 // load existing personal game controller mappings
11364 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11366 // add new mapping to personal game controller mappings
11367 addGameControllerMappingToHash(mappings_hash, mapping);
11369 // save updated personal game controller mappings
11370 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11372 freeSetupFileHash(mappings_hash);
11376 void LoadCustomElementDescriptions(void)
11378 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11379 SetupFileHash *setup_file_hash;
11382 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11384 if (element_info[i].custom_description != NULL)
11386 free(element_info[i].custom_description);
11387 element_info[i].custom_description = NULL;
11391 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11394 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11396 char *token = getStringCat2(element_info[i].token_name, ".name");
11397 char *value = getHashEntry(setup_file_hash, token);
11400 element_info[i].custom_description = getStringCopy(value);
11405 freeSetupFileHash(setup_file_hash);
11408 static int getElementFromToken(char *token)
11410 char *value = getHashEntry(element_token_hash, token);
11413 return atoi(value);
11415 Warn("unknown element token '%s'", token);
11417 return EL_UNDEFINED;
11420 void FreeGlobalAnimEventInfo(void)
11422 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11424 if (gaei->event_list == NULL)
11429 for (i = 0; i < gaei->num_event_lists; i++)
11431 checked_free(gaei->event_list[i]->event_value);
11432 checked_free(gaei->event_list[i]);
11435 checked_free(gaei->event_list);
11437 gaei->event_list = NULL;
11438 gaei->num_event_lists = 0;
11441 static int AddGlobalAnimEventList(void)
11443 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11444 int list_pos = gaei->num_event_lists++;
11446 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11447 sizeof(struct GlobalAnimEventListInfo *));
11449 gaei->event_list[list_pos] =
11450 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11452 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11454 gaeli->event_value = NULL;
11455 gaeli->num_event_values = 0;
11460 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11462 // do not add empty global animation events
11463 if (event_value == ANIM_EVENT_NONE)
11466 // if list position is undefined, create new list
11467 if (list_pos == ANIM_EVENT_UNDEFINED)
11468 list_pos = AddGlobalAnimEventList();
11470 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11471 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11472 int value_pos = gaeli->num_event_values++;
11474 gaeli->event_value = checked_realloc(gaeli->event_value,
11475 gaeli->num_event_values * sizeof(int *));
11477 gaeli->event_value[value_pos] = event_value;
11482 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11484 if (list_pos == ANIM_EVENT_UNDEFINED)
11485 return ANIM_EVENT_NONE;
11487 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11488 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11490 return gaeli->event_value[value_pos];
11493 int GetGlobalAnimEventValueCount(int list_pos)
11495 if (list_pos == ANIM_EVENT_UNDEFINED)
11498 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11499 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11501 return gaeli->num_event_values;
11504 // This function checks if a string <s> of the format "string1, string2, ..."
11505 // exactly contains a string <s_contained>.
11507 static boolean string_has_parameter(char *s, char *s_contained)
11511 if (s == NULL || s_contained == NULL)
11514 if (strlen(s_contained) > strlen(s))
11517 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11519 char next_char = s[strlen(s_contained)];
11521 // check if next character is delimiter or whitespace
11522 return (next_char == ',' || next_char == '\0' ||
11523 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11526 // check if string contains another parameter string after a comma
11527 substring = strchr(s, ',');
11528 if (substring == NULL) // string does not contain a comma
11531 // advance string pointer to next character after the comma
11534 // skip potential whitespaces after the comma
11535 while (*substring == ' ' || *substring == '\t')
11538 return string_has_parameter(substring, s_contained);
11541 static int get_anim_parameter_value(char *s)
11543 int event_value[] =
11551 char *pattern_1[] =
11559 char *pattern_2 = ".part_";
11560 char *matching_char = NULL;
11562 int pattern_1_len = 0;
11563 int result = ANIM_EVENT_NONE;
11566 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11568 matching_char = strstr(s_ptr, pattern_1[i]);
11569 pattern_1_len = strlen(pattern_1[i]);
11570 result = event_value[i];
11572 if (matching_char != NULL)
11576 if (matching_char == NULL)
11577 return ANIM_EVENT_NONE;
11579 s_ptr = matching_char + pattern_1_len;
11581 // check for main animation number ("anim_X" or "anim_XX")
11582 if (*s_ptr >= '0' && *s_ptr <= '9')
11584 int gic_anim_nr = (*s_ptr++ - '0');
11586 if (*s_ptr >= '0' && *s_ptr <= '9')
11587 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11589 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11590 return ANIM_EVENT_NONE;
11592 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11596 // invalid main animation number specified
11598 return ANIM_EVENT_NONE;
11601 // check for animation part number ("part_X" or "part_XX") (optional)
11602 if (strPrefix(s_ptr, pattern_2))
11604 s_ptr += strlen(pattern_2);
11606 if (*s_ptr >= '0' && *s_ptr <= '9')
11608 int gic_part_nr = (*s_ptr++ - '0');
11610 if (*s_ptr >= '0' && *s_ptr <= '9')
11611 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11613 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11614 return ANIM_EVENT_NONE;
11616 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11620 // invalid animation part number specified
11622 return ANIM_EVENT_NONE;
11626 // discard result if next character is neither delimiter nor whitespace
11627 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11628 *s_ptr == ' ' || *s_ptr == '\t'))
11629 return ANIM_EVENT_NONE;
11634 static int get_anim_parameter_values(char *s)
11636 int list_pos = ANIM_EVENT_UNDEFINED;
11637 int event_value = ANIM_EVENT_DEFAULT;
11639 if (string_has_parameter(s, "any"))
11640 event_value |= ANIM_EVENT_ANY;
11642 if (string_has_parameter(s, "click:self") ||
11643 string_has_parameter(s, "click") ||
11644 string_has_parameter(s, "self"))
11645 event_value |= ANIM_EVENT_SELF;
11647 if (string_has_parameter(s, "unclick:any"))
11648 event_value |= ANIM_EVENT_UNCLICK_ANY;
11650 // if animation event found, add it to global animation event list
11651 if (event_value != ANIM_EVENT_NONE)
11652 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11656 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11657 event_value = get_anim_parameter_value(s);
11659 // if animation event found, add it to global animation event list
11660 if (event_value != ANIM_EVENT_NONE)
11661 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11663 // continue with next part of the string, starting with next comma
11664 s = strchr(s + 1, ',');
11670 static int get_anim_action_parameter_value(char *token)
11672 // check most common default case first to massively speed things up
11673 if (strEqual(token, ARG_UNDEFINED))
11674 return ANIM_EVENT_ACTION_NONE;
11676 int result = getImageIDFromToken(token);
11680 char *gfx_token = getStringCat2("gfx.", token);
11682 result = getImageIDFromToken(gfx_token);
11684 checked_free(gfx_token);
11689 Key key = getKeyFromX11KeyName(token);
11691 if (key != KSYM_UNDEFINED)
11692 result = -(int)key;
11699 result = get_hash_from_key(token); // unsigned int => int
11700 result = ABS(result); // may be negative now
11701 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11703 setHashEntry(anim_url_hash, int2str(result, 0), token);
11708 result = ANIM_EVENT_ACTION_NONE;
11713 int get_parameter_value(char *value_raw, char *suffix, int type)
11715 char *value = getStringToLower(value_raw);
11716 int result = 0; // probably a save default value
11718 if (strEqual(suffix, ".direction"))
11720 result = (strEqual(value, "left") ? MV_LEFT :
11721 strEqual(value, "right") ? MV_RIGHT :
11722 strEqual(value, "up") ? MV_UP :
11723 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11725 else if (strEqual(suffix, ".position"))
11727 result = (strEqual(value, "left") ? POS_LEFT :
11728 strEqual(value, "right") ? POS_RIGHT :
11729 strEqual(value, "top") ? POS_TOP :
11730 strEqual(value, "upper") ? POS_UPPER :
11731 strEqual(value, "middle") ? POS_MIDDLE :
11732 strEqual(value, "lower") ? POS_LOWER :
11733 strEqual(value, "bottom") ? POS_BOTTOM :
11734 strEqual(value, "any") ? POS_ANY :
11735 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11737 else if (strEqual(suffix, ".align"))
11739 result = (strEqual(value, "left") ? ALIGN_LEFT :
11740 strEqual(value, "right") ? ALIGN_RIGHT :
11741 strEqual(value, "center") ? ALIGN_CENTER :
11742 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11744 else if (strEqual(suffix, ".valign"))
11746 result = (strEqual(value, "top") ? VALIGN_TOP :
11747 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11748 strEqual(value, "middle") ? VALIGN_MIDDLE :
11749 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11751 else if (strEqual(suffix, ".anim_mode"))
11753 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11754 string_has_parameter(value, "loop") ? ANIM_LOOP :
11755 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11756 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11757 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11758 string_has_parameter(value, "random") ? ANIM_RANDOM :
11759 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11760 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11761 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11762 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11763 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11764 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11765 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11766 string_has_parameter(value, "all") ? ANIM_ALL :
11767 string_has_parameter(value, "tiled") ? ANIM_TILED :
11770 if (string_has_parameter(value, "once"))
11771 result |= ANIM_ONCE;
11773 if (string_has_parameter(value, "reverse"))
11774 result |= ANIM_REVERSE;
11776 if (string_has_parameter(value, "opaque_player"))
11777 result |= ANIM_OPAQUE_PLAYER;
11779 if (string_has_parameter(value, "static_panel"))
11780 result |= ANIM_STATIC_PANEL;
11782 else if (strEqual(suffix, ".init_event") ||
11783 strEqual(suffix, ".anim_event"))
11785 result = get_anim_parameter_values(value);
11787 else if (strEqual(suffix, ".init_delay_action") ||
11788 strEqual(suffix, ".anim_delay_action") ||
11789 strEqual(suffix, ".post_delay_action") ||
11790 strEqual(suffix, ".init_event_action") ||
11791 strEqual(suffix, ".anim_event_action"))
11793 result = get_anim_action_parameter_value(value_raw);
11795 else if (strEqual(suffix, ".class"))
11797 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11798 get_hash_from_key(value));
11800 else if (strEqual(suffix, ".style"))
11802 result = STYLE_DEFAULT;
11804 if (string_has_parameter(value, "accurate_borders"))
11805 result |= STYLE_ACCURATE_BORDERS;
11807 if (string_has_parameter(value, "inner_corners"))
11808 result |= STYLE_INNER_CORNERS;
11810 if (string_has_parameter(value, "reverse"))
11811 result |= STYLE_REVERSE;
11813 if (string_has_parameter(value, "leftmost_position"))
11814 result |= STYLE_LEFTMOST_POSITION;
11816 if (string_has_parameter(value, "block_clicks"))
11817 result |= STYLE_BLOCK;
11819 if (string_has_parameter(value, "passthrough_clicks"))
11820 result |= STYLE_PASSTHROUGH;
11822 if (string_has_parameter(value, "multiple_actions"))
11823 result |= STYLE_MULTIPLE_ACTIONS;
11825 else if (strEqual(suffix, ".fade_mode"))
11827 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11828 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11829 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11830 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11831 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11832 FADE_MODE_DEFAULT);
11834 else if (strEqual(suffix, ".auto_delay_unit"))
11836 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11837 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11838 AUTO_DELAY_UNIT_DEFAULT);
11840 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11842 result = gfx.get_font_from_token_function(value);
11844 else // generic parameter of type integer or boolean
11846 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11847 type == TYPE_INTEGER ? get_integer_from_string(value) :
11848 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11849 ARG_UNDEFINED_VALUE);
11857 static int get_token_parameter_value(char *token, char *value_raw)
11861 if (token == NULL || value_raw == NULL)
11862 return ARG_UNDEFINED_VALUE;
11864 suffix = strrchr(token, '.');
11865 if (suffix == NULL)
11868 if (strEqual(suffix, ".element"))
11869 return getElementFromToken(value_raw);
11871 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11872 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11875 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11876 boolean ignore_defaults)
11880 for (i = 0; image_config_vars[i].token != NULL; i++)
11882 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11884 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11885 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11889 *image_config_vars[i].value =
11890 get_token_parameter_value(image_config_vars[i].token, value);
11894 void InitMenuDesignSettings_Static(void)
11896 // always start with reliable default values from static default config
11897 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11900 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11904 // the following initializes hierarchical values from static configuration
11906 // special case: initialize "ARG_DEFAULT" values in static default config
11907 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11908 titlescreen_initial_first_default.fade_mode =
11909 title_initial_first_default.fade_mode;
11910 titlescreen_initial_first_default.fade_delay =
11911 title_initial_first_default.fade_delay;
11912 titlescreen_initial_first_default.post_delay =
11913 title_initial_first_default.post_delay;
11914 titlescreen_initial_first_default.auto_delay =
11915 title_initial_first_default.auto_delay;
11916 titlescreen_initial_first_default.auto_delay_unit =
11917 title_initial_first_default.auto_delay_unit;
11918 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11919 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11920 titlescreen_first_default.post_delay = title_first_default.post_delay;
11921 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11922 titlescreen_first_default.auto_delay_unit =
11923 title_first_default.auto_delay_unit;
11924 titlemessage_initial_first_default.fade_mode =
11925 title_initial_first_default.fade_mode;
11926 titlemessage_initial_first_default.fade_delay =
11927 title_initial_first_default.fade_delay;
11928 titlemessage_initial_first_default.post_delay =
11929 title_initial_first_default.post_delay;
11930 titlemessage_initial_first_default.auto_delay =
11931 title_initial_first_default.auto_delay;
11932 titlemessage_initial_first_default.auto_delay_unit =
11933 title_initial_first_default.auto_delay_unit;
11934 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11935 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11936 titlemessage_first_default.post_delay = title_first_default.post_delay;
11937 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11938 titlemessage_first_default.auto_delay_unit =
11939 title_first_default.auto_delay_unit;
11941 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11942 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11943 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11944 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11945 titlescreen_initial_default.auto_delay_unit =
11946 title_initial_default.auto_delay_unit;
11947 titlescreen_default.fade_mode = title_default.fade_mode;
11948 titlescreen_default.fade_delay = title_default.fade_delay;
11949 titlescreen_default.post_delay = title_default.post_delay;
11950 titlescreen_default.auto_delay = title_default.auto_delay;
11951 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11952 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11953 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11954 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11955 titlemessage_initial_default.auto_delay_unit =
11956 title_initial_default.auto_delay_unit;
11957 titlemessage_default.fade_mode = title_default.fade_mode;
11958 titlemessage_default.fade_delay = title_default.fade_delay;
11959 titlemessage_default.post_delay = title_default.post_delay;
11960 titlemessage_default.auto_delay = title_default.auto_delay;
11961 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11963 // special case: initialize "ARG_DEFAULT" values in static default config
11964 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11965 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11967 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11968 titlescreen_first[i] = titlescreen_first_default;
11969 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11970 titlemessage_first[i] = titlemessage_first_default;
11972 titlescreen_initial[i] = titlescreen_initial_default;
11973 titlescreen[i] = titlescreen_default;
11974 titlemessage_initial[i] = titlemessage_initial_default;
11975 titlemessage[i] = titlemessage_default;
11978 // special case: initialize "ARG_DEFAULT" values in static default config
11979 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11980 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11982 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11985 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11986 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11987 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11990 // special case: initialize "ARG_DEFAULT" values in static default config
11991 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11992 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11994 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11995 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11996 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11998 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12001 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12005 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12009 struct XY *dst, *src;
12011 game_buttons_xy[] =
12013 { &game.button.save, &game.button.stop },
12014 { &game.button.pause2, &game.button.pause },
12015 { &game.button.load, &game.button.play },
12016 { &game.button.undo, &game.button.stop },
12017 { &game.button.redo, &game.button.play },
12023 // special case: initialize later added SETUP list size from LEVELS value
12024 if (menu.list_size[GAME_MODE_SETUP] == -1)
12025 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12027 // set default position for snapshot buttons to stop/pause/play buttons
12028 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12029 if ((*game_buttons_xy[i].dst).x == -1 &&
12030 (*game_buttons_xy[i].dst).y == -1)
12031 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12033 // --------------------------------------------------------------------------
12034 // dynamic viewports (including playfield margins, borders and alignments)
12035 // --------------------------------------------------------------------------
12037 // dynamic viewports currently only supported for landscape mode
12038 int display_width = MAX(video.display_width, video.display_height);
12039 int display_height = MIN(video.display_width, video.display_height);
12041 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12043 struct RectWithBorder *vp_window = &viewport.window[i];
12044 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12045 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12046 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12047 boolean dynamic_window_width = (vp_window->min_width != -1);
12048 boolean dynamic_window_height = (vp_window->min_height != -1);
12049 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12050 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12052 // adjust window size if min/max width/height is specified
12054 if (vp_window->min_width != -1)
12056 int window_width = display_width;
12058 // when using static window height, use aspect ratio of display
12059 if (vp_window->min_height == -1)
12060 window_width = vp_window->height * display_width / display_height;
12062 vp_window->width = MAX(vp_window->min_width, window_width);
12065 if (vp_window->min_height != -1)
12067 int window_height = display_height;
12069 // when using static window width, use aspect ratio of display
12070 if (vp_window->min_width == -1)
12071 window_height = vp_window->width * display_height / display_width;
12073 vp_window->height = MAX(vp_window->min_height, window_height);
12076 if (vp_window->max_width != -1)
12077 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12079 if (vp_window->max_height != -1)
12080 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12082 int playfield_width = vp_window->width;
12083 int playfield_height = vp_window->height;
12085 // adjust playfield size and position according to specified margins
12087 playfield_width -= vp_playfield->margin_left;
12088 playfield_width -= vp_playfield->margin_right;
12090 playfield_height -= vp_playfield->margin_top;
12091 playfield_height -= vp_playfield->margin_bottom;
12093 // adjust playfield size if min/max width/height is specified
12095 if (vp_playfield->min_width != -1)
12096 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12098 if (vp_playfield->min_height != -1)
12099 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12101 if (vp_playfield->max_width != -1)
12102 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12104 if (vp_playfield->max_height != -1)
12105 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12107 // adjust playfield position according to specified alignment
12109 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12110 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12111 else if (vp_playfield->align == ALIGN_CENTER)
12112 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12113 else if (vp_playfield->align == ALIGN_RIGHT)
12114 vp_playfield->x += playfield_width - vp_playfield->width;
12116 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12117 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12118 else if (vp_playfield->valign == VALIGN_MIDDLE)
12119 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12120 else if (vp_playfield->valign == VALIGN_BOTTOM)
12121 vp_playfield->y += playfield_height - vp_playfield->height;
12123 vp_playfield->x += vp_playfield->margin_left;
12124 vp_playfield->y += vp_playfield->margin_top;
12126 // adjust individual playfield borders if only default border is specified
12128 if (vp_playfield->border_left == -1)
12129 vp_playfield->border_left = vp_playfield->border_size;
12130 if (vp_playfield->border_right == -1)
12131 vp_playfield->border_right = vp_playfield->border_size;
12132 if (vp_playfield->border_top == -1)
12133 vp_playfield->border_top = vp_playfield->border_size;
12134 if (vp_playfield->border_bottom == -1)
12135 vp_playfield->border_bottom = vp_playfield->border_size;
12137 // set dynamic playfield borders if borders are specified as undefined
12138 // (but only if window size was dynamic and playfield size was static)
12140 if (dynamic_window_width && !dynamic_playfield_width)
12142 if (vp_playfield->border_left == -1)
12144 vp_playfield->border_left = (vp_playfield->x -
12145 vp_playfield->margin_left);
12146 vp_playfield->x -= vp_playfield->border_left;
12147 vp_playfield->width += vp_playfield->border_left;
12150 if (vp_playfield->border_right == -1)
12152 vp_playfield->border_right = (vp_window->width -
12154 vp_playfield->width -
12155 vp_playfield->margin_right);
12156 vp_playfield->width += vp_playfield->border_right;
12160 if (dynamic_window_height && !dynamic_playfield_height)
12162 if (vp_playfield->border_top == -1)
12164 vp_playfield->border_top = (vp_playfield->y -
12165 vp_playfield->margin_top);
12166 vp_playfield->y -= vp_playfield->border_top;
12167 vp_playfield->height += vp_playfield->border_top;
12170 if (vp_playfield->border_bottom == -1)
12172 vp_playfield->border_bottom = (vp_window->height -
12174 vp_playfield->height -
12175 vp_playfield->margin_bottom);
12176 vp_playfield->height += vp_playfield->border_bottom;
12180 // adjust playfield size to be a multiple of a defined alignment tile size
12182 int align_size = vp_playfield->align_size;
12183 int playfield_xtiles = vp_playfield->width / align_size;
12184 int playfield_ytiles = vp_playfield->height / align_size;
12185 int playfield_width_corrected = playfield_xtiles * align_size;
12186 int playfield_height_corrected = playfield_ytiles * align_size;
12187 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12188 i == GFX_SPECIAL_ARG_EDITOR);
12190 if (is_playfield_mode &&
12191 dynamic_playfield_width &&
12192 vp_playfield->width != playfield_width_corrected)
12194 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12196 vp_playfield->width = playfield_width_corrected;
12198 if (vp_playfield->align == ALIGN_LEFT)
12200 vp_playfield->border_left += playfield_xdiff;
12202 else if (vp_playfield->align == ALIGN_RIGHT)
12204 vp_playfield->border_right += playfield_xdiff;
12206 else if (vp_playfield->align == ALIGN_CENTER)
12208 int border_left_diff = playfield_xdiff / 2;
12209 int border_right_diff = playfield_xdiff - border_left_diff;
12211 vp_playfield->border_left += border_left_diff;
12212 vp_playfield->border_right += border_right_diff;
12216 if (is_playfield_mode &&
12217 dynamic_playfield_height &&
12218 vp_playfield->height != playfield_height_corrected)
12220 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12222 vp_playfield->height = playfield_height_corrected;
12224 if (vp_playfield->valign == VALIGN_TOP)
12226 vp_playfield->border_top += playfield_ydiff;
12228 else if (vp_playfield->align == VALIGN_BOTTOM)
12230 vp_playfield->border_right += playfield_ydiff;
12232 else if (vp_playfield->align == VALIGN_MIDDLE)
12234 int border_top_diff = playfield_ydiff / 2;
12235 int border_bottom_diff = playfield_ydiff - border_top_diff;
12237 vp_playfield->border_top += border_top_diff;
12238 vp_playfield->border_bottom += border_bottom_diff;
12242 // adjust door positions according to specified alignment
12244 for (j = 0; j < 2; j++)
12246 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12248 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12249 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12250 else if (vp_door->align == ALIGN_CENTER)
12251 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12252 else if (vp_door->align == ALIGN_RIGHT)
12253 vp_door->x += vp_window->width - vp_door->width;
12255 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12256 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12257 else if (vp_door->valign == VALIGN_MIDDLE)
12258 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12259 else if (vp_door->valign == VALIGN_BOTTOM)
12260 vp_door->y += vp_window->height - vp_door->height;
12265 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12269 struct XYTileSize *dst, *src;
12272 editor_buttons_xy[] =
12275 &editor.button.element_left, &editor.palette.element_left,
12276 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12279 &editor.button.element_middle, &editor.palette.element_middle,
12280 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12283 &editor.button.element_right, &editor.palette.element_right,
12284 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12291 // set default position for element buttons to element graphics
12292 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12294 if ((*editor_buttons_xy[i].dst).x == -1 &&
12295 (*editor_buttons_xy[i].dst).y == -1)
12297 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12299 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12301 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12305 // adjust editor palette rows and columns if specified to be dynamic
12307 if (editor.palette.cols == -1)
12309 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12310 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12311 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12313 editor.palette.cols = (vp_width - sc_width) / bt_width;
12315 if (editor.palette.x == -1)
12317 int palette_width = editor.palette.cols * bt_width + sc_width;
12319 editor.palette.x = (vp_width - palette_width) / 2;
12323 if (editor.palette.rows == -1)
12325 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12326 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12327 int tx_height = getFontHeight(FONT_TEXT_2);
12329 editor.palette.rows = (vp_height - tx_height) / bt_height;
12331 if (editor.palette.y == -1)
12333 int palette_height = editor.palette.rows * bt_height + tx_height;
12335 editor.palette.y = (vp_height - palette_height) / 2;
12340 static void LoadMenuDesignSettingsFromFilename(char *filename)
12342 static struct TitleFadingInfo tfi;
12343 static struct TitleMessageInfo tmi;
12344 static struct TokenInfo title_tokens[] =
12346 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12347 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12348 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12349 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12350 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12354 static struct TokenInfo titlemessage_tokens[] =
12356 { TYPE_INTEGER, &tmi.x, ".x" },
12357 { TYPE_INTEGER, &tmi.y, ".y" },
12358 { TYPE_INTEGER, &tmi.width, ".width" },
12359 { TYPE_INTEGER, &tmi.height, ".height" },
12360 { TYPE_INTEGER, &tmi.chars, ".chars" },
12361 { TYPE_INTEGER, &tmi.lines, ".lines" },
12362 { TYPE_INTEGER, &tmi.align, ".align" },
12363 { TYPE_INTEGER, &tmi.valign, ".valign" },
12364 { TYPE_INTEGER, &tmi.font, ".font" },
12365 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12366 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12367 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12368 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12369 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12370 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12371 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12372 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12373 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12379 struct TitleFadingInfo *info;
12384 // initialize first titles from "enter screen" definitions, if defined
12385 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12386 { &title_first_default, "menu.enter_screen.TITLE" },
12388 // initialize title screens from "next screen" definitions, if defined
12389 { &title_initial_default, "menu.next_screen.TITLE" },
12390 { &title_default, "menu.next_screen.TITLE" },
12396 struct TitleMessageInfo *array;
12399 titlemessage_arrays[] =
12401 // initialize first titles from "enter screen" definitions, if defined
12402 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12403 { titlescreen_first, "menu.enter_screen.TITLE" },
12404 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12405 { titlemessage_first, "menu.enter_screen.TITLE" },
12407 // initialize titles from "next screen" definitions, if defined
12408 { titlescreen_initial, "menu.next_screen.TITLE" },
12409 { titlescreen, "menu.next_screen.TITLE" },
12410 { titlemessage_initial, "menu.next_screen.TITLE" },
12411 { titlemessage, "menu.next_screen.TITLE" },
12413 // overwrite titles with title definitions, if defined
12414 { titlescreen_initial_first, "[title_initial]" },
12415 { titlescreen_first, "[title]" },
12416 { titlemessage_initial_first, "[title_initial]" },
12417 { titlemessage_first, "[title]" },
12419 { titlescreen_initial, "[title_initial]" },
12420 { titlescreen, "[title]" },
12421 { titlemessage_initial, "[title_initial]" },
12422 { titlemessage, "[title]" },
12424 // overwrite titles with title screen/message definitions, if defined
12425 { titlescreen_initial_first, "[titlescreen_initial]" },
12426 { titlescreen_first, "[titlescreen]" },
12427 { titlemessage_initial_first, "[titlemessage_initial]" },
12428 { titlemessage_first, "[titlemessage]" },
12430 { titlescreen_initial, "[titlescreen_initial]" },
12431 { titlescreen, "[titlescreen]" },
12432 { titlemessage_initial, "[titlemessage_initial]" },
12433 { titlemessage, "[titlemessage]" },
12437 SetupFileHash *setup_file_hash;
12440 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12443 // the following initializes hierarchical values from dynamic configuration
12445 // special case: initialize with default values that may be overwritten
12446 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12447 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12449 struct TokenIntPtrInfo menu_config[] =
12451 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12452 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12453 { "menu.list_size", &menu.list_size[i] }
12456 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12458 char *token = menu_config[j].token;
12459 char *value = getHashEntry(setup_file_hash, token);
12462 *menu_config[j].value = get_integer_from_string(value);
12466 // special case: initialize with default values that may be overwritten
12467 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12468 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12470 struct TokenIntPtrInfo menu_config[] =
12472 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12473 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12474 { "menu.list_size.INFO", &menu.list_size_info[i] }
12477 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12479 char *token = menu_config[j].token;
12480 char *value = getHashEntry(setup_file_hash, token);
12483 *menu_config[j].value = get_integer_from_string(value);
12487 // special case: initialize with default values that may be overwritten
12488 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12489 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12491 struct TokenIntPtrInfo menu_config[] =
12493 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12494 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12497 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12499 char *token = menu_config[j].token;
12500 char *value = getHashEntry(setup_file_hash, token);
12503 *menu_config[j].value = get_integer_from_string(value);
12507 // special case: initialize with default values that may be overwritten
12508 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12509 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12511 struct TokenIntPtrInfo menu_config[] =
12513 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12514 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12515 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12516 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12517 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12518 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12519 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12520 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12521 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12524 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12526 char *token = menu_config[j].token;
12527 char *value = getHashEntry(setup_file_hash, token);
12530 *menu_config[j].value = get_integer_from_string(value);
12534 // special case: initialize with default values that may be overwritten
12535 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12536 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12538 struct TokenIntPtrInfo menu_config[] =
12540 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12541 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12542 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12543 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12544 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12545 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12546 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12547 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12548 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12551 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12553 char *token = menu_config[j].token;
12554 char *value = getHashEntry(setup_file_hash, token);
12557 *menu_config[j].value = get_token_parameter_value(token, value);
12561 // special case: initialize with default values that may be overwritten
12562 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12563 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12567 char *token_prefix;
12568 struct RectWithBorder *struct_ptr;
12572 { "viewport.window", &viewport.window[i] },
12573 { "viewport.playfield", &viewport.playfield[i] },
12574 { "viewport.door_1", &viewport.door_1[i] },
12575 { "viewport.door_2", &viewport.door_2[i] }
12578 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12580 struct TokenIntPtrInfo vp_config[] =
12582 { ".x", &vp_struct[j].struct_ptr->x },
12583 { ".y", &vp_struct[j].struct_ptr->y },
12584 { ".width", &vp_struct[j].struct_ptr->width },
12585 { ".height", &vp_struct[j].struct_ptr->height },
12586 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12587 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12588 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12589 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12590 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12591 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12592 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12593 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12594 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12595 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12596 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12597 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12598 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12599 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12600 { ".align", &vp_struct[j].struct_ptr->align },
12601 { ".valign", &vp_struct[j].struct_ptr->valign }
12604 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12606 char *token = getStringCat2(vp_struct[j].token_prefix,
12607 vp_config[k].token);
12608 char *value = getHashEntry(setup_file_hash, token);
12611 *vp_config[k].value = get_token_parameter_value(token, value);
12618 // special case: initialize with default values that may be overwritten
12619 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12620 for (i = 0; title_info[i].info != NULL; i++)
12622 struct TitleFadingInfo *info = title_info[i].info;
12623 char *base_token = title_info[i].text;
12625 for (j = 0; title_tokens[j].type != -1; j++)
12627 char *token = getStringCat2(base_token, title_tokens[j].text);
12628 char *value = getHashEntry(setup_file_hash, token);
12632 int parameter_value = get_token_parameter_value(token, value);
12636 *(int *)title_tokens[j].value = (int)parameter_value;
12645 // special case: initialize with default values that may be overwritten
12646 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12647 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12649 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12650 char *base_token = titlemessage_arrays[i].text;
12652 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12654 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12655 char *value = getHashEntry(setup_file_hash, token);
12659 int parameter_value = get_token_parameter_value(token, value);
12661 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12665 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12666 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12668 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12678 // special case: check if network and preview player positions are redefined,
12679 // to compare this later against the main menu level preview being redefined
12680 struct TokenIntPtrInfo menu_config_players[] =
12682 { "main.network_players.x", &menu.main.network_players.redefined },
12683 { "main.network_players.y", &menu.main.network_players.redefined },
12684 { "main.preview_players.x", &menu.main.preview_players.redefined },
12685 { "main.preview_players.y", &menu.main.preview_players.redefined },
12686 { "preview.x", &preview.redefined },
12687 { "preview.y", &preview.redefined }
12690 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12691 *menu_config_players[i].value = FALSE;
12693 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12694 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12695 *menu_config_players[i].value = TRUE;
12697 // read (and overwrite with) values that may be specified in config file
12698 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12700 freeSetupFileHash(setup_file_hash);
12703 void LoadMenuDesignSettings(void)
12705 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12707 InitMenuDesignSettings_Static();
12708 InitMenuDesignSettings_SpecialPreProcessing();
12710 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12712 // first look for special settings configured in level series config
12713 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12715 if (fileExists(filename_base))
12716 LoadMenuDesignSettingsFromFilename(filename_base);
12719 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12721 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12722 LoadMenuDesignSettingsFromFilename(filename_local);
12724 InitMenuDesignSettings_SpecialPostProcessing();
12727 void LoadMenuDesignSettings_AfterGraphics(void)
12729 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12732 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12734 char *filename = getEditorSetupFilename();
12735 SetupFileList *setup_file_list, *list;
12736 SetupFileHash *element_hash;
12737 int num_unknown_tokens = 0;
12740 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12743 element_hash = newSetupFileHash();
12745 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12746 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12748 // determined size may be larger than needed (due to unknown elements)
12750 for (list = setup_file_list; list != NULL; list = list->next)
12753 // add space for up to 3 more elements for padding that may be needed
12754 *num_elements += 3;
12756 // free memory for old list of elements, if needed
12757 checked_free(*elements);
12759 // allocate memory for new list of elements
12760 *elements = checked_malloc(*num_elements * sizeof(int));
12763 for (list = setup_file_list; list != NULL; list = list->next)
12765 char *value = getHashEntry(element_hash, list->token);
12767 if (value == NULL) // try to find obsolete token mapping
12769 char *mapped_token = get_mapped_token(list->token);
12771 if (mapped_token != NULL)
12773 value = getHashEntry(element_hash, mapped_token);
12775 free(mapped_token);
12781 (*elements)[(*num_elements)++] = atoi(value);
12785 if (num_unknown_tokens == 0)
12788 Warn("unknown token(s) found in config file:");
12789 Warn("- config file: '%s'", filename);
12791 num_unknown_tokens++;
12794 Warn("- token: '%s'", list->token);
12798 if (num_unknown_tokens > 0)
12801 while (*num_elements % 4) // pad with empty elements, if needed
12802 (*elements)[(*num_elements)++] = EL_EMPTY;
12804 freeSetupFileList(setup_file_list);
12805 freeSetupFileHash(element_hash);
12808 for (i = 0; i < *num_elements; i++)
12809 Debug("editor", "element '%s' [%d]\n",
12810 element_info[(*elements)[i]].token_name, (*elements)[i]);
12814 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12817 SetupFileHash *setup_file_hash = NULL;
12818 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12819 char *filename_music, *filename_prefix, *filename_info;
12825 token_to_value_ptr[] =
12827 { "title_header", &tmp_music_file_info.title_header },
12828 { "artist_header", &tmp_music_file_info.artist_header },
12829 { "album_header", &tmp_music_file_info.album_header },
12830 { "year_header", &tmp_music_file_info.year_header },
12832 { "title", &tmp_music_file_info.title },
12833 { "artist", &tmp_music_file_info.artist },
12834 { "album", &tmp_music_file_info.album },
12835 { "year", &tmp_music_file_info.year },
12841 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12842 getCustomMusicFilename(basename));
12844 if (filename_music == NULL)
12847 // ---------- try to replace file extension ----------
12849 filename_prefix = getStringCopy(filename_music);
12850 if (strrchr(filename_prefix, '.') != NULL)
12851 *strrchr(filename_prefix, '.') = '\0';
12852 filename_info = getStringCat2(filename_prefix, ".txt");
12854 if (fileExists(filename_info))
12855 setup_file_hash = loadSetupFileHash(filename_info);
12857 free(filename_prefix);
12858 free(filename_info);
12860 if (setup_file_hash == NULL)
12862 // ---------- try to add file extension ----------
12864 filename_prefix = getStringCopy(filename_music);
12865 filename_info = getStringCat2(filename_prefix, ".txt");
12867 if (fileExists(filename_info))
12868 setup_file_hash = loadSetupFileHash(filename_info);
12870 free(filename_prefix);
12871 free(filename_info);
12874 if (setup_file_hash == NULL)
12877 // ---------- music file info found ----------
12879 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12881 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12883 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12885 *token_to_value_ptr[i].value_ptr =
12886 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12889 tmp_music_file_info.basename = getStringCopy(basename);
12890 tmp_music_file_info.music = music;
12891 tmp_music_file_info.is_sound = is_sound;
12893 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12894 *new_music_file_info = tmp_music_file_info;
12896 return new_music_file_info;
12899 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12901 return get_music_file_info_ext(basename, music, FALSE);
12904 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12906 return get_music_file_info_ext(basename, sound, TRUE);
12909 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12910 char *basename, boolean is_sound)
12912 for (; list != NULL; list = list->next)
12913 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12919 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12921 return music_info_listed_ext(list, basename, FALSE);
12924 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12926 return music_info_listed_ext(list, basename, TRUE);
12929 void LoadMusicInfo(void)
12931 char *music_directory = getCustomMusicDirectory_NoConf();
12932 int num_music = getMusicListSize();
12933 int num_music_noconf = 0;
12934 int num_sounds = getSoundListSize();
12936 DirectoryEntry *dir_entry;
12937 struct FileInfo *music, *sound;
12938 struct MusicFileInfo *next, **new;
12941 while (music_file_info != NULL)
12943 next = music_file_info->next;
12945 checked_free(music_file_info->basename);
12947 checked_free(music_file_info->title_header);
12948 checked_free(music_file_info->artist_header);
12949 checked_free(music_file_info->album_header);
12950 checked_free(music_file_info->year_header);
12952 checked_free(music_file_info->title);
12953 checked_free(music_file_info->artist);
12954 checked_free(music_file_info->album);
12955 checked_free(music_file_info->year);
12957 free(music_file_info);
12959 music_file_info = next;
12962 new = &music_file_info;
12964 for (i = 0; i < num_music; i++)
12966 music = getMusicListEntry(i);
12968 if (music->filename == NULL)
12971 if (strEqual(music->filename, UNDEFINED_FILENAME))
12974 // a configured file may be not recognized as music
12975 if (!FileIsMusic(music->filename))
12978 if (!music_info_listed(music_file_info, music->filename))
12980 *new = get_music_file_info(music->filename, i);
12983 new = &(*new)->next;
12987 if ((dir = openDirectory(music_directory)) == NULL)
12989 Warn("cannot read music directory '%s'", music_directory);
12994 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12996 char *basename = dir_entry->basename;
12997 boolean music_already_used = FALSE;
13000 // skip all music files that are configured in music config file
13001 for (i = 0; i < num_music; i++)
13003 music = getMusicListEntry(i);
13005 if (music->filename == NULL)
13008 if (strEqual(basename, music->filename))
13010 music_already_used = TRUE;
13015 if (music_already_used)
13018 if (!FileIsMusic(dir_entry->filename))
13021 if (!music_info_listed(music_file_info, basename))
13023 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13026 new = &(*new)->next;
13029 num_music_noconf++;
13032 closeDirectory(dir);
13034 for (i = 0; i < num_sounds; i++)
13036 sound = getSoundListEntry(i);
13038 if (sound->filename == NULL)
13041 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13044 // a configured file may be not recognized as sound
13045 if (!FileIsSound(sound->filename))
13048 if (!sound_info_listed(music_file_info, sound->filename))
13050 *new = get_sound_file_info(sound->filename, i);
13052 new = &(*new)->next;
13056 // add pointers to previous list nodes
13058 struct MusicFileInfo *node = music_file_info;
13060 while (node != NULL)
13063 node->next->prev = node;
13069 static void add_helpanim_entry(int element, int action, int direction,
13070 int delay, int *num_list_entries)
13072 struct HelpAnimInfo *new_list_entry;
13073 (*num_list_entries)++;
13076 checked_realloc(helpanim_info,
13077 *num_list_entries * sizeof(struct HelpAnimInfo));
13078 new_list_entry = &helpanim_info[*num_list_entries - 1];
13080 new_list_entry->element = element;
13081 new_list_entry->action = action;
13082 new_list_entry->direction = direction;
13083 new_list_entry->delay = delay;
13086 static void print_unknown_token(char *filename, char *token, int token_nr)
13091 Warn("unknown token(s) found in config file:");
13092 Warn("- config file: '%s'", filename);
13095 Warn("- token: '%s'", token);
13098 static void print_unknown_token_end(int token_nr)
13104 void LoadHelpAnimInfo(void)
13106 char *filename = getHelpAnimFilename();
13107 SetupFileList *setup_file_list = NULL, *list;
13108 SetupFileHash *element_hash, *action_hash, *direction_hash;
13109 int num_list_entries = 0;
13110 int num_unknown_tokens = 0;
13113 if (fileExists(filename))
13114 setup_file_list = loadSetupFileList(filename);
13116 if (setup_file_list == NULL)
13118 // use reliable default values from static configuration
13119 SetupFileList *insert_ptr;
13121 insert_ptr = setup_file_list =
13122 newSetupFileList(helpanim_config[0].token,
13123 helpanim_config[0].value);
13125 for (i = 1; helpanim_config[i].token; i++)
13126 insert_ptr = addListEntry(insert_ptr,
13127 helpanim_config[i].token,
13128 helpanim_config[i].value);
13131 element_hash = newSetupFileHash();
13132 action_hash = newSetupFileHash();
13133 direction_hash = newSetupFileHash();
13135 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13136 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13138 for (i = 0; i < NUM_ACTIONS; i++)
13139 setHashEntry(action_hash, element_action_info[i].suffix,
13140 i_to_a(element_action_info[i].value));
13142 // do not store direction index (bit) here, but direction value!
13143 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13144 setHashEntry(direction_hash, element_direction_info[i].suffix,
13145 i_to_a(1 << element_direction_info[i].value));
13147 for (list = setup_file_list; list != NULL; list = list->next)
13149 char *element_token, *action_token, *direction_token;
13150 char *element_value, *action_value, *direction_value;
13151 int delay = atoi(list->value);
13153 if (strEqual(list->token, "end"))
13155 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13160 /* first try to break element into element/action/direction parts;
13161 if this does not work, also accept combined "element[.act][.dir]"
13162 elements (like "dynamite.active"), which are unique elements */
13164 if (strchr(list->token, '.') == NULL) // token contains no '.'
13166 element_value = getHashEntry(element_hash, list->token);
13167 if (element_value != NULL) // element found
13168 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13169 &num_list_entries);
13172 // no further suffixes found -- this is not an element
13173 print_unknown_token(filename, list->token, num_unknown_tokens++);
13179 // token has format "<prefix>.<something>"
13181 action_token = strchr(list->token, '.'); // suffix may be action ...
13182 direction_token = action_token; // ... or direction
13184 element_token = getStringCopy(list->token);
13185 *strchr(element_token, '.') = '\0';
13187 element_value = getHashEntry(element_hash, element_token);
13189 if (element_value == NULL) // this is no element
13191 element_value = getHashEntry(element_hash, list->token);
13192 if (element_value != NULL) // combined element found
13193 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13194 &num_list_entries);
13196 print_unknown_token(filename, list->token, num_unknown_tokens++);
13198 free(element_token);
13203 action_value = getHashEntry(action_hash, action_token);
13205 if (action_value != NULL) // action found
13207 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13208 &num_list_entries);
13210 free(element_token);
13215 direction_value = getHashEntry(direction_hash, direction_token);
13217 if (direction_value != NULL) // direction found
13219 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13220 &num_list_entries);
13222 free(element_token);
13227 if (strchr(action_token + 1, '.') == NULL)
13229 // no further suffixes found -- this is not an action nor direction
13231 element_value = getHashEntry(element_hash, list->token);
13232 if (element_value != NULL) // combined element found
13233 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13234 &num_list_entries);
13236 print_unknown_token(filename, list->token, num_unknown_tokens++);
13238 free(element_token);
13243 // token has format "<prefix>.<suffix>.<something>"
13245 direction_token = strchr(action_token + 1, '.');
13247 action_token = getStringCopy(action_token);
13248 *strchr(action_token + 1, '.') = '\0';
13250 action_value = getHashEntry(action_hash, action_token);
13252 if (action_value == NULL) // this is no action
13254 element_value = getHashEntry(element_hash, list->token);
13255 if (element_value != NULL) // combined element found
13256 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13257 &num_list_entries);
13259 print_unknown_token(filename, list->token, num_unknown_tokens++);
13261 free(element_token);
13262 free(action_token);
13267 direction_value = getHashEntry(direction_hash, direction_token);
13269 if (direction_value != NULL) // direction found
13271 add_helpanim_entry(atoi(element_value), atoi(action_value),
13272 atoi(direction_value), delay, &num_list_entries);
13274 free(element_token);
13275 free(action_token);
13280 // this is no direction
13282 element_value = getHashEntry(element_hash, list->token);
13283 if (element_value != NULL) // combined element found
13284 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13285 &num_list_entries);
13287 print_unknown_token(filename, list->token, num_unknown_tokens++);
13289 free(element_token);
13290 free(action_token);
13293 print_unknown_token_end(num_unknown_tokens);
13295 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13296 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13298 freeSetupFileList(setup_file_list);
13299 freeSetupFileHash(element_hash);
13300 freeSetupFileHash(action_hash);
13301 freeSetupFileHash(direction_hash);
13304 for (i = 0; i < num_list_entries; i++)
13305 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13306 EL_NAME(helpanim_info[i].element),
13307 helpanim_info[i].element,
13308 helpanim_info[i].action,
13309 helpanim_info[i].direction,
13310 helpanim_info[i].delay);
13314 void LoadHelpTextInfo(void)
13316 char *filename = getHelpTextFilename();
13319 if (helptext_info != NULL)
13321 freeSetupFileHash(helptext_info);
13322 helptext_info = NULL;
13325 if (fileExists(filename))
13326 helptext_info = loadSetupFileHash(filename);
13328 if (helptext_info == NULL)
13330 // use reliable default values from static configuration
13331 helptext_info = newSetupFileHash();
13333 for (i = 0; helptext_config[i].token; i++)
13334 setHashEntry(helptext_info,
13335 helptext_config[i].token,
13336 helptext_config[i].value);
13340 BEGIN_HASH_ITERATION(helptext_info, itr)
13342 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13343 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13345 END_HASH_ITERATION(hash, itr)
13350 // ----------------------------------------------------------------------------
13352 // ----------------------------------------------------------------------------
13354 #define MAX_NUM_CONVERT_LEVELS 1000
13356 void ConvertLevels(void)
13358 static LevelDirTree *convert_leveldir = NULL;
13359 static int convert_level_nr = -1;
13360 static int num_levels_handled = 0;
13361 static int num_levels_converted = 0;
13362 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13365 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13366 global.convert_leveldir);
13368 if (convert_leveldir == NULL)
13369 Fail("no such level identifier: '%s'", global.convert_leveldir);
13371 leveldir_current = convert_leveldir;
13373 if (global.convert_level_nr != -1)
13375 convert_leveldir->first_level = global.convert_level_nr;
13376 convert_leveldir->last_level = global.convert_level_nr;
13379 convert_level_nr = convert_leveldir->first_level;
13381 PrintLine("=", 79);
13382 Print("Converting levels\n");
13383 PrintLine("-", 79);
13384 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13385 Print("Level series name: '%s'\n", convert_leveldir->name);
13386 Print("Level series author: '%s'\n", convert_leveldir->author);
13387 Print("Number of levels: %d\n", convert_leveldir->levels);
13388 PrintLine("=", 79);
13391 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13392 levels_failed[i] = FALSE;
13394 while (convert_level_nr <= convert_leveldir->last_level)
13396 char *level_filename;
13399 level_nr = convert_level_nr++;
13401 Print("Level %03d: ", level_nr);
13403 LoadLevel(level_nr);
13404 if (level.no_level_file || level.no_valid_file)
13406 Print("(no level)\n");
13410 Print("converting level ... ");
13413 // special case: conversion of some EMC levels as requested by ACME
13414 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13417 level_filename = getDefaultLevelFilename(level_nr);
13418 new_level = !fileExists(level_filename);
13422 SaveLevel(level_nr);
13424 num_levels_converted++;
13426 Print("converted.\n");
13430 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13431 levels_failed[level_nr] = TRUE;
13433 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13436 num_levels_handled++;
13440 PrintLine("=", 79);
13441 Print("Number of levels handled: %d\n", num_levels_handled);
13442 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13443 (num_levels_handled ?
13444 num_levels_converted * 100 / num_levels_handled : 0));
13445 PrintLine("-", 79);
13446 Print("Summary (for automatic parsing by scripts):\n");
13447 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13448 convert_leveldir->identifier, num_levels_converted,
13449 num_levels_handled,
13450 (num_levels_handled ?
13451 num_levels_converted * 100 / num_levels_handled : 0));
13453 if (num_levels_handled != num_levels_converted)
13455 Print(", FAILED:");
13456 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13457 if (levels_failed[i])
13462 PrintLine("=", 79);
13464 CloseAllAndExit(0);
13468 // ----------------------------------------------------------------------------
13469 // create and save images for use in level sketches (raw BMP format)
13470 // ----------------------------------------------------------------------------
13472 void CreateLevelSketchImages(void)
13478 InitElementPropertiesGfxElement();
13480 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13481 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13483 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13485 int element = getMappedElement(i);
13486 char basename1[16];
13487 char basename2[16];
13491 sprintf(basename1, "%04d.bmp", i);
13492 sprintf(basename2, "%04ds.bmp", i);
13494 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13495 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13497 DrawSizedElement(0, 0, element, TILESIZE);
13498 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13500 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13501 Fail("cannot save level sketch image file '%s'", filename1);
13503 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13504 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13506 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13507 Fail("cannot save level sketch image file '%s'", filename2);
13512 // create corresponding SQL statements (for normal and small images)
13515 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13516 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13519 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13520 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13522 // optional: create content for forum level sketch demonstration post
13524 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13527 FreeBitmap(bitmap1);
13528 FreeBitmap(bitmap2);
13531 fprintf(stderr, "\n");
13533 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13535 CloseAllAndExit(0);
13539 // ----------------------------------------------------------------------------
13540 // create and save images for element collecting animations (raw BMP format)
13541 // ----------------------------------------------------------------------------
13543 static boolean createCollectImage(int element)
13545 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13548 void CreateCollectElementImages(void)
13552 int anim_frames = num_steps - 1;
13553 int tile_size = TILESIZE;
13554 int anim_width = tile_size * anim_frames;
13555 int anim_height = tile_size;
13556 int num_collect_images = 0;
13557 int pos_collect_images = 0;
13559 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13560 if (createCollectImage(i))
13561 num_collect_images++;
13563 Info("Creating %d element collecting animation images ...",
13564 num_collect_images);
13566 int dst_width = anim_width * 2;
13567 int dst_height = anim_height * num_collect_images / 2;
13568 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13569 char *basename_bmp = "RocksCollect.bmp";
13570 char *basename_png = "RocksCollect.png";
13571 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13572 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13573 int len_filename_bmp = strlen(filename_bmp);
13574 int len_filename_png = strlen(filename_png);
13575 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13576 char cmd_convert[max_command_len];
13578 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13582 // force using RGBA surface for destination bitmap
13583 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13584 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13586 dst_bitmap->surface =
13587 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13589 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13591 if (!createCollectImage(i))
13594 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13595 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13596 int graphic = el2img(i);
13597 char *token_name = element_info[i].token_name;
13598 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13599 Bitmap *src_bitmap;
13602 Info("- creating collecting image for '%s' ...", token_name);
13604 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13606 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13607 tile_size, tile_size, 0, 0);
13609 // force using RGBA surface for temporary bitmap (using transparent black)
13610 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13611 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13613 tmp_bitmap->surface =
13614 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13616 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13618 for (j = 0; j < anim_frames; j++)
13620 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13621 int frame_size = frame_size_final * num_steps;
13622 int offset = (tile_size - frame_size_final) / 2;
13623 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13625 while (frame_size > frame_size_final)
13629 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13631 FreeBitmap(frame_bitmap);
13633 frame_bitmap = half_bitmap;
13636 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13637 frame_size_final, frame_size_final,
13638 dst_x + j * tile_size + offset, dst_y + offset);
13640 FreeBitmap(frame_bitmap);
13643 tmp_bitmap->surface_masked = NULL;
13645 FreeBitmap(tmp_bitmap);
13647 pos_collect_images++;
13650 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13651 Fail("cannot save element collecting image file '%s'", filename_bmp);
13653 FreeBitmap(dst_bitmap);
13655 Info("Converting image file from BMP to PNG ...");
13657 if (system(cmd_convert) != 0)
13658 Fail("converting image file failed");
13660 unlink(filename_bmp);
13664 CloseAllAndExit(0);
13668 // ----------------------------------------------------------------------------
13669 // create and save images for custom and group elements (raw BMP format)
13670 // ----------------------------------------------------------------------------
13672 void CreateCustomElementImages(char *directory)
13674 char *src_basename = "RocksCE-template.ilbm";
13675 char *dst_basename = "RocksCE.bmp";
13676 char *src_filename = getPath2(directory, src_basename);
13677 char *dst_filename = getPath2(directory, dst_basename);
13678 Bitmap *src_bitmap;
13680 int yoffset_ce = 0;
13681 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13684 InitVideoDefaults();
13686 ReCreateBitmap(&backbuffer, video.width, video.height);
13688 src_bitmap = LoadImage(src_filename);
13690 bitmap = CreateBitmap(TILEX * 16 * 2,
13691 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13694 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13701 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13702 TILEX * x, TILEY * y + yoffset_ce);
13704 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13706 TILEX * x + TILEX * 16,
13707 TILEY * y + yoffset_ce);
13709 for (j = 2; j >= 0; j--)
13713 BlitBitmap(src_bitmap, bitmap,
13714 TILEX + c * 7, 0, 6, 10,
13715 TILEX * x + 6 + j * 7,
13716 TILEY * y + 11 + yoffset_ce);
13718 BlitBitmap(src_bitmap, bitmap,
13719 TILEX + c * 8, TILEY, 6, 10,
13720 TILEX * 16 + TILEX * x + 6 + j * 8,
13721 TILEY * y + 10 + yoffset_ce);
13727 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13734 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13735 TILEX * x, TILEY * y + yoffset_ge);
13737 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13739 TILEX * x + TILEX * 16,
13740 TILEY * y + yoffset_ge);
13742 for (j = 1; j >= 0; j--)
13746 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13747 TILEX * x + 6 + j * 10,
13748 TILEY * y + 11 + yoffset_ge);
13750 BlitBitmap(src_bitmap, bitmap,
13751 TILEX + c * 8, TILEY + 12, 6, 10,
13752 TILEX * 16 + TILEX * x + 10 + j * 8,
13753 TILEY * y + 10 + yoffset_ge);
13759 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13760 Fail("cannot save CE graphics file '%s'", dst_filename);
13762 FreeBitmap(bitmap);
13764 CloseAllAndExit(0);