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),
909 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
913 EL_MM_STEEL_BLOCK, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 &li.mm_time_block, 75
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
920 &li.score[SC_ELEM_BONUS], 10
923 // ---------- unused values -------------------------------------------------
926 EL_UNKNOWN, SAVE_CONF_NEVER,
927 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
928 &li.score[SC_UNKNOWN_15], 10
938 static struct LevelFileConfigInfo chunk_config_NOTE[] =
942 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
943 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
947 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
948 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
953 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
954 &xx_envelope.autowrap, FALSE
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
959 &xx_envelope.centered, FALSE
964 TYPE_STRING, CONF_VALUE_BYTES(1),
965 &xx_envelope.text, -1, NULL,
966 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
967 &xx_default_string_empty[0]
977 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
981 TYPE_STRING, CONF_VALUE_BYTES(1),
982 &xx_ei.description[0], -1,
983 &yy_ei.description[0],
984 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
985 &xx_default_description[0]
990 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
991 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
992 &yy_ei.properties[EP_BITFIELD_BASE_NR]
994 #if ENABLE_RESERVED_CODE
995 // (reserved for later use)
998 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
999 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1000 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1007 &xx_ei.use_gfx_element, FALSE,
1008 &yy_ei.use_gfx_element
1012 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1013 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1014 &yy_ei.gfx_element_initial
1019 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1020 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1021 &yy_ei.access_direction
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1027 &xx_ei.collect_score_initial, 10,
1028 &yy_ei.collect_score_initial
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1033 &xx_ei.collect_count_initial, 1,
1034 &yy_ei.collect_count_initial
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1040 &xx_ei.ce_value_fixed_initial, 0,
1041 &yy_ei.ce_value_fixed_initial
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1046 &xx_ei.ce_value_random_initial, 0,
1047 &yy_ei.ce_value_random_initial
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1052 &xx_ei.use_last_ce_value, FALSE,
1053 &yy_ei.use_last_ce_value
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1059 &xx_ei.push_delay_fixed, 8,
1060 &yy_ei.push_delay_fixed
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1065 &xx_ei.push_delay_random, 8,
1066 &yy_ei.push_delay_random
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1071 &xx_ei.drop_delay_fixed, 0,
1072 &yy_ei.drop_delay_fixed
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1077 &xx_ei.drop_delay_random, 0,
1078 &yy_ei.drop_delay_random
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1083 &xx_ei.move_delay_fixed, 0,
1084 &yy_ei.move_delay_fixed
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1089 &xx_ei.move_delay_random, 0,
1090 &yy_ei.move_delay_random
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1095 &xx_ei.step_delay_fixed, 0,
1096 &yy_ei.step_delay_fixed
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1101 &xx_ei.step_delay_random, 0,
1102 &yy_ei.step_delay_random
1107 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1108 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1113 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1114 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1115 &yy_ei.move_direction_initial
1119 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1120 &xx_ei.move_stepsize, TILEX / 8,
1121 &yy_ei.move_stepsize
1126 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1127 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1128 &yy_ei.move_enter_element
1132 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1133 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1134 &yy_ei.move_leave_element
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1139 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1140 &yy_ei.move_leave_type
1145 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1146 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1147 &yy_ei.slippery_type
1152 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1153 &xx_ei.explosion_type, EXPLODES_3X3,
1154 &yy_ei.explosion_type
1158 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1159 &xx_ei.explosion_delay, 16,
1160 &yy_ei.explosion_delay
1164 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1165 &xx_ei.ignition_delay, 8,
1166 &yy_ei.ignition_delay
1171 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1172 &xx_ei.content, EL_EMPTY_SPACE,
1174 &xx_num_contents, 1, 1
1177 // ---------- "num_change_pages" must be the last entry ---------------------
1180 -1, SAVE_CONF_ALWAYS,
1181 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1182 &xx_ei.num_change_pages, 1,
1183 &yy_ei.num_change_pages
1194 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1196 // ---------- "current_change_page" must be the first entry -----------------
1199 -1, SAVE_CONF_ALWAYS,
1200 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1201 &xx_current_change_page, -1
1204 // ---------- (the remaining entries can be in any order) -------------------
1208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1209 &xx_change.can_change, FALSE
1214 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1215 &xx_event_bits[0], 0
1219 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1220 &xx_event_bits[1], 0
1225 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1226 &xx_change.trigger_player, CH_PLAYER_ANY
1230 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1231 &xx_change.trigger_side, CH_SIDE_ANY
1235 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1236 &xx_change.trigger_page, CH_PAGE_ANY
1241 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1242 &xx_change.target_element, EL_EMPTY_SPACE
1247 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 &xx_change.delay_fixed, 0
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1253 &xx_change.delay_random, 0
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1258 &xx_change.delay_frames, FRAMES_PER_SECOND
1263 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1264 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1269 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1270 &xx_change.explode, FALSE
1274 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1275 &xx_change.use_target_content, FALSE
1279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1280 &xx_change.only_if_complete, FALSE
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1285 &xx_change.use_random_replace, FALSE
1289 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1290 &xx_change.random_percentage, 100
1294 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1295 &xx_change.replace_when, CP_WHEN_EMPTY
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1301 &xx_change.has_action, FALSE
1305 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1306 &xx_change.action_type, CA_NO_ACTION
1310 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1311 &xx_change.action_mode, CA_MODE_UNDEFINED
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1316 &xx_change.action_arg, CA_ARG_UNDEFINED
1321 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1322 &xx_change.action_element, EL_EMPTY_SPACE
1327 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1328 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1329 &xx_num_contents, 1, 1
1339 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1343 TYPE_STRING, CONF_VALUE_BYTES(1),
1344 &xx_ei.description[0], -1, NULL,
1345 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1346 &xx_default_description[0]
1351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1352 &xx_ei.use_gfx_element, FALSE
1356 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1357 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1362 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1363 &xx_group.choice_mode, ANIM_RANDOM
1368 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1369 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1370 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1380 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1385 &xx_ei.use_gfx_element, FALSE
1389 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1390 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1400 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1405 &li.block_snap_field, TRUE
1409 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1410 &li.continuous_snapping, TRUE
1414 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1420 &li.use_start_element[0], FALSE
1424 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1425 &li.start_element[0], EL_PLAYER_1
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1430 &li.use_artwork_element[0], FALSE
1434 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1435 &li.artwork_element[0], EL_PLAYER_1
1439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1440 &li.use_explosion_element[0], FALSE
1444 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1445 &li.explosion_element[0], EL_PLAYER_1
1460 filetype_id_list[] =
1462 { LEVEL_FILE_TYPE_RND, "RND" },
1463 { LEVEL_FILE_TYPE_BD, "BD" },
1464 { LEVEL_FILE_TYPE_EM, "EM" },
1465 { LEVEL_FILE_TYPE_SP, "SP" },
1466 { LEVEL_FILE_TYPE_DX, "DX" },
1467 { LEVEL_FILE_TYPE_SB, "SB" },
1468 { LEVEL_FILE_TYPE_DC, "DC" },
1469 { LEVEL_FILE_TYPE_MM, "MM" },
1470 { LEVEL_FILE_TYPE_MM, "DF" },
1475 // ============================================================================
1476 // level file functions
1477 // ============================================================================
1479 static boolean check_special_flags(char *flag)
1481 if (strEqual(options.special_flags, flag) ||
1482 strEqual(leveldir_current->special_flags, flag))
1488 static struct DateInfo getCurrentDate(void)
1490 time_t epoch_seconds = time(NULL);
1491 struct tm *now = localtime(&epoch_seconds);
1492 struct DateInfo date;
1494 date.year = now->tm_year + 1900;
1495 date.month = now->tm_mon + 1;
1496 date.day = now->tm_mday;
1498 date.src = DATE_SRC_CLOCK;
1503 static void resetEventFlags(struct ElementChangeInfo *change)
1507 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1508 change->has_event[i] = FALSE;
1511 static void resetEventBits(void)
1515 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1516 xx_event_bits[i] = 0;
1519 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1523 /* important: only change event flag if corresponding event bit is set
1524 (this is because all xx_event_bits[] values are loaded separately,
1525 and all xx_event_bits[] values are set back to zero before loading
1526 another value xx_event_bits[x] (each value representing 32 flags)) */
1528 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1529 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1530 change->has_event[i] = TRUE;
1533 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1537 /* in contrast to the above function setEventFlagsFromEventBits(), it
1538 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1539 depending on the corresponding change->has_event[i] values here, as
1540 all xx_event_bits[] values are reset in resetEventBits() before */
1542 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1543 if (change->has_event[i])
1544 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1547 static char *getDefaultElementDescription(struct ElementInfo *ei)
1549 static char description[MAX_ELEMENT_NAME_LEN + 1];
1550 char *default_description = (ei->custom_description != NULL ?
1551 ei->custom_description :
1552 ei->editor_description);
1555 // always start with reliable default values
1556 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1557 description[i] = '\0';
1559 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1560 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1562 return &description[0];
1565 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1567 char *default_description = getDefaultElementDescription(ei);
1570 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1571 ei->description[i] = default_description[i];
1574 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1578 for (i = 0; conf[i].data_type != -1; i++)
1580 int default_value = conf[i].default_value;
1581 int data_type = conf[i].data_type;
1582 int conf_type = conf[i].conf_type;
1583 int byte_mask = conf_type & CONF_MASK_BYTES;
1585 if (byte_mask == CONF_MASK_MULTI_BYTES)
1587 int default_num_entities = conf[i].default_num_entities;
1588 int max_num_entities = conf[i].max_num_entities;
1590 *(int *)(conf[i].num_entities) = default_num_entities;
1592 if (data_type == TYPE_STRING)
1594 char *default_string = conf[i].default_string;
1595 char *string = (char *)(conf[i].value);
1597 strncpy(string, default_string, max_num_entities);
1599 else if (data_type == TYPE_ELEMENT_LIST)
1601 int *element_array = (int *)(conf[i].value);
1604 for (j = 0; j < max_num_entities; j++)
1605 element_array[j] = default_value;
1607 else if (data_type == TYPE_CONTENT_LIST)
1609 struct Content *content = (struct Content *)(conf[i].value);
1612 for (c = 0; c < max_num_entities; c++)
1613 for (y = 0; y < 3; y++)
1614 for (x = 0; x < 3; x++)
1615 content[c].e[x][y] = default_value;
1618 else // constant size configuration data (1, 2 or 4 bytes)
1620 if (data_type == TYPE_BOOLEAN)
1621 *(boolean *)(conf[i].value) = default_value;
1623 *(int *) (conf[i].value) = default_value;
1628 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1632 for (i = 0; conf[i].data_type != -1; i++)
1634 int data_type = conf[i].data_type;
1635 int conf_type = conf[i].conf_type;
1636 int byte_mask = conf_type & CONF_MASK_BYTES;
1638 if (byte_mask == CONF_MASK_MULTI_BYTES)
1640 int max_num_entities = conf[i].max_num_entities;
1642 if (data_type == TYPE_STRING)
1644 char *string = (char *)(conf[i].value);
1645 char *string_copy = (char *)(conf[i].value_copy);
1647 strncpy(string_copy, string, max_num_entities);
1649 else if (data_type == TYPE_ELEMENT_LIST)
1651 int *element_array = (int *)(conf[i].value);
1652 int *element_array_copy = (int *)(conf[i].value_copy);
1655 for (j = 0; j < max_num_entities; j++)
1656 element_array_copy[j] = element_array[j];
1658 else if (data_type == TYPE_CONTENT_LIST)
1660 struct Content *content = (struct Content *)(conf[i].value);
1661 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1664 for (c = 0; c < max_num_entities; c++)
1665 for (y = 0; y < 3; y++)
1666 for (x = 0; x < 3; x++)
1667 content_copy[c].e[x][y] = content[c].e[x][y];
1670 else // constant size configuration data (1, 2 or 4 bytes)
1672 if (data_type == TYPE_BOOLEAN)
1673 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1675 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1680 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1684 xx_ei = *ei_from; // copy element data into temporary buffer
1685 yy_ei = *ei_to; // copy element data into temporary buffer
1687 copyConfigFromConfigList(chunk_config_CUSX_base);
1692 // ---------- reinitialize and copy change pages ----------
1694 ei_to->num_change_pages = ei_from->num_change_pages;
1695 ei_to->current_change_page = ei_from->current_change_page;
1697 setElementChangePages(ei_to, ei_to->num_change_pages);
1699 for (i = 0; i < ei_to->num_change_pages; i++)
1700 ei_to->change_page[i] = ei_from->change_page[i];
1702 // ---------- copy group element info ----------
1703 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1704 *ei_to->group = *ei_from->group;
1706 // mark this custom element as modified
1707 ei_to->modified_settings = TRUE;
1710 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1712 int change_page_size = sizeof(struct ElementChangeInfo);
1714 ei->num_change_pages = MAX(1, change_pages);
1717 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1719 if (ei->current_change_page >= ei->num_change_pages)
1720 ei->current_change_page = ei->num_change_pages - 1;
1722 ei->change = &ei->change_page[ei->current_change_page];
1725 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1727 xx_change = *change; // copy change data into temporary buffer
1729 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1731 *change = xx_change;
1733 resetEventFlags(change);
1735 change->direct_action = 0;
1736 change->other_action = 0;
1738 change->pre_change_function = NULL;
1739 change->change_function = NULL;
1740 change->post_change_function = NULL;
1743 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1747 li = *level; // copy level data into temporary buffer
1748 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1749 *level = li; // copy temporary buffer back to level data
1751 setLevelInfoToDefaults_EM();
1752 setLevelInfoToDefaults_SP();
1753 setLevelInfoToDefaults_MM();
1755 level->native_em_level = &native_em_level;
1756 level->native_sp_level = &native_sp_level;
1757 level->native_mm_level = &native_mm_level;
1759 level->file_version = FILE_VERSION_ACTUAL;
1760 level->game_version = GAME_VERSION_ACTUAL;
1762 level->creation_date = getCurrentDate();
1764 level->encoding_16bit_field = TRUE;
1765 level->encoding_16bit_yamyam = TRUE;
1766 level->encoding_16bit_amoeba = TRUE;
1768 // clear level name and level author string buffers
1769 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1770 level->name[i] = '\0';
1771 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1772 level->author[i] = '\0';
1774 // set level name and level author to default values
1775 strcpy(level->name, NAMELESS_LEVEL_NAME);
1776 strcpy(level->author, ANONYMOUS_NAME);
1778 // set level playfield to playable default level with player and exit
1779 for (x = 0; x < MAX_LEV_FIELDX; x++)
1780 for (y = 0; y < MAX_LEV_FIELDY; y++)
1781 level->field[x][y] = EL_SAND;
1783 level->field[0][0] = EL_PLAYER_1;
1784 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1786 BorderElement = EL_STEELWALL;
1788 // detect custom elements when loading them
1789 level->file_has_custom_elements = FALSE;
1791 // set all bug compatibility flags to "false" => do not emulate this bug
1792 level->use_action_after_change_bug = FALSE;
1794 if (leveldir_current)
1796 // try to determine better author name than 'anonymous'
1797 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1799 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1800 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1804 switch (LEVELCLASS(leveldir_current))
1806 case LEVELCLASS_TUTORIAL:
1807 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1810 case LEVELCLASS_CONTRIB:
1811 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1812 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1815 case LEVELCLASS_PRIVATE:
1816 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1817 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1821 // keep default value
1828 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1830 static boolean clipboard_elements_initialized = FALSE;
1833 InitElementPropertiesStatic();
1835 li = *level; // copy level data into temporary buffer
1836 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1837 *level = li; // copy temporary buffer back to level data
1839 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1842 struct ElementInfo *ei = &element_info[element];
1844 // never initialize clipboard elements after the very first time
1845 // (to be able to use clipboard elements between several levels)
1846 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1849 if (IS_ENVELOPE(element))
1851 int envelope_nr = element - EL_ENVELOPE_1;
1853 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1855 level->envelope[envelope_nr] = xx_envelope;
1858 if (IS_CUSTOM_ELEMENT(element) ||
1859 IS_GROUP_ELEMENT(element) ||
1860 IS_INTERNAL_ELEMENT(element))
1862 xx_ei = *ei; // copy element data into temporary buffer
1864 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1869 setElementChangePages(ei, 1);
1870 setElementChangeInfoToDefaults(ei->change);
1872 if (IS_CUSTOM_ELEMENT(element) ||
1873 IS_GROUP_ELEMENT(element) ||
1874 IS_INTERNAL_ELEMENT(element))
1876 setElementDescriptionToDefault(ei);
1878 ei->modified_settings = FALSE;
1881 if (IS_CUSTOM_ELEMENT(element) ||
1882 IS_INTERNAL_ELEMENT(element))
1884 // internal values used in level editor
1886 ei->access_type = 0;
1887 ei->access_layer = 0;
1888 ei->access_protected = 0;
1889 ei->walk_to_action = 0;
1890 ei->smash_targets = 0;
1893 ei->can_explode_by_fire = FALSE;
1894 ei->can_explode_smashed = FALSE;
1895 ei->can_explode_impact = FALSE;
1897 ei->current_change_page = 0;
1900 if (IS_GROUP_ELEMENT(element) ||
1901 IS_INTERNAL_ELEMENT(element))
1903 struct ElementGroupInfo *group;
1905 // initialize memory for list of elements in group
1906 if (ei->group == NULL)
1907 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1911 xx_group = *group; // copy group data into temporary buffer
1913 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1918 if (IS_EMPTY_ELEMENT(element) ||
1919 IS_INTERNAL_ELEMENT(element))
1921 xx_ei = *ei; // copy element data into temporary buffer
1923 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1929 clipboard_elements_initialized = TRUE;
1932 static void setLevelInfoToDefaults(struct LevelInfo *level,
1933 boolean level_info_only,
1934 boolean reset_file_status)
1936 setLevelInfoToDefaults_Level(level);
1938 if (!level_info_only)
1939 setLevelInfoToDefaults_Elements(level);
1941 if (reset_file_status)
1943 level->no_valid_file = FALSE;
1944 level->no_level_file = FALSE;
1947 level->changed = FALSE;
1950 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1952 level_file_info->nr = 0;
1953 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1954 level_file_info->packed = FALSE;
1956 setString(&level_file_info->basename, NULL);
1957 setString(&level_file_info->filename, NULL);
1960 int getMappedElement_SB(int, boolean);
1962 static void ActivateLevelTemplate(void)
1966 if (check_special_flags("load_xsb_to_ces"))
1968 // fill smaller playfields with padding "beyond border wall" elements
1969 if (level.fieldx < level_template.fieldx ||
1970 level.fieldy < level_template.fieldy)
1972 short field[level.fieldx][level.fieldy];
1973 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1974 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1975 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1976 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1978 // copy old playfield (which is smaller than the visible area)
1979 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1980 field[x][y] = level.field[x][y];
1982 // fill new, larger playfield with "beyond border wall" elements
1983 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1984 level.field[x][y] = getMappedElement_SB('_', TRUE);
1986 // copy the old playfield to the middle of the new playfield
1987 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1988 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1990 level.fieldx = new_fieldx;
1991 level.fieldy = new_fieldy;
1995 // Currently there is no special action needed to activate the template
1996 // data, because 'element_info' property settings overwrite the original
1997 // level data, while all other variables do not change.
1999 // Exception: 'from_level_template' elements in the original level playfield
2000 // are overwritten with the corresponding elements at the same position in
2001 // playfield from the level template.
2003 for (x = 0; x < level.fieldx; x++)
2004 for (y = 0; y < level.fieldy; y++)
2005 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2006 level.field[x][y] = level_template.field[x][y];
2008 if (check_special_flags("load_xsb_to_ces"))
2010 struct LevelInfo level_backup = level;
2012 // overwrite all individual level settings from template level settings
2013 level = level_template;
2015 // restore level file info
2016 level.file_info = level_backup.file_info;
2018 // restore playfield size
2019 level.fieldx = level_backup.fieldx;
2020 level.fieldy = level_backup.fieldy;
2022 // restore playfield content
2023 for (x = 0; x < level.fieldx; x++)
2024 for (y = 0; y < level.fieldy; y++)
2025 level.field[x][y] = level_backup.field[x][y];
2027 // restore name and author from individual level
2028 strcpy(level.name, level_backup.name);
2029 strcpy(level.author, level_backup.author);
2031 // restore flag "use_custom_template"
2032 level.use_custom_template = level_backup.use_custom_template;
2036 static char *getLevelFilenameFromBasename(char *basename)
2038 static char *filename = NULL;
2040 checked_free(filename);
2042 filename = getPath2(getCurrentLevelDir(), basename);
2047 static int getFileTypeFromBasename(char *basename)
2049 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2051 static char *filename = NULL;
2052 struct stat file_status;
2054 // ---------- try to determine file type from filename ----------
2056 // check for typical filename of a Supaplex level package file
2057 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2058 return LEVEL_FILE_TYPE_SP;
2060 // check for typical filename of a Diamond Caves II level package file
2061 if (strSuffixLower(basename, ".dc") ||
2062 strSuffixLower(basename, ".dc2"))
2063 return LEVEL_FILE_TYPE_DC;
2065 // check for typical filename of a Sokoban level package file
2066 if (strSuffixLower(basename, ".xsb") &&
2067 strchr(basename, '%') == NULL)
2068 return LEVEL_FILE_TYPE_SB;
2070 // ---------- try to determine file type from filesize ----------
2072 checked_free(filename);
2073 filename = getPath2(getCurrentLevelDir(), basename);
2075 if (stat(filename, &file_status) == 0)
2077 // check for typical filesize of a Supaplex level package file
2078 if (file_status.st_size == 170496)
2079 return LEVEL_FILE_TYPE_SP;
2082 return LEVEL_FILE_TYPE_UNKNOWN;
2085 static int getFileTypeFromMagicBytes(char *filename, int type)
2089 if ((file = openFile(filename, MODE_READ)))
2091 char chunk_name[CHUNK_ID_LEN + 1];
2093 getFileChunkBE(file, chunk_name, NULL);
2095 if (strEqual(chunk_name, "MMII") ||
2096 strEqual(chunk_name, "MIRR"))
2097 type = LEVEL_FILE_TYPE_MM;
2105 static boolean checkForPackageFromBasename(char *basename)
2107 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2108 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2110 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2113 static char *getSingleLevelBasenameExt(int nr, char *extension)
2115 static char basename[MAX_FILENAME_LEN];
2118 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2120 sprintf(basename, "%03d.%s", nr, extension);
2125 static char *getSingleLevelBasename(int nr)
2127 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2130 static char *getPackedLevelBasename(int type)
2132 static char basename[MAX_FILENAME_LEN];
2133 char *directory = getCurrentLevelDir();
2135 DirectoryEntry *dir_entry;
2137 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2139 if ((dir = openDirectory(directory)) == NULL)
2141 Warn("cannot read current level directory '%s'", directory);
2146 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2148 char *entry_basename = dir_entry->basename;
2149 int entry_type = getFileTypeFromBasename(entry_basename);
2151 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2153 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2156 strcpy(basename, entry_basename);
2163 closeDirectory(dir);
2168 static char *getSingleLevelFilename(int nr)
2170 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2173 #if ENABLE_UNUSED_CODE
2174 static char *getPackedLevelFilename(int type)
2176 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2180 char *getDefaultLevelFilename(int nr)
2182 return getSingleLevelFilename(nr);
2185 #if ENABLE_UNUSED_CODE
2186 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2190 lfi->packed = FALSE;
2192 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2193 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2197 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2198 int type, char *format, ...)
2200 static char basename[MAX_FILENAME_LEN];
2203 va_start(ap, format);
2204 vsprintf(basename, format, ap);
2208 lfi->packed = FALSE;
2210 setString(&lfi->basename, basename);
2211 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2214 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2220 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2221 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2224 static int getFiletypeFromID(char *filetype_id)
2226 char *filetype_id_lower;
2227 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2230 if (filetype_id == NULL)
2231 return LEVEL_FILE_TYPE_UNKNOWN;
2233 filetype_id_lower = getStringToLower(filetype_id);
2235 for (i = 0; filetype_id_list[i].id != NULL; i++)
2237 char *id_lower = getStringToLower(filetype_id_list[i].id);
2239 if (strEqual(filetype_id_lower, id_lower))
2240 filetype = filetype_id_list[i].filetype;
2244 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2248 free(filetype_id_lower);
2253 char *getLocalLevelTemplateFilename(void)
2255 return getDefaultLevelFilename(-1);
2258 char *getGlobalLevelTemplateFilename(void)
2260 // global variable "leveldir_current" must be modified in the loop below
2261 LevelDirTree *leveldir_current_last = leveldir_current;
2262 char *filename = NULL;
2264 // check for template level in path from current to topmost tree node
2266 while (leveldir_current != NULL)
2268 filename = getDefaultLevelFilename(-1);
2270 if (fileExists(filename))
2273 leveldir_current = leveldir_current->node_parent;
2276 // restore global variable "leveldir_current" modified in above loop
2277 leveldir_current = leveldir_current_last;
2282 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2286 // special case: level number is negative => check for level template file
2289 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2290 getSingleLevelBasename(-1));
2292 // replace local level template filename with global template filename
2293 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2295 // no fallback if template file not existing
2299 // special case: check for file name/pattern specified in "levelinfo.conf"
2300 if (leveldir_current->level_filename != NULL)
2302 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2304 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2305 leveldir_current->level_filename, nr);
2307 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2309 if (fileExists(lfi->filename))
2312 else if (leveldir_current->level_filetype != NULL)
2314 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2316 // check for specified native level file with standard file name
2317 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2318 "%03d.%s", nr, LEVELFILE_EXTENSION);
2319 if (fileExists(lfi->filename))
2323 // check for native Rocks'n'Diamonds level file
2324 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2325 "%03d.%s", nr, LEVELFILE_EXTENSION);
2326 if (fileExists(lfi->filename))
2329 // check for Emerald Mine level file (V1)
2330 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2331 'a' + (nr / 10) % 26, '0' + nr % 10);
2332 if (fileExists(lfi->filename))
2334 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2335 'A' + (nr / 10) % 26, '0' + nr % 10);
2336 if (fileExists(lfi->filename))
2339 // check for Emerald Mine level file (V2 to V5)
2340 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2341 if (fileExists(lfi->filename))
2344 // check for Emerald Mine level file (V6 / single mode)
2345 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2346 if (fileExists(lfi->filename))
2348 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2349 if (fileExists(lfi->filename))
2352 // check for Emerald Mine level file (V6 / teamwork mode)
2353 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2354 if (fileExists(lfi->filename))
2356 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2357 if (fileExists(lfi->filename))
2360 // check for various packed level file formats
2361 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2362 if (fileExists(lfi->filename))
2365 // no known level file found -- use default values (and fail later)
2366 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2367 "%03d.%s", nr, LEVELFILE_EXTENSION);
2370 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2372 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2373 lfi->type = getFileTypeFromBasename(lfi->basename);
2375 if (lfi->type == LEVEL_FILE_TYPE_RND)
2376 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2379 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2381 // always start with reliable default values
2382 setFileInfoToDefaults(level_file_info);
2384 level_file_info->nr = nr; // set requested level number
2386 determineLevelFileInfo_Filename(level_file_info);
2387 determineLevelFileInfo_Filetype(level_file_info);
2390 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2391 struct LevelFileInfo *lfi_to)
2393 lfi_to->nr = lfi_from->nr;
2394 lfi_to->type = lfi_from->type;
2395 lfi_to->packed = lfi_from->packed;
2397 setString(&lfi_to->basename, lfi_from->basename);
2398 setString(&lfi_to->filename, lfi_from->filename);
2401 // ----------------------------------------------------------------------------
2402 // functions for loading R'n'D level
2403 // ----------------------------------------------------------------------------
2405 int getMappedElement(int element)
2407 // remap some (historic, now obsolete) elements
2411 case EL_PLAYER_OBSOLETE:
2412 element = EL_PLAYER_1;
2415 case EL_KEY_OBSOLETE:
2419 case EL_EM_KEY_1_FILE_OBSOLETE:
2420 element = EL_EM_KEY_1;
2423 case EL_EM_KEY_2_FILE_OBSOLETE:
2424 element = EL_EM_KEY_2;
2427 case EL_EM_KEY_3_FILE_OBSOLETE:
2428 element = EL_EM_KEY_3;
2431 case EL_EM_KEY_4_FILE_OBSOLETE:
2432 element = EL_EM_KEY_4;
2435 case EL_ENVELOPE_OBSOLETE:
2436 element = EL_ENVELOPE_1;
2444 if (element >= NUM_FILE_ELEMENTS)
2446 Warn("invalid level element %d", element);
2448 element = EL_UNKNOWN;
2456 static int getMappedElementByVersion(int element, int game_version)
2458 // remap some elements due to certain game version
2460 if (game_version <= VERSION_IDENT(2,2,0,0))
2462 // map game font elements
2463 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2464 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2465 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2466 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2469 if (game_version < VERSION_IDENT(3,0,0,0))
2471 // map Supaplex gravity tube elements
2472 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2473 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2474 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2475 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2482 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2484 level->file_version = getFileVersion(file);
2485 level->game_version = getFileVersion(file);
2490 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2492 level->creation_date.year = getFile16BitBE(file);
2493 level->creation_date.month = getFile8Bit(file);
2494 level->creation_date.day = getFile8Bit(file);
2496 level->creation_date.src = DATE_SRC_LEVELFILE;
2501 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2503 int initial_player_stepsize;
2504 int initial_player_gravity;
2507 level->fieldx = getFile8Bit(file);
2508 level->fieldy = getFile8Bit(file);
2510 level->time = getFile16BitBE(file);
2511 level->gems_needed = getFile16BitBE(file);
2513 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2514 level->name[i] = getFile8Bit(file);
2515 level->name[MAX_LEVEL_NAME_LEN] = 0;
2517 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2518 level->score[i] = getFile8Bit(file);
2520 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2521 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2522 for (y = 0; y < 3; y++)
2523 for (x = 0; x < 3; x++)
2524 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2526 level->amoeba_speed = getFile8Bit(file);
2527 level->time_magic_wall = getFile8Bit(file);
2528 level->time_wheel = getFile8Bit(file);
2529 level->amoeba_content = getMappedElement(getFile8Bit(file));
2531 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2534 for (i = 0; i < MAX_PLAYERS; i++)
2535 level->initial_player_stepsize[i] = initial_player_stepsize;
2537 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2539 for (i = 0; i < MAX_PLAYERS; i++)
2540 level->initial_player_gravity[i] = initial_player_gravity;
2542 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2543 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2545 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2547 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2548 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2549 level->can_move_into_acid_bits = getFile32BitBE(file);
2550 level->dont_collide_with_bits = getFile8Bit(file);
2552 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2553 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2555 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2556 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2557 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2559 level->game_engine_type = getFile8Bit(file);
2561 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2566 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2570 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2571 level->name[i] = getFile8Bit(file);
2572 level->name[MAX_LEVEL_NAME_LEN] = 0;
2577 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2581 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2582 level->author[i] = getFile8Bit(file);
2583 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2588 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2591 int chunk_size_expected = level->fieldx * level->fieldy;
2593 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2594 stored with 16-bit encoding (and should be twice as big then).
2595 Even worse, playfield data was stored 16-bit when only yamyam content
2596 contained 16-bit elements and vice versa. */
2598 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2599 chunk_size_expected *= 2;
2601 if (chunk_size_expected != chunk_size)
2603 ReadUnusedBytesFromFile(file, chunk_size);
2604 return chunk_size_expected;
2607 for (y = 0; y < level->fieldy; y++)
2608 for (x = 0; x < level->fieldx; x++)
2609 level->field[x][y] =
2610 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2615 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2618 int header_size = 4;
2619 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2620 int chunk_size_expected = header_size + content_size;
2622 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2623 stored with 16-bit encoding (and should be twice as big then).
2624 Even worse, playfield data was stored 16-bit when only yamyam content
2625 contained 16-bit elements and vice versa. */
2627 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2628 chunk_size_expected += content_size;
2630 if (chunk_size_expected != chunk_size)
2632 ReadUnusedBytesFromFile(file, chunk_size);
2633 return chunk_size_expected;
2637 level->num_yamyam_contents = getFile8Bit(file);
2641 // correct invalid number of content fields -- should never happen
2642 if (level->num_yamyam_contents < 1 ||
2643 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2644 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2646 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2647 for (y = 0; y < 3; y++)
2648 for (x = 0; x < 3; x++)
2649 level->yamyam_content[i].e[x][y] =
2650 getMappedElement(level->encoding_16bit_field ?
2651 getFile16BitBE(file) : getFile8Bit(file));
2655 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2660 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2662 element = getMappedElement(getFile16BitBE(file));
2663 num_contents = getFile8Bit(file);
2665 getFile8Bit(file); // content x size (unused)
2666 getFile8Bit(file); // content y size (unused)
2668 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2671 for (y = 0; y < 3; y++)
2672 for (x = 0; x < 3; x++)
2673 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2675 // correct invalid number of content fields -- should never happen
2676 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2677 num_contents = STD_ELEMENT_CONTENTS;
2679 if (element == EL_YAMYAM)
2681 level->num_yamyam_contents = num_contents;
2683 for (i = 0; i < num_contents; i++)
2684 for (y = 0; y < 3; y++)
2685 for (x = 0; x < 3; x++)
2686 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2688 else if (element == EL_BD_AMOEBA)
2690 level->amoeba_content = content_array[0][0][0];
2694 Warn("cannot load content for element '%d'", element);
2700 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2706 int chunk_size_expected;
2708 element = getMappedElement(getFile16BitBE(file));
2709 if (!IS_ENVELOPE(element))
2710 element = EL_ENVELOPE_1;
2712 envelope_nr = element - EL_ENVELOPE_1;
2714 envelope_len = getFile16BitBE(file);
2716 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2717 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2719 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2721 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2722 if (chunk_size_expected != chunk_size)
2724 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2725 return chunk_size_expected;
2728 for (i = 0; i < envelope_len; i++)
2729 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2734 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2736 int num_changed_custom_elements = getFile16BitBE(file);
2737 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2740 if (chunk_size_expected != chunk_size)
2742 ReadUnusedBytesFromFile(file, chunk_size - 2);
2743 return chunk_size_expected;
2746 for (i = 0; i < num_changed_custom_elements; i++)
2748 int element = getMappedElement(getFile16BitBE(file));
2749 int properties = getFile32BitBE(file);
2751 if (IS_CUSTOM_ELEMENT(element))
2752 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2754 Warn("invalid custom element number %d", element);
2756 // older game versions that wrote level files with CUS1 chunks used
2757 // different default push delay values (not yet stored in level file)
2758 element_info[element].push_delay_fixed = 2;
2759 element_info[element].push_delay_random = 8;
2762 level->file_has_custom_elements = TRUE;
2767 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2769 int num_changed_custom_elements = getFile16BitBE(file);
2770 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2773 if (chunk_size_expected != chunk_size)
2775 ReadUnusedBytesFromFile(file, chunk_size - 2);
2776 return chunk_size_expected;
2779 for (i = 0; i < num_changed_custom_elements; i++)
2781 int element = getMappedElement(getFile16BitBE(file));
2782 int custom_target_element = getMappedElement(getFile16BitBE(file));
2784 if (IS_CUSTOM_ELEMENT(element))
2785 element_info[element].change->target_element = custom_target_element;
2787 Warn("invalid custom element number %d", element);
2790 level->file_has_custom_elements = TRUE;
2795 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2797 int num_changed_custom_elements = getFile16BitBE(file);
2798 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2801 if (chunk_size_expected != chunk_size)
2803 ReadUnusedBytesFromFile(file, chunk_size - 2);
2804 return chunk_size_expected;
2807 for (i = 0; i < num_changed_custom_elements; i++)
2809 int element = getMappedElement(getFile16BitBE(file));
2810 struct ElementInfo *ei = &element_info[element];
2811 unsigned int event_bits;
2813 if (!IS_CUSTOM_ELEMENT(element))
2815 Warn("invalid custom element number %d", element);
2817 element = EL_INTERNAL_DUMMY;
2820 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2821 ei->description[j] = getFile8Bit(file);
2822 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2824 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2826 // some free bytes for future properties and padding
2827 ReadUnusedBytesFromFile(file, 7);
2829 ei->use_gfx_element = getFile8Bit(file);
2830 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2832 ei->collect_score_initial = getFile8Bit(file);
2833 ei->collect_count_initial = getFile8Bit(file);
2835 ei->push_delay_fixed = getFile16BitBE(file);
2836 ei->push_delay_random = getFile16BitBE(file);
2837 ei->move_delay_fixed = getFile16BitBE(file);
2838 ei->move_delay_random = getFile16BitBE(file);
2840 ei->move_pattern = getFile16BitBE(file);
2841 ei->move_direction_initial = getFile8Bit(file);
2842 ei->move_stepsize = getFile8Bit(file);
2844 for (y = 0; y < 3; y++)
2845 for (x = 0; x < 3; x++)
2846 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2848 // bits 0 - 31 of "has_event[]"
2849 event_bits = getFile32BitBE(file);
2850 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2851 if (event_bits & (1u << j))
2852 ei->change->has_event[j] = TRUE;
2854 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2856 ei->change->delay_fixed = getFile16BitBE(file);
2857 ei->change->delay_random = getFile16BitBE(file);
2858 ei->change->delay_frames = getFile16BitBE(file);
2860 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2862 ei->change->explode = getFile8Bit(file);
2863 ei->change->use_target_content = getFile8Bit(file);
2864 ei->change->only_if_complete = getFile8Bit(file);
2865 ei->change->use_random_replace = getFile8Bit(file);
2867 ei->change->random_percentage = getFile8Bit(file);
2868 ei->change->replace_when = getFile8Bit(file);
2870 for (y = 0; y < 3; y++)
2871 for (x = 0; x < 3; x++)
2872 ei->change->target_content.e[x][y] =
2873 getMappedElement(getFile16BitBE(file));
2875 ei->slippery_type = getFile8Bit(file);
2877 // some free bytes for future properties and padding
2878 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2880 // mark that this custom element has been modified
2881 ei->modified_settings = TRUE;
2884 level->file_has_custom_elements = TRUE;
2889 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2891 struct ElementInfo *ei;
2892 int chunk_size_expected;
2896 // ---------- custom element base property values (96 bytes) ----------------
2898 element = getMappedElement(getFile16BitBE(file));
2900 if (!IS_CUSTOM_ELEMENT(element))
2902 Warn("invalid custom element number %d", element);
2904 ReadUnusedBytesFromFile(file, chunk_size - 2);
2909 ei = &element_info[element];
2911 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2912 ei->description[i] = getFile8Bit(file);
2913 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2915 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2917 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2919 ei->num_change_pages = getFile8Bit(file);
2921 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2922 if (chunk_size_expected != chunk_size)
2924 ReadUnusedBytesFromFile(file, chunk_size - 43);
2925 return chunk_size_expected;
2928 ei->ce_value_fixed_initial = getFile16BitBE(file);
2929 ei->ce_value_random_initial = getFile16BitBE(file);
2930 ei->use_last_ce_value = getFile8Bit(file);
2932 ei->use_gfx_element = getFile8Bit(file);
2933 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2935 ei->collect_score_initial = getFile8Bit(file);
2936 ei->collect_count_initial = getFile8Bit(file);
2938 ei->drop_delay_fixed = getFile8Bit(file);
2939 ei->push_delay_fixed = getFile8Bit(file);
2940 ei->drop_delay_random = getFile8Bit(file);
2941 ei->push_delay_random = getFile8Bit(file);
2942 ei->move_delay_fixed = getFile16BitBE(file);
2943 ei->move_delay_random = getFile16BitBE(file);
2945 // bits 0 - 15 of "move_pattern" ...
2946 ei->move_pattern = getFile16BitBE(file);
2947 ei->move_direction_initial = getFile8Bit(file);
2948 ei->move_stepsize = getFile8Bit(file);
2950 ei->slippery_type = getFile8Bit(file);
2952 for (y = 0; y < 3; y++)
2953 for (x = 0; x < 3; x++)
2954 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2956 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2957 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2958 ei->move_leave_type = getFile8Bit(file);
2960 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2961 ei->move_pattern |= (getFile16BitBE(file) << 16);
2963 ei->access_direction = getFile8Bit(file);
2965 ei->explosion_delay = getFile8Bit(file);
2966 ei->ignition_delay = getFile8Bit(file);
2967 ei->explosion_type = getFile8Bit(file);
2969 // some free bytes for future custom property values and padding
2970 ReadUnusedBytesFromFile(file, 1);
2972 // ---------- change page property values (48 bytes) ------------------------
2974 setElementChangePages(ei, ei->num_change_pages);
2976 for (i = 0; i < ei->num_change_pages; i++)
2978 struct ElementChangeInfo *change = &ei->change_page[i];
2979 unsigned int event_bits;
2981 // always start with reliable default values
2982 setElementChangeInfoToDefaults(change);
2984 // bits 0 - 31 of "has_event[]" ...
2985 event_bits = getFile32BitBE(file);
2986 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2987 if (event_bits & (1u << j))
2988 change->has_event[j] = TRUE;
2990 change->target_element = getMappedElement(getFile16BitBE(file));
2992 change->delay_fixed = getFile16BitBE(file);
2993 change->delay_random = getFile16BitBE(file);
2994 change->delay_frames = getFile16BitBE(file);
2996 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2998 change->explode = getFile8Bit(file);
2999 change->use_target_content = getFile8Bit(file);
3000 change->only_if_complete = getFile8Bit(file);
3001 change->use_random_replace = getFile8Bit(file);
3003 change->random_percentage = getFile8Bit(file);
3004 change->replace_when = getFile8Bit(file);
3006 for (y = 0; y < 3; y++)
3007 for (x = 0; x < 3; x++)
3008 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3010 change->can_change = getFile8Bit(file);
3012 change->trigger_side = getFile8Bit(file);
3014 change->trigger_player = getFile8Bit(file);
3015 change->trigger_page = getFile8Bit(file);
3017 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3018 CH_PAGE_ANY : (1 << change->trigger_page));
3020 change->has_action = getFile8Bit(file);
3021 change->action_type = getFile8Bit(file);
3022 change->action_mode = getFile8Bit(file);
3023 change->action_arg = getFile16BitBE(file);
3025 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3026 event_bits = getFile8Bit(file);
3027 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3028 if (event_bits & (1u << (j - 32)))
3029 change->has_event[j] = TRUE;
3032 // mark this custom element as modified
3033 ei->modified_settings = TRUE;
3035 level->file_has_custom_elements = TRUE;
3040 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3042 struct ElementInfo *ei;
3043 struct ElementGroupInfo *group;
3047 element = getMappedElement(getFile16BitBE(file));
3049 if (!IS_GROUP_ELEMENT(element))
3051 Warn("invalid group element number %d", element);
3053 ReadUnusedBytesFromFile(file, chunk_size - 2);
3058 ei = &element_info[element];
3060 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3061 ei->description[i] = getFile8Bit(file);
3062 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3064 group = element_info[element].group;
3066 group->num_elements = getFile8Bit(file);
3068 ei->use_gfx_element = getFile8Bit(file);
3069 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3071 group->choice_mode = getFile8Bit(file);
3073 // some free bytes for future values and padding
3074 ReadUnusedBytesFromFile(file, 3);
3076 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3077 group->element[i] = getMappedElement(getFile16BitBE(file));
3079 // mark this group element as modified
3080 element_info[element].modified_settings = TRUE;
3082 level->file_has_custom_elements = TRUE;
3087 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3088 int element, int real_element)
3090 int micro_chunk_size = 0;
3091 int conf_type = getFile8Bit(file);
3092 int byte_mask = conf_type & CONF_MASK_BYTES;
3093 boolean element_found = FALSE;
3096 micro_chunk_size += 1;
3098 if (byte_mask == CONF_MASK_MULTI_BYTES)
3100 int num_bytes = getFile16BitBE(file);
3101 byte *buffer = checked_malloc(num_bytes);
3103 ReadBytesFromFile(file, buffer, num_bytes);
3105 for (i = 0; conf[i].data_type != -1; i++)
3107 if (conf[i].element == element &&
3108 conf[i].conf_type == conf_type)
3110 int data_type = conf[i].data_type;
3111 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3112 int max_num_entities = conf[i].max_num_entities;
3114 if (num_entities > max_num_entities)
3116 Warn("truncating number of entities for element %d from %d to %d",
3117 element, num_entities, max_num_entities);
3119 num_entities = max_num_entities;
3122 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3123 data_type == TYPE_CONTENT_LIST))
3125 // for element and content lists, zero entities are not allowed
3126 Warn("found empty list of entities for element %d", element);
3128 // do not set "num_entities" here to prevent reading behind buffer
3130 *(int *)(conf[i].num_entities) = 1; // at least one is required
3134 *(int *)(conf[i].num_entities) = num_entities;
3137 element_found = TRUE;
3139 if (data_type == TYPE_STRING)
3141 char *string = (char *)(conf[i].value);
3144 for (j = 0; j < max_num_entities; j++)
3145 string[j] = (j < num_entities ? buffer[j] : '\0');
3147 else if (data_type == TYPE_ELEMENT_LIST)
3149 int *element_array = (int *)(conf[i].value);
3152 for (j = 0; j < num_entities; j++)
3154 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3156 else if (data_type == TYPE_CONTENT_LIST)
3158 struct Content *content= (struct Content *)(conf[i].value);
3161 for (c = 0; c < num_entities; c++)
3162 for (y = 0; y < 3; y++)
3163 for (x = 0; x < 3; x++)
3164 content[c].e[x][y] =
3165 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3168 element_found = FALSE;
3174 checked_free(buffer);
3176 micro_chunk_size += 2 + num_bytes;
3178 else // constant size configuration data (1, 2 or 4 bytes)
3180 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3181 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3182 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3184 for (i = 0; conf[i].data_type != -1; i++)
3186 if (conf[i].element == element &&
3187 conf[i].conf_type == conf_type)
3189 int data_type = conf[i].data_type;
3191 if (data_type == TYPE_ELEMENT)
3192 value = getMappedElement(value);
3194 if (data_type == TYPE_BOOLEAN)
3195 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3197 *(int *) (conf[i].value) = value;
3199 element_found = TRUE;
3205 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3210 char *error_conf_chunk_bytes =
3211 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3212 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3213 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3214 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3215 int error_element = real_element;
3217 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3218 error_conf_chunk_bytes, error_conf_chunk_token,
3219 error_element, EL_NAME(error_element));
3222 return micro_chunk_size;
3225 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3227 int real_chunk_size = 0;
3229 li = *level; // copy level data into temporary buffer
3231 while (!checkEndOfFile(file))
3233 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3235 if (real_chunk_size >= chunk_size)
3239 *level = li; // copy temporary buffer back to level data
3241 return real_chunk_size;
3244 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3246 int real_chunk_size = 0;
3248 li = *level; // copy level data into temporary buffer
3250 while (!checkEndOfFile(file))
3252 int element = getMappedElement(getFile16BitBE(file));
3254 real_chunk_size += 2;
3255 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3257 if (real_chunk_size >= chunk_size)
3261 *level = li; // copy temporary buffer back to level data
3263 return real_chunk_size;
3266 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3268 int real_chunk_size = 0;
3270 li = *level; // copy level data into temporary buffer
3272 while (!checkEndOfFile(file))
3274 int element = getMappedElement(getFile16BitBE(file));
3276 real_chunk_size += 2;
3277 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3279 if (real_chunk_size >= chunk_size)
3283 *level = li; // copy temporary buffer back to level data
3285 return real_chunk_size;
3288 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3290 int element = getMappedElement(getFile16BitBE(file));
3291 int envelope_nr = element - EL_ENVELOPE_1;
3292 int real_chunk_size = 2;
3294 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3296 while (!checkEndOfFile(file))
3298 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3301 if (real_chunk_size >= chunk_size)
3305 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3307 return real_chunk_size;
3310 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3312 int element = getMappedElement(getFile16BitBE(file));
3313 int real_chunk_size = 2;
3314 struct ElementInfo *ei = &element_info[element];
3317 xx_ei = *ei; // copy element data into temporary buffer
3319 xx_ei.num_change_pages = -1;
3321 while (!checkEndOfFile(file))
3323 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3325 if (xx_ei.num_change_pages != -1)
3328 if (real_chunk_size >= chunk_size)
3334 if (ei->num_change_pages == -1)
3336 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3339 ei->num_change_pages = 1;
3341 setElementChangePages(ei, 1);
3342 setElementChangeInfoToDefaults(ei->change);
3344 return real_chunk_size;
3347 // initialize number of change pages stored for this custom element
3348 setElementChangePages(ei, ei->num_change_pages);
3349 for (i = 0; i < ei->num_change_pages; i++)
3350 setElementChangeInfoToDefaults(&ei->change_page[i]);
3352 // start with reading properties for the first change page
3353 xx_current_change_page = 0;
3355 while (!checkEndOfFile(file))
3357 // level file might contain invalid change page number
3358 if (xx_current_change_page >= ei->num_change_pages)
3361 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3363 xx_change = *change; // copy change data into temporary buffer
3365 resetEventBits(); // reset bits; change page might have changed
3367 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3370 *change = xx_change;
3372 setEventFlagsFromEventBits(change);
3374 if (real_chunk_size >= chunk_size)
3378 level->file_has_custom_elements = TRUE;
3380 return real_chunk_size;
3383 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3385 int element = getMappedElement(getFile16BitBE(file));
3386 int real_chunk_size = 2;
3387 struct ElementInfo *ei = &element_info[element];
3388 struct ElementGroupInfo *group = ei->group;
3393 xx_ei = *ei; // copy element data into temporary buffer
3394 xx_group = *group; // copy group data into temporary buffer
3396 while (!checkEndOfFile(file))
3398 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3401 if (real_chunk_size >= chunk_size)
3408 level->file_has_custom_elements = TRUE;
3410 return real_chunk_size;
3413 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3415 int element = getMappedElement(getFile16BitBE(file));
3416 int real_chunk_size = 2;
3417 struct ElementInfo *ei = &element_info[element];
3419 xx_ei = *ei; // copy element data into temporary buffer
3421 while (!checkEndOfFile(file))
3423 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3426 if (real_chunk_size >= chunk_size)
3432 level->file_has_custom_elements = TRUE;
3434 return real_chunk_size;
3437 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3438 struct LevelFileInfo *level_file_info,
3439 boolean level_info_only)
3441 char *filename = level_file_info->filename;
3442 char cookie[MAX_LINE_LEN];
3443 char chunk_name[CHUNK_ID_LEN + 1];
3447 if (!(file = openFile(filename, MODE_READ)))
3449 level->no_valid_file = TRUE;
3450 level->no_level_file = TRUE;
3452 if (level_info_only)
3455 Warn("cannot read level '%s' -- using empty level", filename);
3457 if (!setup.editor.use_template_for_new_levels)
3460 // if level file not found, try to initialize level data from template
3461 filename = getGlobalLevelTemplateFilename();
3463 if (!(file = openFile(filename, MODE_READ)))
3466 // default: for empty levels, use level template for custom elements
3467 level->use_custom_template = TRUE;
3469 level->no_valid_file = FALSE;
3472 getFileChunkBE(file, chunk_name, NULL);
3473 if (strEqual(chunk_name, "RND1"))
3475 getFile32BitBE(file); // not used
3477 getFileChunkBE(file, chunk_name, NULL);
3478 if (!strEqual(chunk_name, "CAVE"))
3480 level->no_valid_file = TRUE;
3482 Warn("unknown format of level file '%s'", filename);
3489 else // check for pre-2.0 file format with cookie string
3491 strcpy(cookie, chunk_name);
3492 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3494 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3495 cookie[strlen(cookie) - 1] = '\0';
3497 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3499 level->no_valid_file = TRUE;
3501 Warn("unknown format of level file '%s'", filename);
3508 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3510 level->no_valid_file = TRUE;
3512 Warn("unsupported version of level file '%s'", filename);
3519 // pre-2.0 level files have no game version, so use file version here
3520 level->game_version = level->file_version;
3523 if (level->file_version < FILE_VERSION_1_2)
3525 // level files from versions before 1.2.0 without chunk structure
3526 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3527 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3535 int (*loader)(File *, int, struct LevelInfo *);
3539 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3540 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3541 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3542 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3543 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3544 { "INFO", -1, LoadLevel_INFO },
3545 { "BODY", -1, LoadLevel_BODY },
3546 { "CONT", -1, LoadLevel_CONT },
3547 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3548 { "CNT3", -1, LoadLevel_CNT3 },
3549 { "CUS1", -1, LoadLevel_CUS1 },
3550 { "CUS2", -1, LoadLevel_CUS2 },
3551 { "CUS3", -1, LoadLevel_CUS3 },
3552 { "CUS4", -1, LoadLevel_CUS4 },
3553 { "GRP1", -1, LoadLevel_GRP1 },
3554 { "CONF", -1, LoadLevel_CONF },
3555 { "ELEM", -1, LoadLevel_ELEM },
3556 { "NOTE", -1, LoadLevel_NOTE },
3557 { "CUSX", -1, LoadLevel_CUSX },
3558 { "GRPX", -1, LoadLevel_GRPX },
3559 { "EMPX", -1, LoadLevel_EMPX },
3564 while (getFileChunkBE(file, chunk_name, &chunk_size))
3568 while (chunk_info[i].name != NULL &&
3569 !strEqual(chunk_name, chunk_info[i].name))
3572 if (chunk_info[i].name == NULL)
3574 Warn("unknown chunk '%s' in level file '%s'",
3575 chunk_name, filename);
3577 ReadUnusedBytesFromFile(file, chunk_size);
3579 else if (chunk_info[i].size != -1 &&
3580 chunk_info[i].size != chunk_size)
3582 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3583 chunk_size, chunk_name, filename);
3585 ReadUnusedBytesFromFile(file, chunk_size);
3589 // call function to load this level chunk
3590 int chunk_size_expected =
3591 (chunk_info[i].loader)(file, chunk_size, level);
3593 if (chunk_size_expected < 0)
3595 Warn("error reading chunk '%s' in level file '%s'",
3596 chunk_name, filename);
3601 // the size of some chunks cannot be checked before reading other
3602 // chunks first (like "HEAD" and "BODY") that contain some header
3603 // information, so check them here
3604 if (chunk_size_expected != chunk_size)
3606 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3607 chunk_size, chunk_name, filename);
3619 // ----------------------------------------------------------------------------
3620 // functions for loading EM level
3621 // ----------------------------------------------------------------------------
3623 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3625 static int ball_xy[8][2] =
3636 struct LevelInfo_EM *level_em = level->native_em_level;
3637 struct CAVE *cav = level_em->cav;
3640 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3641 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3643 cav->time_seconds = level->time;
3644 cav->gems_needed = level->gems_needed;
3646 cav->emerald_score = level->score[SC_EMERALD];
3647 cav->diamond_score = level->score[SC_DIAMOND];
3648 cav->alien_score = level->score[SC_ROBOT];
3649 cav->tank_score = level->score[SC_SPACESHIP];
3650 cav->bug_score = level->score[SC_BUG];
3651 cav->eater_score = level->score[SC_YAMYAM];
3652 cav->nut_score = level->score[SC_NUT];
3653 cav->dynamite_score = level->score[SC_DYNAMITE];
3654 cav->key_score = level->score[SC_KEY];
3655 cav->exit_score = level->score[SC_TIME_BONUS];
3657 cav->num_eater_arrays = level->num_yamyam_contents;
3659 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3660 for (y = 0; y < 3; y++)
3661 for (x = 0; x < 3; x++)
3662 cav->eater_array[i][y * 3 + x] =
3663 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3665 cav->amoeba_time = level->amoeba_speed;
3666 cav->wonderwall_time = level->time_magic_wall;
3667 cav->wheel_time = level->time_wheel;
3669 cav->android_move_time = level->android_move_time;
3670 cav->android_clone_time = level->android_clone_time;
3671 cav->ball_random = level->ball_random;
3672 cav->ball_active = level->ball_active_initial;
3673 cav->ball_time = level->ball_time;
3674 cav->num_ball_arrays = level->num_ball_contents;
3676 cav->lenses_score = level->lenses_score;
3677 cav->magnify_score = level->magnify_score;
3678 cav->slurp_score = level->slurp_score;
3680 cav->lenses_time = level->lenses_time;
3681 cav->magnify_time = level->magnify_time;
3683 cav->wind_direction =
3684 map_direction_RND_to_EM(level->wind_direction_initial);
3686 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3687 for (j = 0; j < 8; j++)
3688 cav->ball_array[i][j] =
3689 map_element_RND_to_EM_cave(level->ball_content[i].
3690 e[ball_xy[j][0]][ball_xy[j][1]]);
3692 map_android_clone_elements_RND_to_EM(level);
3694 // first fill the complete playfield with the empty space element
3695 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3696 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3697 cav->cave[x][y] = Cblank;
3699 // then copy the real level contents from level file into the playfield
3700 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3702 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3704 if (level->field[x][y] == EL_AMOEBA_DEAD)
3705 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3707 cav->cave[x][y] = new_element;
3710 for (i = 0; i < MAX_PLAYERS; i++)
3712 cav->player_x[i] = -1;
3713 cav->player_y[i] = -1;
3716 // initialize player positions and delete players from the playfield
3717 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3719 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3721 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3723 cav->player_x[player_nr] = x;
3724 cav->player_y[player_nr] = y;
3726 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3731 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3733 static int ball_xy[8][2] =
3744 struct LevelInfo_EM *level_em = level->native_em_level;
3745 struct CAVE *cav = level_em->cav;
3748 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3749 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3751 level->time = cav->time_seconds;
3752 level->gems_needed = cav->gems_needed;
3754 sprintf(level->name, "Level %d", level->file_info.nr);
3756 level->score[SC_EMERALD] = cav->emerald_score;
3757 level->score[SC_DIAMOND] = cav->diamond_score;
3758 level->score[SC_ROBOT] = cav->alien_score;
3759 level->score[SC_SPACESHIP] = cav->tank_score;
3760 level->score[SC_BUG] = cav->bug_score;
3761 level->score[SC_YAMYAM] = cav->eater_score;
3762 level->score[SC_NUT] = cav->nut_score;
3763 level->score[SC_DYNAMITE] = cav->dynamite_score;
3764 level->score[SC_KEY] = cav->key_score;
3765 level->score[SC_TIME_BONUS] = cav->exit_score;
3767 level->num_yamyam_contents = cav->num_eater_arrays;
3769 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3770 for (y = 0; y < 3; y++)
3771 for (x = 0; x < 3; x++)
3772 level->yamyam_content[i].e[x][y] =
3773 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3775 level->amoeba_speed = cav->amoeba_time;
3776 level->time_magic_wall = cav->wonderwall_time;
3777 level->time_wheel = cav->wheel_time;
3779 level->android_move_time = cav->android_move_time;
3780 level->android_clone_time = cav->android_clone_time;
3781 level->ball_random = cav->ball_random;
3782 level->ball_active_initial = cav->ball_active;
3783 level->ball_time = cav->ball_time;
3784 level->num_ball_contents = cav->num_ball_arrays;
3786 level->lenses_score = cav->lenses_score;
3787 level->magnify_score = cav->magnify_score;
3788 level->slurp_score = cav->slurp_score;
3790 level->lenses_time = cav->lenses_time;
3791 level->magnify_time = cav->magnify_time;
3793 level->wind_direction_initial =
3794 map_direction_EM_to_RND(cav->wind_direction);
3796 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3797 for (j = 0; j < 8; j++)
3798 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3799 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3801 map_android_clone_elements_EM_to_RND(level);
3803 // convert the playfield (some elements need special treatment)
3804 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3806 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3808 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3809 new_element = EL_AMOEBA_DEAD;
3811 level->field[x][y] = new_element;
3814 for (i = 0; i < MAX_PLAYERS; i++)
3816 // in case of all players set to the same field, use the first player
3817 int nr = MAX_PLAYERS - i - 1;
3818 int jx = cav->player_x[nr];
3819 int jy = cav->player_y[nr];
3821 if (jx != -1 && jy != -1)
3822 level->field[jx][jy] = EL_PLAYER_1 + nr;
3825 // time score is counted for each 10 seconds left in Emerald Mine levels
3826 level->time_score_base = 10;
3830 // ----------------------------------------------------------------------------
3831 // functions for loading SP level
3832 // ----------------------------------------------------------------------------
3834 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3836 struct LevelInfo_SP *level_sp = level->native_sp_level;
3837 LevelInfoType *header = &level_sp->header;
3840 level_sp->width = level->fieldx;
3841 level_sp->height = level->fieldy;
3843 for (x = 0; x < level->fieldx; x++)
3844 for (y = 0; y < level->fieldy; y++)
3845 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3847 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3849 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3850 header->LevelTitle[i] = level->name[i];
3851 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3853 header->InfotronsNeeded = level->gems_needed;
3855 header->SpecialPortCount = 0;
3857 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3859 boolean gravity_port_found = FALSE;
3860 boolean gravity_port_valid = FALSE;
3861 int gravity_port_flag;
3862 int gravity_port_base_element;
3863 int element = level->field[x][y];
3865 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3866 element <= EL_SP_GRAVITY_ON_PORT_UP)
3868 gravity_port_found = TRUE;
3869 gravity_port_valid = TRUE;
3870 gravity_port_flag = 1;
3871 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3873 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3874 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3876 gravity_port_found = TRUE;
3877 gravity_port_valid = TRUE;
3878 gravity_port_flag = 0;
3879 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3881 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3882 element <= EL_SP_GRAVITY_PORT_UP)
3884 // change R'n'D style gravity inverting special port to normal port
3885 // (there are no gravity inverting ports in native Supaplex engine)
3887 gravity_port_found = TRUE;
3888 gravity_port_valid = FALSE;
3889 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3892 if (gravity_port_found)
3894 if (gravity_port_valid &&
3895 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3897 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3899 port->PortLocation = (y * level->fieldx + x) * 2;
3900 port->Gravity = gravity_port_flag;
3902 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3904 header->SpecialPortCount++;
3908 // change special gravity port to normal port
3910 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3913 level_sp->playfield[x][y] = element - EL_SP_START;
3918 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3920 struct LevelInfo_SP *level_sp = level->native_sp_level;
3921 LevelInfoType *header = &level_sp->header;
3922 boolean num_invalid_elements = 0;
3925 level->fieldx = level_sp->width;
3926 level->fieldy = level_sp->height;
3928 for (x = 0; x < level->fieldx; x++)
3930 for (y = 0; y < level->fieldy; y++)
3932 int element_old = level_sp->playfield[x][y];
3933 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3935 if (element_new == EL_UNKNOWN)
3937 num_invalid_elements++;
3939 Debug("level:native:SP", "invalid element %d at position %d, %d",
3943 level->field[x][y] = element_new;
3947 if (num_invalid_elements > 0)
3948 Warn("found %d invalid elements%s", num_invalid_elements,
3949 (!options.debug ? " (use '--debug' for more details)" : ""));
3951 for (i = 0; i < MAX_PLAYERS; i++)
3952 level->initial_player_gravity[i] =
3953 (header->InitialGravity == 1 ? TRUE : FALSE);
3955 // skip leading spaces
3956 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3957 if (header->LevelTitle[i] != ' ')
3961 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3962 level->name[j] = header->LevelTitle[i];
3963 level->name[j] = '\0';
3965 // cut trailing spaces
3967 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3968 level->name[j - 1] = '\0';
3970 level->gems_needed = header->InfotronsNeeded;
3972 for (i = 0; i < header->SpecialPortCount; i++)
3974 SpecialPortType *port = &header->SpecialPort[i];
3975 int port_location = port->PortLocation;
3976 int gravity = port->Gravity;
3977 int port_x, port_y, port_element;
3979 port_x = (port_location / 2) % level->fieldx;
3980 port_y = (port_location / 2) / level->fieldx;
3982 if (port_x < 0 || port_x >= level->fieldx ||
3983 port_y < 0 || port_y >= level->fieldy)
3985 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3990 port_element = level->field[port_x][port_y];
3992 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3993 port_element > EL_SP_GRAVITY_PORT_UP)
3995 Warn("no special port at position (%d, %d)", port_x, port_y);
4000 // change previous (wrong) gravity inverting special port to either
4001 // gravity enabling special port or gravity disabling special port
4002 level->field[port_x][port_y] +=
4003 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4004 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4007 // change special gravity ports without database entries to normal ports
4008 for (x = 0; x < level->fieldx; x++)
4009 for (y = 0; y < level->fieldy; y++)
4010 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4011 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4012 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4014 level->time = 0; // no time limit
4015 level->amoeba_speed = 0;
4016 level->time_magic_wall = 0;
4017 level->time_wheel = 0;
4018 level->amoeba_content = EL_EMPTY;
4020 // original Supaplex does not use score values -- rate by playing time
4021 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4022 level->score[i] = 0;
4024 level->rate_time_over_score = TRUE;
4026 // there are no yamyams in supaplex levels
4027 for (i = 0; i < level->num_yamyam_contents; i++)
4028 for (x = 0; x < 3; x++)
4029 for (y = 0; y < 3; y++)
4030 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4033 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4035 struct LevelInfo_SP *level_sp = level->native_sp_level;
4036 struct DemoInfo_SP *demo = &level_sp->demo;
4039 // always start with reliable default values
4040 demo->is_available = FALSE;
4043 if (TAPE_IS_EMPTY(tape))
4046 demo->level_nr = tape.level_nr; // (currently not used)
4048 level_sp->header.DemoRandomSeed = tape.random_seed;
4052 for (i = 0; i < tape.length; i++)
4054 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4055 int demo_repeat = tape.pos[i].delay;
4056 int demo_entries = (demo_repeat + 15) / 16;
4058 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4060 Warn("tape truncated: size exceeds maximum SP demo size %d",
4066 for (j = 0; j < demo_repeat / 16; j++)
4067 demo->data[demo->length++] = 0xf0 | demo_action;
4069 if (demo_repeat % 16)
4070 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4073 demo->is_available = TRUE;
4076 static void setTapeInfoToDefaults(void);
4078 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4080 struct LevelInfo_SP *level_sp = level->native_sp_level;
4081 struct DemoInfo_SP *demo = &level_sp->demo;
4082 char *filename = level->file_info.filename;
4085 // always start with reliable default values
4086 setTapeInfoToDefaults();
4088 if (!demo->is_available)
4091 tape.level_nr = demo->level_nr; // (currently not used)
4092 tape.random_seed = level_sp->header.DemoRandomSeed;
4094 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4097 tape.pos[tape.counter].delay = 0;
4099 for (i = 0; i < demo->length; i++)
4101 int demo_action = demo->data[i] & 0x0f;
4102 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4103 int tape_action = map_key_SP_to_RND(demo_action);
4104 int tape_repeat = demo_repeat + 1;
4105 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4106 boolean success = 0;
4109 for (j = 0; j < tape_repeat; j++)
4110 success = TapeAddAction(action);
4114 Warn("SP demo truncated: size exceeds maximum tape size %d",
4121 TapeHaltRecording();
4125 // ----------------------------------------------------------------------------
4126 // functions for loading MM level
4127 // ----------------------------------------------------------------------------
4129 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4131 struct LevelInfo_MM *level_mm = level->native_mm_level;
4134 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4135 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4137 level_mm->time = level->time;
4138 level_mm->kettles_needed = level->gems_needed;
4139 level_mm->auto_count_kettles = level->auto_count_gems;
4141 level_mm->laser_red = level->mm_laser_red;
4142 level_mm->laser_green = level->mm_laser_green;
4143 level_mm->laser_blue = level->mm_laser_blue;
4145 strcpy(level_mm->name, level->name);
4146 strcpy(level_mm->author, level->author);
4148 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4149 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4150 level_mm->score[SC_KEY] = level->score[SC_KEY];
4151 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4152 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4154 level_mm->amoeba_speed = level->amoeba_speed;
4155 level_mm->time_fuse = level->mm_time_fuse;
4156 level_mm->time_bomb = level->mm_time_bomb;
4157 level_mm->time_ball = level->mm_time_ball;
4158 level_mm->time_block = level->mm_time_block;
4160 for (x = 0; x < level->fieldx; x++)
4161 for (y = 0; y < level->fieldy; y++)
4163 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4166 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4168 struct LevelInfo_MM *level_mm = level->native_mm_level;
4171 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4172 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4174 level->time = level_mm->time;
4175 level->gems_needed = level_mm->kettles_needed;
4176 level->auto_count_gems = level_mm->auto_count_kettles;
4178 level->mm_laser_red = level_mm->laser_red;
4179 level->mm_laser_green = level_mm->laser_green;
4180 level->mm_laser_blue = level_mm->laser_blue;
4182 strcpy(level->name, level_mm->name);
4184 // only overwrite author from 'levelinfo.conf' if author defined in level
4185 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4186 strcpy(level->author, level_mm->author);
4188 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4189 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4190 level->score[SC_KEY] = level_mm->score[SC_KEY];
4191 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4192 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4194 level->amoeba_speed = level_mm->amoeba_speed;
4195 level->mm_time_fuse = level_mm->time_fuse;
4196 level->mm_time_bomb = level_mm->time_bomb;
4197 level->mm_time_ball = level_mm->time_ball;
4198 level->mm_time_block = level_mm->time_block;
4200 for (x = 0; x < level->fieldx; x++)
4201 for (y = 0; y < level->fieldy; y++)
4202 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4206 // ----------------------------------------------------------------------------
4207 // functions for loading DC level
4208 // ----------------------------------------------------------------------------
4210 #define DC_LEVEL_HEADER_SIZE 344
4212 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4215 static int last_data_encoded;
4219 int diff_hi, diff_lo;
4220 int data_hi, data_lo;
4221 unsigned short data_decoded;
4225 last_data_encoded = 0;
4232 diff = data_encoded - last_data_encoded;
4233 diff_hi = diff & ~0xff;
4234 diff_lo = diff & 0xff;
4238 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4239 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4240 data_hi = data_hi & 0xff00;
4242 data_decoded = data_hi | data_lo;
4244 last_data_encoded = data_encoded;
4246 offset1 = (offset1 + 1) % 31;
4247 offset2 = offset2 & 0xff;
4249 return data_decoded;
4252 static int getMappedElement_DC(int element)
4260 // 0x0117 - 0x036e: (?)
4263 // 0x042d - 0x0684: (?)
4279 element = EL_CRYSTAL;
4282 case 0x0e77: // quicksand (boulder)
4283 element = EL_QUICKSAND_FAST_FULL;
4286 case 0x0e99: // slow quicksand (boulder)
4287 element = EL_QUICKSAND_FULL;
4291 element = EL_EM_EXIT_OPEN;
4295 element = EL_EM_EXIT_CLOSED;
4299 element = EL_EM_STEEL_EXIT_OPEN;
4303 element = EL_EM_STEEL_EXIT_CLOSED;
4306 case 0x0f4f: // dynamite (lit 1)
4307 element = EL_EM_DYNAMITE_ACTIVE;
4310 case 0x0f57: // dynamite (lit 2)
4311 element = EL_EM_DYNAMITE_ACTIVE;
4314 case 0x0f5f: // dynamite (lit 3)
4315 element = EL_EM_DYNAMITE_ACTIVE;
4318 case 0x0f67: // dynamite (lit 4)
4319 element = EL_EM_DYNAMITE_ACTIVE;
4326 element = EL_AMOEBA_WET;
4330 element = EL_AMOEBA_DROP;
4334 element = EL_DC_MAGIC_WALL;
4338 element = EL_SPACESHIP_UP;
4342 element = EL_SPACESHIP_DOWN;
4346 element = EL_SPACESHIP_LEFT;
4350 element = EL_SPACESHIP_RIGHT;
4354 element = EL_BUG_UP;
4358 element = EL_BUG_DOWN;
4362 element = EL_BUG_LEFT;
4366 element = EL_BUG_RIGHT;
4370 element = EL_MOLE_UP;
4374 element = EL_MOLE_DOWN;
4378 element = EL_MOLE_LEFT;
4382 element = EL_MOLE_RIGHT;
4390 element = EL_YAMYAM_UP;
4394 element = EL_SWITCHGATE_OPEN;
4398 element = EL_SWITCHGATE_CLOSED;
4402 element = EL_DC_SWITCHGATE_SWITCH_UP;
4406 element = EL_TIMEGATE_CLOSED;
4409 case 0x144c: // conveyor belt switch (green)
4410 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4413 case 0x144f: // conveyor belt switch (red)
4414 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4417 case 0x1452: // conveyor belt switch (blue)
4418 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4422 element = EL_CONVEYOR_BELT_3_MIDDLE;
4426 element = EL_CONVEYOR_BELT_3_LEFT;
4430 element = EL_CONVEYOR_BELT_3_RIGHT;
4434 element = EL_CONVEYOR_BELT_1_MIDDLE;
4438 element = EL_CONVEYOR_BELT_1_LEFT;
4442 element = EL_CONVEYOR_BELT_1_RIGHT;
4446 element = EL_CONVEYOR_BELT_4_MIDDLE;
4450 element = EL_CONVEYOR_BELT_4_LEFT;
4454 element = EL_CONVEYOR_BELT_4_RIGHT;
4458 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4462 element = EL_EXPANDABLE_WALL_VERTICAL;
4466 element = EL_EXPANDABLE_WALL_ANY;
4469 case 0x14ce: // growing steel wall (left/right)
4470 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4473 case 0x14df: // growing steel wall (up/down)
4474 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4477 case 0x14e8: // growing steel wall (up/down/left/right)
4478 element = EL_EXPANDABLE_STEELWALL_ANY;
4482 element = EL_SHIELD_DEADLY;
4486 element = EL_EXTRA_TIME;
4494 element = EL_EMPTY_SPACE;
4497 case 0x1578: // quicksand (empty)
4498 element = EL_QUICKSAND_FAST_EMPTY;
4501 case 0x1579: // slow quicksand (empty)
4502 element = EL_QUICKSAND_EMPTY;
4512 element = EL_EM_DYNAMITE;
4515 case 0x15a1: // key (red)
4516 element = EL_EM_KEY_1;
4519 case 0x15a2: // key (yellow)
4520 element = EL_EM_KEY_2;
4523 case 0x15a3: // key (blue)
4524 element = EL_EM_KEY_4;
4527 case 0x15a4: // key (green)
4528 element = EL_EM_KEY_3;
4531 case 0x15a5: // key (white)
4532 element = EL_DC_KEY_WHITE;
4536 element = EL_WALL_SLIPPERY;
4543 case 0x15a8: // wall (not round)
4547 case 0x15a9: // (blue)
4548 element = EL_CHAR_A;
4551 case 0x15aa: // (blue)
4552 element = EL_CHAR_B;
4555 case 0x15ab: // (blue)
4556 element = EL_CHAR_C;
4559 case 0x15ac: // (blue)
4560 element = EL_CHAR_D;
4563 case 0x15ad: // (blue)
4564 element = EL_CHAR_E;
4567 case 0x15ae: // (blue)
4568 element = EL_CHAR_F;
4571 case 0x15af: // (blue)
4572 element = EL_CHAR_G;
4575 case 0x15b0: // (blue)
4576 element = EL_CHAR_H;
4579 case 0x15b1: // (blue)
4580 element = EL_CHAR_I;
4583 case 0x15b2: // (blue)
4584 element = EL_CHAR_J;
4587 case 0x15b3: // (blue)
4588 element = EL_CHAR_K;
4591 case 0x15b4: // (blue)
4592 element = EL_CHAR_L;
4595 case 0x15b5: // (blue)
4596 element = EL_CHAR_M;
4599 case 0x15b6: // (blue)
4600 element = EL_CHAR_N;
4603 case 0x15b7: // (blue)
4604 element = EL_CHAR_O;
4607 case 0x15b8: // (blue)
4608 element = EL_CHAR_P;
4611 case 0x15b9: // (blue)
4612 element = EL_CHAR_Q;
4615 case 0x15ba: // (blue)
4616 element = EL_CHAR_R;
4619 case 0x15bb: // (blue)
4620 element = EL_CHAR_S;
4623 case 0x15bc: // (blue)
4624 element = EL_CHAR_T;
4627 case 0x15bd: // (blue)
4628 element = EL_CHAR_U;
4631 case 0x15be: // (blue)
4632 element = EL_CHAR_V;
4635 case 0x15bf: // (blue)
4636 element = EL_CHAR_W;
4639 case 0x15c0: // (blue)
4640 element = EL_CHAR_X;
4643 case 0x15c1: // (blue)
4644 element = EL_CHAR_Y;
4647 case 0x15c2: // (blue)
4648 element = EL_CHAR_Z;
4651 case 0x15c3: // (blue)
4652 element = EL_CHAR_AUMLAUT;
4655 case 0x15c4: // (blue)
4656 element = EL_CHAR_OUMLAUT;
4659 case 0x15c5: // (blue)
4660 element = EL_CHAR_UUMLAUT;
4663 case 0x15c6: // (blue)
4664 element = EL_CHAR_0;
4667 case 0x15c7: // (blue)
4668 element = EL_CHAR_1;
4671 case 0x15c8: // (blue)
4672 element = EL_CHAR_2;
4675 case 0x15c9: // (blue)
4676 element = EL_CHAR_3;
4679 case 0x15ca: // (blue)
4680 element = EL_CHAR_4;
4683 case 0x15cb: // (blue)
4684 element = EL_CHAR_5;
4687 case 0x15cc: // (blue)
4688 element = EL_CHAR_6;
4691 case 0x15cd: // (blue)
4692 element = EL_CHAR_7;
4695 case 0x15ce: // (blue)
4696 element = EL_CHAR_8;
4699 case 0x15cf: // (blue)
4700 element = EL_CHAR_9;
4703 case 0x15d0: // (blue)
4704 element = EL_CHAR_PERIOD;
4707 case 0x15d1: // (blue)
4708 element = EL_CHAR_EXCLAM;
4711 case 0x15d2: // (blue)
4712 element = EL_CHAR_COLON;
4715 case 0x15d3: // (blue)
4716 element = EL_CHAR_LESS;
4719 case 0x15d4: // (blue)
4720 element = EL_CHAR_GREATER;
4723 case 0x15d5: // (blue)
4724 element = EL_CHAR_QUESTION;
4727 case 0x15d6: // (blue)
4728 element = EL_CHAR_COPYRIGHT;
4731 case 0x15d7: // (blue)
4732 element = EL_CHAR_UP;
4735 case 0x15d8: // (blue)
4736 element = EL_CHAR_DOWN;
4739 case 0x15d9: // (blue)
4740 element = EL_CHAR_BUTTON;
4743 case 0x15da: // (blue)
4744 element = EL_CHAR_PLUS;
4747 case 0x15db: // (blue)
4748 element = EL_CHAR_MINUS;
4751 case 0x15dc: // (blue)
4752 element = EL_CHAR_APOSTROPHE;
4755 case 0x15dd: // (blue)
4756 element = EL_CHAR_PARENLEFT;
4759 case 0x15de: // (blue)
4760 element = EL_CHAR_PARENRIGHT;
4763 case 0x15df: // (green)
4764 element = EL_CHAR_A;
4767 case 0x15e0: // (green)
4768 element = EL_CHAR_B;
4771 case 0x15e1: // (green)
4772 element = EL_CHAR_C;
4775 case 0x15e2: // (green)
4776 element = EL_CHAR_D;
4779 case 0x15e3: // (green)
4780 element = EL_CHAR_E;
4783 case 0x15e4: // (green)
4784 element = EL_CHAR_F;
4787 case 0x15e5: // (green)
4788 element = EL_CHAR_G;
4791 case 0x15e6: // (green)
4792 element = EL_CHAR_H;
4795 case 0x15e7: // (green)
4796 element = EL_CHAR_I;
4799 case 0x15e8: // (green)
4800 element = EL_CHAR_J;
4803 case 0x15e9: // (green)
4804 element = EL_CHAR_K;
4807 case 0x15ea: // (green)
4808 element = EL_CHAR_L;
4811 case 0x15eb: // (green)
4812 element = EL_CHAR_M;
4815 case 0x15ec: // (green)
4816 element = EL_CHAR_N;
4819 case 0x15ed: // (green)
4820 element = EL_CHAR_O;
4823 case 0x15ee: // (green)
4824 element = EL_CHAR_P;
4827 case 0x15ef: // (green)
4828 element = EL_CHAR_Q;
4831 case 0x15f0: // (green)
4832 element = EL_CHAR_R;
4835 case 0x15f1: // (green)
4836 element = EL_CHAR_S;
4839 case 0x15f2: // (green)
4840 element = EL_CHAR_T;
4843 case 0x15f3: // (green)
4844 element = EL_CHAR_U;
4847 case 0x15f4: // (green)
4848 element = EL_CHAR_V;
4851 case 0x15f5: // (green)
4852 element = EL_CHAR_W;
4855 case 0x15f6: // (green)
4856 element = EL_CHAR_X;
4859 case 0x15f7: // (green)
4860 element = EL_CHAR_Y;
4863 case 0x15f8: // (green)
4864 element = EL_CHAR_Z;
4867 case 0x15f9: // (green)
4868 element = EL_CHAR_AUMLAUT;
4871 case 0x15fa: // (green)
4872 element = EL_CHAR_OUMLAUT;
4875 case 0x15fb: // (green)
4876 element = EL_CHAR_UUMLAUT;
4879 case 0x15fc: // (green)
4880 element = EL_CHAR_0;
4883 case 0x15fd: // (green)
4884 element = EL_CHAR_1;
4887 case 0x15fe: // (green)
4888 element = EL_CHAR_2;
4891 case 0x15ff: // (green)
4892 element = EL_CHAR_3;
4895 case 0x1600: // (green)
4896 element = EL_CHAR_4;
4899 case 0x1601: // (green)
4900 element = EL_CHAR_5;
4903 case 0x1602: // (green)
4904 element = EL_CHAR_6;
4907 case 0x1603: // (green)
4908 element = EL_CHAR_7;
4911 case 0x1604: // (green)
4912 element = EL_CHAR_8;
4915 case 0x1605: // (green)
4916 element = EL_CHAR_9;
4919 case 0x1606: // (green)
4920 element = EL_CHAR_PERIOD;
4923 case 0x1607: // (green)
4924 element = EL_CHAR_EXCLAM;
4927 case 0x1608: // (green)
4928 element = EL_CHAR_COLON;
4931 case 0x1609: // (green)
4932 element = EL_CHAR_LESS;
4935 case 0x160a: // (green)
4936 element = EL_CHAR_GREATER;
4939 case 0x160b: // (green)
4940 element = EL_CHAR_QUESTION;
4943 case 0x160c: // (green)
4944 element = EL_CHAR_COPYRIGHT;
4947 case 0x160d: // (green)
4948 element = EL_CHAR_UP;
4951 case 0x160e: // (green)
4952 element = EL_CHAR_DOWN;
4955 case 0x160f: // (green)
4956 element = EL_CHAR_BUTTON;
4959 case 0x1610: // (green)
4960 element = EL_CHAR_PLUS;
4963 case 0x1611: // (green)
4964 element = EL_CHAR_MINUS;
4967 case 0x1612: // (green)
4968 element = EL_CHAR_APOSTROPHE;
4971 case 0x1613: // (green)
4972 element = EL_CHAR_PARENLEFT;
4975 case 0x1614: // (green)
4976 element = EL_CHAR_PARENRIGHT;
4979 case 0x1615: // (blue steel)
4980 element = EL_STEEL_CHAR_A;
4983 case 0x1616: // (blue steel)
4984 element = EL_STEEL_CHAR_B;
4987 case 0x1617: // (blue steel)
4988 element = EL_STEEL_CHAR_C;
4991 case 0x1618: // (blue steel)
4992 element = EL_STEEL_CHAR_D;
4995 case 0x1619: // (blue steel)
4996 element = EL_STEEL_CHAR_E;
4999 case 0x161a: // (blue steel)
5000 element = EL_STEEL_CHAR_F;
5003 case 0x161b: // (blue steel)
5004 element = EL_STEEL_CHAR_G;
5007 case 0x161c: // (blue steel)
5008 element = EL_STEEL_CHAR_H;
5011 case 0x161d: // (blue steel)
5012 element = EL_STEEL_CHAR_I;
5015 case 0x161e: // (blue steel)
5016 element = EL_STEEL_CHAR_J;
5019 case 0x161f: // (blue steel)
5020 element = EL_STEEL_CHAR_K;
5023 case 0x1620: // (blue steel)
5024 element = EL_STEEL_CHAR_L;
5027 case 0x1621: // (blue steel)
5028 element = EL_STEEL_CHAR_M;
5031 case 0x1622: // (blue steel)
5032 element = EL_STEEL_CHAR_N;
5035 case 0x1623: // (blue steel)
5036 element = EL_STEEL_CHAR_O;
5039 case 0x1624: // (blue steel)
5040 element = EL_STEEL_CHAR_P;
5043 case 0x1625: // (blue steel)
5044 element = EL_STEEL_CHAR_Q;
5047 case 0x1626: // (blue steel)
5048 element = EL_STEEL_CHAR_R;
5051 case 0x1627: // (blue steel)
5052 element = EL_STEEL_CHAR_S;
5055 case 0x1628: // (blue steel)
5056 element = EL_STEEL_CHAR_T;
5059 case 0x1629: // (blue steel)
5060 element = EL_STEEL_CHAR_U;
5063 case 0x162a: // (blue steel)
5064 element = EL_STEEL_CHAR_V;
5067 case 0x162b: // (blue steel)
5068 element = EL_STEEL_CHAR_W;
5071 case 0x162c: // (blue steel)
5072 element = EL_STEEL_CHAR_X;
5075 case 0x162d: // (blue steel)
5076 element = EL_STEEL_CHAR_Y;
5079 case 0x162e: // (blue steel)
5080 element = EL_STEEL_CHAR_Z;
5083 case 0x162f: // (blue steel)
5084 element = EL_STEEL_CHAR_AUMLAUT;
5087 case 0x1630: // (blue steel)
5088 element = EL_STEEL_CHAR_OUMLAUT;
5091 case 0x1631: // (blue steel)
5092 element = EL_STEEL_CHAR_UUMLAUT;
5095 case 0x1632: // (blue steel)
5096 element = EL_STEEL_CHAR_0;
5099 case 0x1633: // (blue steel)
5100 element = EL_STEEL_CHAR_1;
5103 case 0x1634: // (blue steel)
5104 element = EL_STEEL_CHAR_2;
5107 case 0x1635: // (blue steel)
5108 element = EL_STEEL_CHAR_3;
5111 case 0x1636: // (blue steel)
5112 element = EL_STEEL_CHAR_4;
5115 case 0x1637: // (blue steel)
5116 element = EL_STEEL_CHAR_5;
5119 case 0x1638: // (blue steel)
5120 element = EL_STEEL_CHAR_6;
5123 case 0x1639: // (blue steel)
5124 element = EL_STEEL_CHAR_7;
5127 case 0x163a: // (blue steel)
5128 element = EL_STEEL_CHAR_8;
5131 case 0x163b: // (blue steel)
5132 element = EL_STEEL_CHAR_9;
5135 case 0x163c: // (blue steel)
5136 element = EL_STEEL_CHAR_PERIOD;
5139 case 0x163d: // (blue steel)
5140 element = EL_STEEL_CHAR_EXCLAM;
5143 case 0x163e: // (blue steel)
5144 element = EL_STEEL_CHAR_COLON;
5147 case 0x163f: // (blue steel)
5148 element = EL_STEEL_CHAR_LESS;
5151 case 0x1640: // (blue steel)
5152 element = EL_STEEL_CHAR_GREATER;
5155 case 0x1641: // (blue steel)
5156 element = EL_STEEL_CHAR_QUESTION;
5159 case 0x1642: // (blue steel)
5160 element = EL_STEEL_CHAR_COPYRIGHT;
5163 case 0x1643: // (blue steel)
5164 element = EL_STEEL_CHAR_UP;
5167 case 0x1644: // (blue steel)
5168 element = EL_STEEL_CHAR_DOWN;
5171 case 0x1645: // (blue steel)
5172 element = EL_STEEL_CHAR_BUTTON;
5175 case 0x1646: // (blue steel)
5176 element = EL_STEEL_CHAR_PLUS;
5179 case 0x1647: // (blue steel)
5180 element = EL_STEEL_CHAR_MINUS;
5183 case 0x1648: // (blue steel)
5184 element = EL_STEEL_CHAR_APOSTROPHE;
5187 case 0x1649: // (blue steel)
5188 element = EL_STEEL_CHAR_PARENLEFT;
5191 case 0x164a: // (blue steel)
5192 element = EL_STEEL_CHAR_PARENRIGHT;
5195 case 0x164b: // (green steel)
5196 element = EL_STEEL_CHAR_A;
5199 case 0x164c: // (green steel)
5200 element = EL_STEEL_CHAR_B;
5203 case 0x164d: // (green steel)
5204 element = EL_STEEL_CHAR_C;
5207 case 0x164e: // (green steel)
5208 element = EL_STEEL_CHAR_D;
5211 case 0x164f: // (green steel)
5212 element = EL_STEEL_CHAR_E;
5215 case 0x1650: // (green steel)
5216 element = EL_STEEL_CHAR_F;
5219 case 0x1651: // (green steel)
5220 element = EL_STEEL_CHAR_G;
5223 case 0x1652: // (green steel)
5224 element = EL_STEEL_CHAR_H;
5227 case 0x1653: // (green steel)
5228 element = EL_STEEL_CHAR_I;
5231 case 0x1654: // (green steel)
5232 element = EL_STEEL_CHAR_J;
5235 case 0x1655: // (green steel)
5236 element = EL_STEEL_CHAR_K;
5239 case 0x1656: // (green steel)
5240 element = EL_STEEL_CHAR_L;
5243 case 0x1657: // (green steel)
5244 element = EL_STEEL_CHAR_M;
5247 case 0x1658: // (green steel)
5248 element = EL_STEEL_CHAR_N;
5251 case 0x1659: // (green steel)
5252 element = EL_STEEL_CHAR_O;
5255 case 0x165a: // (green steel)
5256 element = EL_STEEL_CHAR_P;
5259 case 0x165b: // (green steel)
5260 element = EL_STEEL_CHAR_Q;
5263 case 0x165c: // (green steel)
5264 element = EL_STEEL_CHAR_R;
5267 case 0x165d: // (green steel)
5268 element = EL_STEEL_CHAR_S;
5271 case 0x165e: // (green steel)
5272 element = EL_STEEL_CHAR_T;
5275 case 0x165f: // (green steel)
5276 element = EL_STEEL_CHAR_U;
5279 case 0x1660: // (green steel)
5280 element = EL_STEEL_CHAR_V;
5283 case 0x1661: // (green steel)
5284 element = EL_STEEL_CHAR_W;
5287 case 0x1662: // (green steel)
5288 element = EL_STEEL_CHAR_X;
5291 case 0x1663: // (green steel)
5292 element = EL_STEEL_CHAR_Y;
5295 case 0x1664: // (green steel)
5296 element = EL_STEEL_CHAR_Z;
5299 case 0x1665: // (green steel)
5300 element = EL_STEEL_CHAR_AUMLAUT;
5303 case 0x1666: // (green steel)
5304 element = EL_STEEL_CHAR_OUMLAUT;
5307 case 0x1667: // (green steel)
5308 element = EL_STEEL_CHAR_UUMLAUT;
5311 case 0x1668: // (green steel)
5312 element = EL_STEEL_CHAR_0;
5315 case 0x1669: // (green steel)
5316 element = EL_STEEL_CHAR_1;
5319 case 0x166a: // (green steel)
5320 element = EL_STEEL_CHAR_2;
5323 case 0x166b: // (green steel)
5324 element = EL_STEEL_CHAR_3;
5327 case 0x166c: // (green steel)
5328 element = EL_STEEL_CHAR_4;
5331 case 0x166d: // (green steel)
5332 element = EL_STEEL_CHAR_5;
5335 case 0x166e: // (green steel)
5336 element = EL_STEEL_CHAR_6;
5339 case 0x166f: // (green steel)
5340 element = EL_STEEL_CHAR_7;
5343 case 0x1670: // (green steel)
5344 element = EL_STEEL_CHAR_8;
5347 case 0x1671: // (green steel)
5348 element = EL_STEEL_CHAR_9;
5351 case 0x1672: // (green steel)
5352 element = EL_STEEL_CHAR_PERIOD;
5355 case 0x1673: // (green steel)
5356 element = EL_STEEL_CHAR_EXCLAM;
5359 case 0x1674: // (green steel)
5360 element = EL_STEEL_CHAR_COLON;
5363 case 0x1675: // (green steel)
5364 element = EL_STEEL_CHAR_LESS;
5367 case 0x1676: // (green steel)
5368 element = EL_STEEL_CHAR_GREATER;
5371 case 0x1677: // (green steel)
5372 element = EL_STEEL_CHAR_QUESTION;
5375 case 0x1678: // (green steel)
5376 element = EL_STEEL_CHAR_COPYRIGHT;
5379 case 0x1679: // (green steel)
5380 element = EL_STEEL_CHAR_UP;
5383 case 0x167a: // (green steel)
5384 element = EL_STEEL_CHAR_DOWN;
5387 case 0x167b: // (green steel)
5388 element = EL_STEEL_CHAR_BUTTON;
5391 case 0x167c: // (green steel)
5392 element = EL_STEEL_CHAR_PLUS;
5395 case 0x167d: // (green steel)
5396 element = EL_STEEL_CHAR_MINUS;
5399 case 0x167e: // (green steel)
5400 element = EL_STEEL_CHAR_APOSTROPHE;
5403 case 0x167f: // (green steel)
5404 element = EL_STEEL_CHAR_PARENLEFT;
5407 case 0x1680: // (green steel)
5408 element = EL_STEEL_CHAR_PARENRIGHT;
5411 case 0x1681: // gate (red)
5412 element = EL_EM_GATE_1;
5415 case 0x1682: // secret gate (red)
5416 element = EL_EM_GATE_1_GRAY;
5419 case 0x1683: // gate (yellow)
5420 element = EL_EM_GATE_2;
5423 case 0x1684: // secret gate (yellow)
5424 element = EL_EM_GATE_2_GRAY;
5427 case 0x1685: // gate (blue)
5428 element = EL_EM_GATE_4;
5431 case 0x1686: // secret gate (blue)
5432 element = EL_EM_GATE_4_GRAY;
5435 case 0x1687: // gate (green)
5436 element = EL_EM_GATE_3;
5439 case 0x1688: // secret gate (green)
5440 element = EL_EM_GATE_3_GRAY;
5443 case 0x1689: // gate (white)
5444 element = EL_DC_GATE_WHITE;
5447 case 0x168a: // secret gate (white)
5448 element = EL_DC_GATE_WHITE_GRAY;
5451 case 0x168b: // secret gate (no key)
5452 element = EL_DC_GATE_FAKE_GRAY;
5456 element = EL_ROBOT_WHEEL;
5460 element = EL_DC_TIMEGATE_SWITCH;
5464 element = EL_ACID_POOL_BOTTOM;
5468 element = EL_ACID_POOL_TOPLEFT;
5472 element = EL_ACID_POOL_TOPRIGHT;
5476 element = EL_ACID_POOL_BOTTOMLEFT;
5480 element = EL_ACID_POOL_BOTTOMRIGHT;
5484 element = EL_STEELWALL;
5488 element = EL_STEELWALL_SLIPPERY;
5491 case 0x1695: // steel wall (not round)
5492 element = EL_STEELWALL;
5495 case 0x1696: // steel wall (left)
5496 element = EL_DC_STEELWALL_1_LEFT;
5499 case 0x1697: // steel wall (bottom)
5500 element = EL_DC_STEELWALL_1_BOTTOM;
5503 case 0x1698: // steel wall (right)
5504 element = EL_DC_STEELWALL_1_RIGHT;
5507 case 0x1699: // steel wall (top)
5508 element = EL_DC_STEELWALL_1_TOP;
5511 case 0x169a: // steel wall (left/bottom)
5512 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5515 case 0x169b: // steel wall (right/bottom)
5516 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5519 case 0x169c: // steel wall (right/top)
5520 element = EL_DC_STEELWALL_1_TOPRIGHT;
5523 case 0x169d: // steel wall (left/top)
5524 element = EL_DC_STEELWALL_1_TOPLEFT;
5527 case 0x169e: // steel wall (right/bottom small)
5528 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5531 case 0x169f: // steel wall (left/bottom small)
5532 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5535 case 0x16a0: // steel wall (right/top small)
5536 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5539 case 0x16a1: // steel wall (left/top small)
5540 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5543 case 0x16a2: // steel wall (left/right)
5544 element = EL_DC_STEELWALL_1_VERTICAL;
5547 case 0x16a3: // steel wall (top/bottom)
5548 element = EL_DC_STEELWALL_1_HORIZONTAL;
5551 case 0x16a4: // steel wall 2 (left end)
5552 element = EL_DC_STEELWALL_2_LEFT;
5555 case 0x16a5: // steel wall 2 (right end)
5556 element = EL_DC_STEELWALL_2_RIGHT;
5559 case 0x16a6: // steel wall 2 (top end)
5560 element = EL_DC_STEELWALL_2_TOP;
5563 case 0x16a7: // steel wall 2 (bottom end)
5564 element = EL_DC_STEELWALL_2_BOTTOM;
5567 case 0x16a8: // steel wall 2 (left/right)
5568 element = EL_DC_STEELWALL_2_HORIZONTAL;
5571 case 0x16a9: // steel wall 2 (up/down)
5572 element = EL_DC_STEELWALL_2_VERTICAL;
5575 case 0x16aa: // steel wall 2 (mid)
5576 element = EL_DC_STEELWALL_2_MIDDLE;
5580 element = EL_SIGN_EXCLAMATION;
5584 element = EL_SIGN_RADIOACTIVITY;
5588 element = EL_SIGN_STOP;
5592 element = EL_SIGN_WHEELCHAIR;
5596 element = EL_SIGN_PARKING;
5600 element = EL_SIGN_NO_ENTRY;
5604 element = EL_SIGN_HEART;
5608 element = EL_SIGN_GIVE_WAY;
5612 element = EL_SIGN_ENTRY_FORBIDDEN;
5616 element = EL_SIGN_EMERGENCY_EXIT;
5620 element = EL_SIGN_YIN_YANG;
5624 element = EL_WALL_EMERALD;
5628 element = EL_WALL_DIAMOND;
5632 element = EL_WALL_PEARL;
5636 element = EL_WALL_CRYSTAL;
5640 element = EL_INVISIBLE_WALL;
5644 element = EL_INVISIBLE_STEELWALL;
5648 // EL_INVISIBLE_SAND
5651 element = EL_LIGHT_SWITCH;
5655 element = EL_ENVELOPE_1;
5659 if (element >= 0x0117 && element <= 0x036e) // (?)
5660 element = EL_DIAMOND;
5661 else if (element >= 0x042d && element <= 0x0684) // (?)
5662 element = EL_EMERALD;
5663 else if (element >= 0x157c && element <= 0x158b)
5665 else if (element >= 0x1590 && element <= 0x159f)
5666 element = EL_DC_LANDMINE;
5667 else if (element >= 0x16bc && element <= 0x16cb)
5668 element = EL_INVISIBLE_SAND;
5671 Warn("unknown Diamond Caves element 0x%04x", element);
5673 element = EL_UNKNOWN;
5678 return getMappedElement(element);
5681 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5683 byte header[DC_LEVEL_HEADER_SIZE];
5685 int envelope_header_pos = 62;
5686 int envelope_content_pos = 94;
5687 int level_name_pos = 251;
5688 int level_author_pos = 292;
5689 int envelope_header_len;
5690 int envelope_content_len;
5692 int level_author_len;
5694 int num_yamyam_contents;
5697 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5699 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5701 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5703 header[i * 2 + 0] = header_word >> 8;
5704 header[i * 2 + 1] = header_word & 0xff;
5707 // read some values from level header to check level decoding integrity
5708 fieldx = header[6] | (header[7] << 8);
5709 fieldy = header[8] | (header[9] << 8);
5710 num_yamyam_contents = header[60] | (header[61] << 8);
5712 // do some simple sanity checks to ensure that level was correctly decoded
5713 if (fieldx < 1 || fieldx > 256 ||
5714 fieldy < 1 || fieldy > 256 ||
5715 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5717 level->no_valid_file = TRUE;
5719 Warn("cannot decode level from stream -- using empty level");
5724 // maximum envelope header size is 31 bytes
5725 envelope_header_len = header[envelope_header_pos];
5726 // maximum envelope content size is 110 (156?) bytes
5727 envelope_content_len = header[envelope_content_pos];
5729 // maximum level title size is 40 bytes
5730 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5731 // maximum level author size is 30 (51?) bytes
5732 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5736 for (i = 0; i < envelope_header_len; i++)
5737 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5738 level->envelope[0].text[envelope_size++] =
5739 header[envelope_header_pos + 1 + i];
5741 if (envelope_header_len > 0 && envelope_content_len > 0)
5743 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5744 level->envelope[0].text[envelope_size++] = '\n';
5745 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5746 level->envelope[0].text[envelope_size++] = '\n';
5749 for (i = 0; i < envelope_content_len; i++)
5750 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5751 level->envelope[0].text[envelope_size++] =
5752 header[envelope_content_pos + 1 + i];
5754 level->envelope[0].text[envelope_size] = '\0';
5756 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5757 level->envelope[0].ysize = 10;
5758 level->envelope[0].autowrap = TRUE;
5759 level->envelope[0].centered = TRUE;
5761 for (i = 0; i < level_name_len; i++)
5762 level->name[i] = header[level_name_pos + 1 + i];
5763 level->name[level_name_len] = '\0';
5765 for (i = 0; i < level_author_len; i++)
5766 level->author[i] = header[level_author_pos + 1 + i];
5767 level->author[level_author_len] = '\0';
5769 num_yamyam_contents = header[60] | (header[61] << 8);
5770 level->num_yamyam_contents =
5771 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5773 for (i = 0; i < num_yamyam_contents; i++)
5775 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5777 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5778 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5780 if (i < MAX_ELEMENT_CONTENTS)
5781 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5785 fieldx = header[6] | (header[7] << 8);
5786 fieldy = header[8] | (header[9] << 8);
5787 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5788 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5790 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5792 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5793 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5795 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5796 level->field[x][y] = getMappedElement_DC(element_dc);
5799 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5800 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5801 level->field[x][y] = EL_PLAYER_1;
5803 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5804 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5805 level->field[x][y] = EL_PLAYER_2;
5807 level->gems_needed = header[18] | (header[19] << 8);
5809 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5810 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5811 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5812 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5813 level->score[SC_NUT] = header[28] | (header[29] << 8);
5814 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5815 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5816 level->score[SC_BUG] = header[34] | (header[35] << 8);
5817 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5818 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5819 level->score[SC_KEY] = header[40] | (header[41] << 8);
5820 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5822 level->time = header[44] | (header[45] << 8);
5824 level->amoeba_speed = header[46] | (header[47] << 8);
5825 level->time_light = header[48] | (header[49] << 8);
5826 level->time_timegate = header[50] | (header[51] << 8);
5827 level->time_wheel = header[52] | (header[53] << 8);
5828 level->time_magic_wall = header[54] | (header[55] << 8);
5829 level->extra_time = header[56] | (header[57] << 8);
5830 level->shield_normal_time = header[58] | (header[59] << 8);
5832 // shield and extra time elements do not have a score
5833 level->score[SC_SHIELD] = 0;
5834 level->extra_time_score = 0;
5836 // set time for normal and deadly shields to the same value
5837 level->shield_deadly_time = level->shield_normal_time;
5839 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5840 // can slip down from flat walls, like normal walls and steel walls
5841 level->em_slippery_gems = TRUE;
5843 // time score is counted for each 10 seconds left in Diamond Caves levels
5844 level->time_score_base = 10;
5847 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5848 struct LevelFileInfo *level_file_info,
5849 boolean level_info_only)
5851 char *filename = level_file_info->filename;
5853 int num_magic_bytes = 8;
5854 char magic_bytes[num_magic_bytes + 1];
5855 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5857 if (!(file = openFile(filename, MODE_READ)))
5859 level->no_valid_file = TRUE;
5861 if (!level_info_only)
5862 Warn("cannot read level '%s' -- using empty level", filename);
5867 // fseek(file, 0x0000, SEEK_SET);
5869 if (level_file_info->packed)
5871 // read "magic bytes" from start of file
5872 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5873 magic_bytes[0] = '\0';
5875 // check "magic bytes" for correct file format
5876 if (!strPrefix(magic_bytes, "DC2"))
5878 level->no_valid_file = TRUE;
5880 Warn("unknown DC level file '%s' -- using empty level", filename);
5885 if (strPrefix(magic_bytes, "DC2Win95") ||
5886 strPrefix(magic_bytes, "DC2Win98"))
5888 int position_first_level = 0x00fa;
5889 int extra_bytes = 4;
5892 // advance file stream to first level inside the level package
5893 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5895 // each block of level data is followed by block of non-level data
5896 num_levels_to_skip *= 2;
5898 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5899 while (num_levels_to_skip >= 0)
5901 // advance file stream to next level inside the level package
5902 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5904 level->no_valid_file = TRUE;
5906 Warn("cannot fseek in file '%s' -- using empty level", filename);
5911 // skip apparently unused extra bytes following each level
5912 ReadUnusedBytesFromFile(file, extra_bytes);
5914 // read size of next level in level package
5915 skip_bytes = getFile32BitLE(file);
5917 num_levels_to_skip--;
5922 level->no_valid_file = TRUE;
5924 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5930 LoadLevelFromFileStream_DC(file, level);
5936 // ----------------------------------------------------------------------------
5937 // functions for loading SB level
5938 // ----------------------------------------------------------------------------
5940 int getMappedElement_SB(int element_ascii, boolean use_ces)
5948 sb_element_mapping[] =
5950 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5951 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5952 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5953 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5954 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5955 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5956 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5957 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5964 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5965 if (element_ascii == sb_element_mapping[i].ascii)
5966 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5968 return EL_UNDEFINED;
5971 static void SetLevelSettings_SB(struct LevelInfo *level)
5975 level->use_step_counter = TRUE;
5978 level->score[SC_TIME_BONUS] = 0;
5979 level->time_score_base = 1;
5980 level->rate_time_over_score = TRUE;
5983 level->auto_exit_sokoban = TRUE;
5986 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5987 struct LevelFileInfo *level_file_info,
5988 boolean level_info_only)
5990 char *filename = level_file_info->filename;
5991 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5992 char last_comment[MAX_LINE_LEN];
5993 char level_name[MAX_LINE_LEN];
5996 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5997 boolean read_continued_line = FALSE;
5998 boolean reading_playfield = FALSE;
5999 boolean got_valid_playfield_line = FALSE;
6000 boolean invalid_playfield_char = FALSE;
6001 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6002 int file_level_nr = 0;
6004 int x = 0, y = 0; // initialized to make compilers happy
6006 last_comment[0] = '\0';
6007 level_name[0] = '\0';
6009 if (!(file = openFile(filename, MODE_READ)))
6011 level->no_valid_file = TRUE;
6013 if (!level_info_only)
6014 Warn("cannot read level '%s' -- using empty level", filename);
6019 while (!checkEndOfFile(file))
6021 // level successfully read, but next level may follow here
6022 if (!got_valid_playfield_line && reading_playfield)
6024 // read playfield from single level file -- skip remaining file
6025 if (!level_file_info->packed)
6028 if (file_level_nr >= num_levels_to_skip)
6033 last_comment[0] = '\0';
6034 level_name[0] = '\0';
6036 reading_playfield = FALSE;
6039 got_valid_playfield_line = FALSE;
6041 // read next line of input file
6042 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6045 // check if line was completely read and is terminated by line break
6046 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6049 // cut trailing line break (this can be newline and/or carriage return)
6050 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6051 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6054 // copy raw input line for later use (mainly debugging output)
6055 strcpy(line_raw, line);
6057 if (read_continued_line)
6059 // append new line to existing line, if there is enough space
6060 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6061 strcat(previous_line, line_ptr);
6063 strcpy(line, previous_line); // copy storage buffer to line
6065 read_continued_line = FALSE;
6068 // if the last character is '\', continue at next line
6069 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6071 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6072 strcpy(previous_line, line); // copy line to storage buffer
6074 read_continued_line = TRUE;
6080 if (line[0] == '\0')
6083 // extract comment text from comment line
6086 for (line_ptr = line; *line_ptr; line_ptr++)
6087 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6090 strcpy(last_comment, line_ptr);
6095 // extract level title text from line containing level title
6096 if (line[0] == '\'')
6098 strcpy(level_name, &line[1]);
6100 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6101 level_name[strlen(level_name) - 1] = '\0';
6106 // skip lines containing only spaces (or empty lines)
6107 for (line_ptr = line; *line_ptr; line_ptr++)
6108 if (*line_ptr != ' ')
6110 if (*line_ptr == '\0')
6113 // at this point, we have found a line containing part of a playfield
6115 got_valid_playfield_line = TRUE;
6117 if (!reading_playfield)
6119 reading_playfield = TRUE;
6120 invalid_playfield_char = FALSE;
6122 for (x = 0; x < MAX_LEV_FIELDX; x++)
6123 for (y = 0; y < MAX_LEV_FIELDY; y++)
6124 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6129 // start with topmost tile row
6133 // skip playfield line if larger row than allowed
6134 if (y >= MAX_LEV_FIELDY)
6137 // start with leftmost tile column
6140 // read playfield elements from line
6141 for (line_ptr = line; *line_ptr; line_ptr++)
6143 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6145 // stop parsing playfield line if larger column than allowed
6146 if (x >= MAX_LEV_FIELDX)
6149 if (mapped_sb_element == EL_UNDEFINED)
6151 invalid_playfield_char = TRUE;
6156 level->field[x][y] = mapped_sb_element;
6158 // continue with next tile column
6161 level->fieldx = MAX(x, level->fieldx);
6164 if (invalid_playfield_char)
6166 // if first playfield line, treat invalid lines as comment lines
6168 reading_playfield = FALSE;
6173 // continue with next tile row
6181 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6182 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6184 if (!reading_playfield)
6186 level->no_valid_file = TRUE;
6188 Warn("cannot read level '%s' -- using empty level", filename);
6193 if (*level_name != '\0')
6195 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6196 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6198 else if (*last_comment != '\0')
6200 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6201 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6205 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6208 // set all empty fields beyond the border walls to invisible steel wall
6209 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6211 if ((x == 0 || x == level->fieldx - 1 ||
6212 y == 0 || y == level->fieldy - 1) &&
6213 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6214 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6215 level->field, level->fieldx, level->fieldy);
6218 // set special level settings for Sokoban levels
6219 SetLevelSettings_SB(level);
6221 if (load_xsb_to_ces)
6223 // special global settings can now be set in level template
6224 level->use_custom_template = TRUE;
6229 // -------------------------------------------------------------------------
6230 // functions for handling native levels
6231 // -------------------------------------------------------------------------
6233 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6234 struct LevelFileInfo *level_file_info,
6235 boolean level_info_only)
6237 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6238 level->no_valid_file = TRUE;
6241 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6242 struct LevelFileInfo *level_file_info,
6243 boolean level_info_only)
6247 // determine position of requested level inside level package
6248 if (level_file_info->packed)
6249 pos = level_file_info->nr - leveldir_current->first_level;
6251 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6252 level->no_valid_file = TRUE;
6255 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6256 struct LevelFileInfo *level_file_info,
6257 boolean level_info_only)
6259 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6260 level->no_valid_file = TRUE;
6263 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6265 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6266 CopyNativeLevel_RND_to_EM(level);
6267 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6268 CopyNativeLevel_RND_to_SP(level);
6269 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6270 CopyNativeLevel_RND_to_MM(level);
6273 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6275 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6276 CopyNativeLevel_EM_to_RND(level);
6277 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6278 CopyNativeLevel_SP_to_RND(level);
6279 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6280 CopyNativeLevel_MM_to_RND(level);
6283 void SaveNativeLevel(struct LevelInfo *level)
6285 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6287 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6288 char *filename = getLevelFilenameFromBasename(basename);
6290 CopyNativeLevel_RND_to_SP(level);
6291 CopyNativeTape_RND_to_SP(level);
6293 SaveNativeLevel_SP(filename);
6298 // ----------------------------------------------------------------------------
6299 // functions for loading generic level
6300 // ----------------------------------------------------------------------------
6302 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6303 struct LevelFileInfo *level_file_info,
6304 boolean level_info_only)
6306 // always start with reliable default values
6307 setLevelInfoToDefaults(level, level_info_only, TRUE);
6309 switch (level_file_info->type)
6311 case LEVEL_FILE_TYPE_RND:
6312 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6315 case LEVEL_FILE_TYPE_EM:
6316 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6317 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6320 case LEVEL_FILE_TYPE_SP:
6321 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6322 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6325 case LEVEL_FILE_TYPE_MM:
6326 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6327 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6330 case LEVEL_FILE_TYPE_DC:
6331 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6334 case LEVEL_FILE_TYPE_SB:
6335 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6339 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6343 // if level file is invalid, restore level structure to default values
6344 if (level->no_valid_file)
6345 setLevelInfoToDefaults(level, level_info_only, FALSE);
6347 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6348 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6350 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6351 CopyNativeLevel_Native_to_RND(level);
6354 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6356 static struct LevelFileInfo level_file_info;
6358 // always start with reliable default values
6359 setFileInfoToDefaults(&level_file_info);
6361 level_file_info.nr = 0; // unknown level number
6362 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6364 setString(&level_file_info.filename, filename);
6366 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6369 static void LoadLevel_InitVersion(struct LevelInfo *level)
6373 if (leveldir_current == NULL) // only when dumping level
6376 // all engine modifications also valid for levels which use latest engine
6377 if (level->game_version < VERSION_IDENT(3,2,0,5))
6379 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6380 level->time_score_base = 10;
6383 if (leveldir_current->latest_engine)
6385 // ---------- use latest game engine --------------------------------------
6387 /* For all levels which are forced to use the latest game engine version
6388 (normally all but user contributed, private and undefined levels), set
6389 the game engine version to the actual version; this allows for actual
6390 corrections in the game engine to take effect for existing, converted
6391 levels (from "classic" or other existing games) to make the emulation
6392 of the corresponding game more accurate, while (hopefully) not breaking
6393 existing levels created from other players. */
6395 level->game_version = GAME_VERSION_ACTUAL;
6397 /* Set special EM style gems behaviour: EM style gems slip down from
6398 normal, steel and growing wall. As this is a more fundamental change,
6399 it seems better to set the default behaviour to "off" (as it is more
6400 natural) and make it configurable in the level editor (as a property
6401 of gem style elements). Already existing converted levels (neither
6402 private nor contributed levels) are changed to the new behaviour. */
6404 if (level->file_version < FILE_VERSION_2_0)
6405 level->em_slippery_gems = TRUE;
6410 // ---------- use game engine the level was created with --------------------
6412 /* For all levels which are not forced to use the latest game engine
6413 version (normally user contributed, private and undefined levels),
6414 use the version of the game engine the levels were created for.
6416 Since 2.0.1, the game engine version is now directly stored
6417 in the level file (chunk "VERS"), so there is no need anymore
6418 to set the game version from the file version (except for old,
6419 pre-2.0 levels, where the game version is still taken from the
6420 file format version used to store the level -- see above). */
6422 // player was faster than enemies in 1.0.0 and before
6423 if (level->file_version == FILE_VERSION_1_0)
6424 for (i = 0; i < MAX_PLAYERS; i++)
6425 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6427 // default behaviour for EM style gems was "slippery" only in 2.0.1
6428 if (level->game_version == VERSION_IDENT(2,0,1,0))
6429 level->em_slippery_gems = TRUE;
6431 // springs could be pushed over pits before (pre-release version) 2.2.0
6432 if (level->game_version < VERSION_IDENT(2,2,0,0))
6433 level->use_spring_bug = TRUE;
6435 if (level->game_version < VERSION_IDENT(3,2,0,5))
6437 // time orb caused limited time in endless time levels before 3.2.0-5
6438 level->use_time_orb_bug = TRUE;
6440 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6441 level->block_snap_field = FALSE;
6443 // extra time score was same value as time left score before 3.2.0-5
6444 level->extra_time_score = level->score[SC_TIME_BONUS];
6447 if (level->game_version < VERSION_IDENT(3,2,0,7))
6449 // default behaviour for snapping was "not continuous" before 3.2.0-7
6450 level->continuous_snapping = FALSE;
6453 // only few elements were able to actively move into acid before 3.1.0
6454 // trigger settings did not exist before 3.1.0; set to default "any"
6455 if (level->game_version < VERSION_IDENT(3,1,0,0))
6457 // correct "can move into acid" settings (all zero in old levels)
6459 level->can_move_into_acid_bits = 0; // nothing can move into acid
6460 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6462 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6463 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6464 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6465 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6468 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6470 // correct trigger settings (stored as zero == "none" in old levels)
6472 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6474 int element = EL_CUSTOM_START + i;
6475 struct ElementInfo *ei = &element_info[element];
6477 for (j = 0; j < ei->num_change_pages; j++)
6479 struct ElementChangeInfo *change = &ei->change_page[j];
6481 change->trigger_player = CH_PLAYER_ANY;
6482 change->trigger_page = CH_PAGE_ANY;
6487 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6489 int element = EL_CUSTOM_256;
6490 struct ElementInfo *ei = &element_info[element];
6491 struct ElementChangeInfo *change = &ei->change_page[0];
6493 /* This is needed to fix a problem that was caused by a bugfix in function
6494 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6495 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6496 not replace walkable elements, but instead just placed the player on it,
6497 without placing the Sokoban field under the player). Unfortunately, this
6498 breaks "Snake Bite" style levels when the snake is halfway through a door
6499 that just closes (the snake head is still alive and can be moved in this
6500 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6501 player (without Sokoban element) which then gets killed as designed). */
6503 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6504 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6505 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6506 change->target_element = EL_PLAYER_1;
6509 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6510 if (level->game_version < VERSION_IDENT(3,2,5,0))
6512 /* This is needed to fix a problem that was caused by a bugfix in function
6513 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6514 corrects the behaviour when a custom element changes to another custom
6515 element with a higher element number that has change actions defined.
6516 Normally, only one change per frame is allowed for custom elements.
6517 Therefore, it is checked if a custom element already changed in the
6518 current frame; if it did, subsequent changes are suppressed.
6519 Unfortunately, this is only checked for element changes, but not for
6520 change actions, which are still executed. As the function above loops
6521 through all custom elements from lower to higher, an element change
6522 resulting in a lower CE number won't be checked again, while a target
6523 element with a higher number will also be checked, and potential change
6524 actions will get executed for this CE, too (which is wrong), while
6525 further changes are ignored (which is correct). As this bugfix breaks
6526 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6527 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6528 behaviour for existing levels and tapes that make use of this bug */
6530 level->use_action_after_change_bug = TRUE;
6533 // not centering level after relocating player was default only in 3.2.3
6534 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6535 level->shifted_relocation = TRUE;
6537 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6538 if (level->game_version < VERSION_IDENT(3,2,6,0))
6539 level->em_explodes_by_fire = TRUE;
6541 // levels were solved by the first player entering an exit up to 4.1.0.0
6542 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6543 level->solved_by_one_player = TRUE;
6545 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6546 if (level->game_version < VERSION_IDENT(4,1,1,1))
6547 level->use_life_bugs = TRUE;
6549 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6550 if (level->game_version < VERSION_IDENT(4,1,1,1))
6551 level->sb_objects_needed = FALSE;
6553 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6554 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6555 level->finish_dig_collect = FALSE;
6557 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6558 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6559 level->keep_walkable_ce = TRUE;
6562 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6564 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6567 // check if this level is (not) a Sokoban level
6568 for (y = 0; y < level->fieldy; y++)
6569 for (x = 0; x < level->fieldx; x++)
6570 if (!IS_SB_ELEMENT(Tile[x][y]))
6571 is_sokoban_level = FALSE;
6573 if (is_sokoban_level)
6575 // set special level settings for Sokoban levels
6576 SetLevelSettings_SB(level);
6580 static void LoadLevel_InitSettings(struct LevelInfo *level)
6582 // adjust level settings for (non-native) Sokoban-style levels
6583 LoadLevel_InitSettings_SB(level);
6585 // rename levels with title "nameless level" or if renaming is forced
6586 if (leveldir_current->empty_level_name != NULL &&
6587 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6588 leveldir_current->force_level_name))
6589 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6590 leveldir_current->empty_level_name, level_nr);
6593 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6597 // map elements that have changed in newer versions
6598 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6599 level->game_version);
6600 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6601 for (x = 0; x < 3; x++)
6602 for (y = 0; y < 3; y++)
6603 level->yamyam_content[i].e[x][y] =
6604 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6605 level->game_version);
6609 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6613 // map custom element change events that have changed in newer versions
6614 // (these following values were accidentally changed in version 3.0.1)
6615 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6616 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6618 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6620 int element = EL_CUSTOM_START + i;
6622 // order of checking and copying events to be mapped is important
6623 // (do not change the start and end value -- they are constant)
6624 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6626 if (HAS_CHANGE_EVENT(element, j - 2))
6628 SET_CHANGE_EVENT(element, j - 2, FALSE);
6629 SET_CHANGE_EVENT(element, j, TRUE);
6633 // order of checking and copying events to be mapped is important
6634 // (do not change the start and end value -- they are constant)
6635 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6637 if (HAS_CHANGE_EVENT(element, j - 1))
6639 SET_CHANGE_EVENT(element, j - 1, FALSE);
6640 SET_CHANGE_EVENT(element, j, TRUE);
6646 // initialize "can_change" field for old levels with only one change page
6647 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6649 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6651 int element = EL_CUSTOM_START + i;
6653 if (CAN_CHANGE(element))
6654 element_info[element].change->can_change = TRUE;
6658 // correct custom element values (for old levels without these options)
6659 if (level->game_version < VERSION_IDENT(3,1,1,0))
6661 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6663 int element = EL_CUSTOM_START + i;
6664 struct ElementInfo *ei = &element_info[element];
6666 if (ei->access_direction == MV_NO_DIRECTION)
6667 ei->access_direction = MV_ALL_DIRECTIONS;
6671 // correct custom element values (fix invalid values for all versions)
6674 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6676 int element = EL_CUSTOM_START + i;
6677 struct ElementInfo *ei = &element_info[element];
6679 for (j = 0; j < ei->num_change_pages; j++)
6681 struct ElementChangeInfo *change = &ei->change_page[j];
6683 if (change->trigger_player == CH_PLAYER_NONE)
6684 change->trigger_player = CH_PLAYER_ANY;
6686 if (change->trigger_side == CH_SIDE_NONE)
6687 change->trigger_side = CH_SIDE_ANY;
6692 // initialize "can_explode" field for old levels which did not store this
6693 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6694 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6696 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6698 int element = EL_CUSTOM_START + i;
6700 if (EXPLODES_1X1_OLD(element))
6701 element_info[element].explosion_type = EXPLODES_1X1;
6703 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6704 EXPLODES_SMASHED(element) ||
6705 EXPLODES_IMPACT(element)));
6709 // correct previously hard-coded move delay values for maze runner style
6710 if (level->game_version < VERSION_IDENT(3,1,1,0))
6712 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6714 int element = EL_CUSTOM_START + i;
6716 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6718 // previously hard-coded and therefore ignored
6719 element_info[element].move_delay_fixed = 9;
6720 element_info[element].move_delay_random = 0;
6725 // set some other uninitialized values of custom elements in older levels
6726 if (level->game_version < VERSION_IDENT(3,1,0,0))
6728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6730 int element = EL_CUSTOM_START + i;
6732 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6734 element_info[element].explosion_delay = 17;
6735 element_info[element].ignition_delay = 8;
6739 // set mouse click change events to work for left/middle/right mouse button
6740 if (level->game_version < VERSION_IDENT(4,2,3,0))
6742 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6744 int element = EL_CUSTOM_START + i;
6745 struct ElementInfo *ei = &element_info[element];
6747 for (j = 0; j < ei->num_change_pages; j++)
6749 struct ElementChangeInfo *change = &ei->change_page[j];
6751 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6752 change->has_event[CE_PRESSED_BY_MOUSE] ||
6753 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6754 change->has_event[CE_MOUSE_PRESSED_ON_X])
6755 change->trigger_side = CH_SIDE_ANY;
6761 static void LoadLevel_InitElements(struct LevelInfo *level)
6763 LoadLevel_InitStandardElements(level);
6765 if (level->file_has_custom_elements)
6766 LoadLevel_InitCustomElements(level);
6768 // initialize element properties for level editor etc.
6769 InitElementPropertiesEngine(level->game_version);
6770 InitElementPropertiesGfxElement();
6773 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6777 // map elements that have changed in newer versions
6778 for (y = 0; y < level->fieldy; y++)
6779 for (x = 0; x < level->fieldx; x++)
6780 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6781 level->game_version);
6783 // clear unused playfield data (nicer if level gets resized in editor)
6784 for (x = 0; x < MAX_LEV_FIELDX; x++)
6785 for (y = 0; y < MAX_LEV_FIELDY; y++)
6786 if (x >= level->fieldx || y >= level->fieldy)
6787 level->field[x][y] = EL_EMPTY;
6789 // copy elements to runtime playfield array
6790 for (x = 0; x < MAX_LEV_FIELDX; x++)
6791 for (y = 0; y < MAX_LEV_FIELDY; y++)
6792 Tile[x][y] = level->field[x][y];
6794 // initialize level size variables for faster access
6795 lev_fieldx = level->fieldx;
6796 lev_fieldy = level->fieldy;
6798 // determine border element for this level
6799 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6800 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6805 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6807 struct LevelFileInfo *level_file_info = &level->file_info;
6809 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6810 CopyNativeLevel_RND_to_Native(level);
6813 static void LoadLevelTemplate_LoadAndInit(void)
6815 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6817 LoadLevel_InitVersion(&level_template);
6818 LoadLevel_InitElements(&level_template);
6819 LoadLevel_InitSettings(&level_template);
6821 ActivateLevelTemplate();
6824 void LoadLevelTemplate(int nr)
6826 if (!fileExists(getGlobalLevelTemplateFilename()))
6828 Warn("no level template found for this level");
6833 setLevelFileInfo(&level_template.file_info, nr);
6835 LoadLevelTemplate_LoadAndInit();
6838 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6840 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6842 LoadLevelTemplate_LoadAndInit();
6845 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6847 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6849 if (level.use_custom_template)
6851 if (network_level != NULL)
6852 LoadNetworkLevelTemplate(network_level);
6854 LoadLevelTemplate(-1);
6857 LoadLevel_InitVersion(&level);
6858 LoadLevel_InitElements(&level);
6859 LoadLevel_InitPlayfield(&level);
6860 LoadLevel_InitSettings(&level);
6862 LoadLevel_InitNativeEngines(&level);
6865 void LoadLevel(int nr)
6867 SetLevelSetInfo(leveldir_current->identifier, nr);
6869 setLevelFileInfo(&level.file_info, nr);
6871 LoadLevel_LoadAndInit(NULL);
6874 void LoadLevelInfoOnly(int nr)
6876 setLevelFileInfo(&level.file_info, nr);
6878 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6881 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6883 SetLevelSetInfo(network_level->leveldir_identifier,
6884 network_level->file_info.nr);
6886 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6888 LoadLevel_LoadAndInit(network_level);
6891 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6895 chunk_size += putFileVersion(file, level->file_version);
6896 chunk_size += putFileVersion(file, level->game_version);
6901 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6905 chunk_size += putFile16BitBE(file, level->creation_date.year);
6906 chunk_size += putFile8Bit(file, level->creation_date.month);
6907 chunk_size += putFile8Bit(file, level->creation_date.day);
6912 #if ENABLE_HISTORIC_CHUNKS
6913 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6917 putFile8Bit(file, level->fieldx);
6918 putFile8Bit(file, level->fieldy);
6920 putFile16BitBE(file, level->time);
6921 putFile16BitBE(file, level->gems_needed);
6923 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6924 putFile8Bit(file, level->name[i]);
6926 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6927 putFile8Bit(file, level->score[i]);
6929 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6930 for (y = 0; y < 3; y++)
6931 for (x = 0; x < 3; x++)
6932 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6933 level->yamyam_content[i].e[x][y]));
6934 putFile8Bit(file, level->amoeba_speed);
6935 putFile8Bit(file, level->time_magic_wall);
6936 putFile8Bit(file, level->time_wheel);
6937 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6938 level->amoeba_content));
6939 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6940 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6941 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6942 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6944 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6946 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6947 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6948 putFile32BitBE(file, level->can_move_into_acid_bits);
6949 putFile8Bit(file, level->dont_collide_with_bits);
6951 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6952 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6954 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6955 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6956 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6958 putFile8Bit(file, level->game_engine_type);
6960 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6964 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6969 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6970 chunk_size += putFile8Bit(file, level->name[i]);
6975 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6980 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6981 chunk_size += putFile8Bit(file, level->author[i]);
6986 #if ENABLE_HISTORIC_CHUNKS
6987 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6992 for (y = 0; y < level->fieldy; y++)
6993 for (x = 0; x < level->fieldx; x++)
6994 if (level->encoding_16bit_field)
6995 chunk_size += putFile16BitBE(file, level->field[x][y]);
6997 chunk_size += putFile8Bit(file, level->field[x][y]);
7003 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7008 for (y = 0; y < level->fieldy; y++)
7009 for (x = 0; x < level->fieldx; x++)
7010 chunk_size += putFile16BitBE(file, level->field[x][y]);
7015 #if ENABLE_HISTORIC_CHUNKS
7016 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7020 putFile8Bit(file, EL_YAMYAM);
7021 putFile8Bit(file, level->num_yamyam_contents);
7022 putFile8Bit(file, 0);
7023 putFile8Bit(file, 0);
7025 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7026 for (y = 0; y < 3; y++)
7027 for (x = 0; x < 3; x++)
7028 if (level->encoding_16bit_field)
7029 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7031 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7035 #if ENABLE_HISTORIC_CHUNKS
7036 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7039 int num_contents, content_xsize, content_ysize;
7040 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7042 if (element == EL_YAMYAM)
7044 num_contents = level->num_yamyam_contents;
7048 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7049 for (y = 0; y < 3; y++)
7050 for (x = 0; x < 3; x++)
7051 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7053 else if (element == EL_BD_AMOEBA)
7059 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7060 for (y = 0; y < 3; y++)
7061 for (x = 0; x < 3; x++)
7062 content_array[i][x][y] = EL_EMPTY;
7063 content_array[0][0][0] = level->amoeba_content;
7067 // chunk header already written -- write empty chunk data
7068 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7070 Warn("cannot save content for element '%d'", element);
7075 putFile16BitBE(file, element);
7076 putFile8Bit(file, num_contents);
7077 putFile8Bit(file, content_xsize);
7078 putFile8Bit(file, content_ysize);
7080 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7082 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7083 for (y = 0; y < 3; y++)
7084 for (x = 0; x < 3; x++)
7085 putFile16BitBE(file, content_array[i][x][y]);
7089 #if ENABLE_HISTORIC_CHUNKS
7090 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7092 int envelope_nr = element - EL_ENVELOPE_1;
7093 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7097 chunk_size += putFile16BitBE(file, element);
7098 chunk_size += putFile16BitBE(file, envelope_len);
7099 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7100 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7102 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7103 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7105 for (i = 0; i < envelope_len; i++)
7106 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7112 #if ENABLE_HISTORIC_CHUNKS
7113 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7114 int num_changed_custom_elements)
7118 putFile16BitBE(file, num_changed_custom_elements);
7120 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7122 int element = EL_CUSTOM_START + i;
7124 struct ElementInfo *ei = &element_info[element];
7126 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7128 if (check < num_changed_custom_elements)
7130 putFile16BitBE(file, element);
7131 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7138 if (check != num_changed_custom_elements) // should not happen
7139 Warn("inconsistent number of custom element properties");
7143 #if ENABLE_HISTORIC_CHUNKS
7144 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7145 int num_changed_custom_elements)
7149 putFile16BitBE(file, num_changed_custom_elements);
7151 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7153 int element = EL_CUSTOM_START + i;
7155 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7157 if (check < num_changed_custom_elements)
7159 putFile16BitBE(file, element);
7160 putFile16BitBE(file, element_info[element].change->target_element);
7167 if (check != num_changed_custom_elements) // should not happen
7168 Warn("inconsistent number of custom target elements");
7172 #if ENABLE_HISTORIC_CHUNKS
7173 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7174 int num_changed_custom_elements)
7176 int i, j, x, y, check = 0;
7178 putFile16BitBE(file, num_changed_custom_elements);
7180 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7182 int element = EL_CUSTOM_START + i;
7183 struct ElementInfo *ei = &element_info[element];
7185 if (ei->modified_settings)
7187 if (check < num_changed_custom_elements)
7189 putFile16BitBE(file, element);
7191 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7192 putFile8Bit(file, ei->description[j]);
7194 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7196 // some free bytes for future properties and padding
7197 WriteUnusedBytesToFile(file, 7);
7199 putFile8Bit(file, ei->use_gfx_element);
7200 putFile16BitBE(file, ei->gfx_element_initial);
7202 putFile8Bit(file, ei->collect_score_initial);
7203 putFile8Bit(file, ei->collect_count_initial);
7205 putFile16BitBE(file, ei->push_delay_fixed);
7206 putFile16BitBE(file, ei->push_delay_random);
7207 putFile16BitBE(file, ei->move_delay_fixed);
7208 putFile16BitBE(file, ei->move_delay_random);
7210 putFile16BitBE(file, ei->move_pattern);
7211 putFile8Bit(file, ei->move_direction_initial);
7212 putFile8Bit(file, ei->move_stepsize);
7214 for (y = 0; y < 3; y++)
7215 for (x = 0; x < 3; x++)
7216 putFile16BitBE(file, ei->content.e[x][y]);
7218 putFile32BitBE(file, ei->change->events);
7220 putFile16BitBE(file, ei->change->target_element);
7222 putFile16BitBE(file, ei->change->delay_fixed);
7223 putFile16BitBE(file, ei->change->delay_random);
7224 putFile16BitBE(file, ei->change->delay_frames);
7226 putFile16BitBE(file, ei->change->initial_trigger_element);
7228 putFile8Bit(file, ei->change->explode);
7229 putFile8Bit(file, ei->change->use_target_content);
7230 putFile8Bit(file, ei->change->only_if_complete);
7231 putFile8Bit(file, ei->change->use_random_replace);
7233 putFile8Bit(file, ei->change->random_percentage);
7234 putFile8Bit(file, ei->change->replace_when);
7236 for (y = 0; y < 3; y++)
7237 for (x = 0; x < 3; x++)
7238 putFile16BitBE(file, ei->change->content.e[x][y]);
7240 putFile8Bit(file, ei->slippery_type);
7242 // some free bytes for future properties and padding
7243 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7250 if (check != num_changed_custom_elements) // should not happen
7251 Warn("inconsistent number of custom element properties");
7255 #if ENABLE_HISTORIC_CHUNKS
7256 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7258 struct ElementInfo *ei = &element_info[element];
7261 // ---------- custom element base property values (96 bytes) ----------------
7263 putFile16BitBE(file, element);
7265 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7266 putFile8Bit(file, ei->description[i]);
7268 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7270 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7272 putFile8Bit(file, ei->num_change_pages);
7274 putFile16BitBE(file, ei->ce_value_fixed_initial);
7275 putFile16BitBE(file, ei->ce_value_random_initial);
7276 putFile8Bit(file, ei->use_last_ce_value);
7278 putFile8Bit(file, ei->use_gfx_element);
7279 putFile16BitBE(file, ei->gfx_element_initial);
7281 putFile8Bit(file, ei->collect_score_initial);
7282 putFile8Bit(file, ei->collect_count_initial);
7284 putFile8Bit(file, ei->drop_delay_fixed);
7285 putFile8Bit(file, ei->push_delay_fixed);
7286 putFile8Bit(file, ei->drop_delay_random);
7287 putFile8Bit(file, ei->push_delay_random);
7288 putFile16BitBE(file, ei->move_delay_fixed);
7289 putFile16BitBE(file, ei->move_delay_random);
7291 // bits 0 - 15 of "move_pattern" ...
7292 putFile16BitBE(file, ei->move_pattern & 0xffff);
7293 putFile8Bit(file, ei->move_direction_initial);
7294 putFile8Bit(file, ei->move_stepsize);
7296 putFile8Bit(file, ei->slippery_type);
7298 for (y = 0; y < 3; y++)
7299 for (x = 0; x < 3; x++)
7300 putFile16BitBE(file, ei->content.e[x][y]);
7302 putFile16BitBE(file, ei->move_enter_element);
7303 putFile16BitBE(file, ei->move_leave_element);
7304 putFile8Bit(file, ei->move_leave_type);
7306 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7307 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7309 putFile8Bit(file, ei->access_direction);
7311 putFile8Bit(file, ei->explosion_delay);
7312 putFile8Bit(file, ei->ignition_delay);
7313 putFile8Bit(file, ei->explosion_type);
7315 // some free bytes for future custom property values and padding
7316 WriteUnusedBytesToFile(file, 1);
7318 // ---------- change page property values (48 bytes) ------------------------
7320 for (i = 0; i < ei->num_change_pages; i++)
7322 struct ElementChangeInfo *change = &ei->change_page[i];
7323 unsigned int event_bits;
7325 // bits 0 - 31 of "has_event[]" ...
7327 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7328 if (change->has_event[j])
7329 event_bits |= (1u << j);
7330 putFile32BitBE(file, event_bits);
7332 putFile16BitBE(file, change->target_element);
7334 putFile16BitBE(file, change->delay_fixed);
7335 putFile16BitBE(file, change->delay_random);
7336 putFile16BitBE(file, change->delay_frames);
7338 putFile16BitBE(file, change->initial_trigger_element);
7340 putFile8Bit(file, change->explode);
7341 putFile8Bit(file, change->use_target_content);
7342 putFile8Bit(file, change->only_if_complete);
7343 putFile8Bit(file, change->use_random_replace);
7345 putFile8Bit(file, change->random_percentage);
7346 putFile8Bit(file, change->replace_when);
7348 for (y = 0; y < 3; y++)
7349 for (x = 0; x < 3; x++)
7350 putFile16BitBE(file, change->target_content.e[x][y]);
7352 putFile8Bit(file, change->can_change);
7354 putFile8Bit(file, change->trigger_side);
7356 putFile8Bit(file, change->trigger_player);
7357 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7358 log_2(change->trigger_page)));
7360 putFile8Bit(file, change->has_action);
7361 putFile8Bit(file, change->action_type);
7362 putFile8Bit(file, change->action_mode);
7363 putFile16BitBE(file, change->action_arg);
7365 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7367 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7368 if (change->has_event[j])
7369 event_bits |= (1u << (j - 32));
7370 putFile8Bit(file, event_bits);
7375 #if ENABLE_HISTORIC_CHUNKS
7376 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7378 struct ElementInfo *ei = &element_info[element];
7379 struct ElementGroupInfo *group = ei->group;
7382 putFile16BitBE(file, element);
7384 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7385 putFile8Bit(file, ei->description[i]);
7387 putFile8Bit(file, group->num_elements);
7389 putFile8Bit(file, ei->use_gfx_element);
7390 putFile16BitBE(file, ei->gfx_element_initial);
7392 putFile8Bit(file, group->choice_mode);
7394 // some free bytes for future values and padding
7395 WriteUnusedBytesToFile(file, 3);
7397 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7398 putFile16BitBE(file, group->element[i]);
7402 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7403 boolean write_element)
7405 int save_type = entry->save_type;
7406 int data_type = entry->data_type;
7407 int conf_type = entry->conf_type;
7408 int byte_mask = conf_type & CONF_MASK_BYTES;
7409 int element = entry->element;
7410 int default_value = entry->default_value;
7412 boolean modified = FALSE;
7414 if (byte_mask != CONF_MASK_MULTI_BYTES)
7416 void *value_ptr = entry->value;
7417 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7420 // check if any settings have been modified before saving them
7421 if (value != default_value)
7424 // do not save if explicitly told or if unmodified default settings
7425 if ((save_type == SAVE_CONF_NEVER) ||
7426 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7430 num_bytes += putFile16BitBE(file, element);
7432 num_bytes += putFile8Bit(file, conf_type);
7433 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7434 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7435 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7438 else if (data_type == TYPE_STRING)
7440 char *default_string = entry->default_string;
7441 char *string = (char *)(entry->value);
7442 int string_length = strlen(string);
7445 // check if any settings have been modified before saving them
7446 if (!strEqual(string, default_string))
7449 // do not save if explicitly told or if unmodified default settings
7450 if ((save_type == SAVE_CONF_NEVER) ||
7451 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7455 num_bytes += putFile16BitBE(file, element);
7457 num_bytes += putFile8Bit(file, conf_type);
7458 num_bytes += putFile16BitBE(file, string_length);
7460 for (i = 0; i < string_length; i++)
7461 num_bytes += putFile8Bit(file, string[i]);
7463 else if (data_type == TYPE_ELEMENT_LIST)
7465 int *element_array = (int *)(entry->value);
7466 int num_elements = *(int *)(entry->num_entities);
7469 // check if any settings have been modified before saving them
7470 for (i = 0; i < num_elements; i++)
7471 if (element_array[i] != default_value)
7474 // do not save if explicitly told or if unmodified default settings
7475 if ((save_type == SAVE_CONF_NEVER) ||
7476 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7480 num_bytes += putFile16BitBE(file, element);
7482 num_bytes += putFile8Bit(file, conf_type);
7483 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7485 for (i = 0; i < num_elements; i++)
7486 num_bytes += putFile16BitBE(file, element_array[i]);
7488 else if (data_type == TYPE_CONTENT_LIST)
7490 struct Content *content = (struct Content *)(entry->value);
7491 int num_contents = *(int *)(entry->num_entities);
7494 // check if any settings have been modified before saving them
7495 for (i = 0; i < num_contents; i++)
7496 for (y = 0; y < 3; y++)
7497 for (x = 0; x < 3; x++)
7498 if (content[i].e[x][y] != default_value)
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, num_contents * CONF_CONTENT_NUM_BYTES);
7512 for (i = 0; i < num_contents; i++)
7513 for (y = 0; y < 3; y++)
7514 for (x = 0; x < 3; x++)
7515 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7521 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7526 li = *level; // copy level data into temporary buffer
7528 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7529 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7534 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7539 li = *level; // copy level data into temporary buffer
7541 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7542 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7547 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7549 int envelope_nr = element - EL_ENVELOPE_1;
7553 chunk_size += putFile16BitBE(file, element);
7555 // copy envelope data into temporary buffer
7556 xx_envelope = level->envelope[envelope_nr];
7558 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7559 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7564 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7566 struct ElementInfo *ei = &element_info[element];
7570 chunk_size += putFile16BitBE(file, element);
7572 xx_ei = *ei; // copy element data into temporary buffer
7574 // set default description string for this specific element
7575 strcpy(xx_default_description, getDefaultElementDescription(ei));
7577 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7578 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7580 for (i = 0; i < ei->num_change_pages; i++)
7582 struct ElementChangeInfo *change = &ei->change_page[i];
7584 xx_current_change_page = i;
7586 xx_change = *change; // copy change data into temporary buffer
7589 setEventBitsFromEventFlags(change);
7591 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7592 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7599 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7601 struct ElementInfo *ei = &element_info[element];
7602 struct ElementGroupInfo *group = ei->group;
7606 chunk_size += putFile16BitBE(file, element);
7608 xx_ei = *ei; // copy element data into temporary buffer
7609 xx_group = *group; // copy group data into temporary buffer
7611 // set default description string for this specific element
7612 strcpy(xx_default_description, getDefaultElementDescription(ei));
7614 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7615 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7620 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7622 struct ElementInfo *ei = &element_info[element];
7626 chunk_size += putFile16BitBE(file, element);
7628 xx_ei = *ei; // copy element data into temporary buffer
7630 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7631 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7636 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7637 boolean save_as_template)
7643 if (!(file = fopen(filename, MODE_WRITE)))
7645 Warn("cannot save level file '%s'", filename);
7650 level->file_version = FILE_VERSION_ACTUAL;
7651 level->game_version = GAME_VERSION_ACTUAL;
7653 level->creation_date = getCurrentDate();
7655 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7656 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7658 chunk_size = SaveLevel_VERS(NULL, level);
7659 putFileChunkBE(file, "VERS", chunk_size);
7660 SaveLevel_VERS(file, level);
7662 chunk_size = SaveLevel_DATE(NULL, level);
7663 putFileChunkBE(file, "DATE", chunk_size);
7664 SaveLevel_DATE(file, level);
7666 chunk_size = SaveLevel_NAME(NULL, level);
7667 putFileChunkBE(file, "NAME", chunk_size);
7668 SaveLevel_NAME(file, level);
7670 chunk_size = SaveLevel_AUTH(NULL, level);
7671 putFileChunkBE(file, "AUTH", chunk_size);
7672 SaveLevel_AUTH(file, level);
7674 chunk_size = SaveLevel_INFO(NULL, level);
7675 putFileChunkBE(file, "INFO", chunk_size);
7676 SaveLevel_INFO(file, level);
7678 chunk_size = SaveLevel_BODY(NULL, level);
7679 putFileChunkBE(file, "BODY", chunk_size);
7680 SaveLevel_BODY(file, level);
7682 chunk_size = SaveLevel_ELEM(NULL, level);
7683 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7685 putFileChunkBE(file, "ELEM", chunk_size);
7686 SaveLevel_ELEM(file, level);
7689 for (i = 0; i < NUM_ENVELOPES; i++)
7691 int element = EL_ENVELOPE_1 + i;
7693 chunk_size = SaveLevel_NOTE(NULL, level, element);
7694 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7696 putFileChunkBE(file, "NOTE", chunk_size);
7697 SaveLevel_NOTE(file, level, element);
7701 // if not using template level, check for non-default custom/group elements
7702 if (!level->use_custom_template || save_as_template)
7704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7706 int element = EL_CUSTOM_START + i;
7708 chunk_size = SaveLevel_CUSX(NULL, level, element);
7709 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7711 putFileChunkBE(file, "CUSX", chunk_size);
7712 SaveLevel_CUSX(file, level, element);
7716 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7718 int element = EL_GROUP_START + i;
7720 chunk_size = SaveLevel_GRPX(NULL, level, element);
7721 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7723 putFileChunkBE(file, "GRPX", chunk_size);
7724 SaveLevel_GRPX(file, level, element);
7728 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7730 int element = GET_EMPTY_ELEMENT(i);
7732 chunk_size = SaveLevel_EMPX(NULL, level, element);
7733 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7735 putFileChunkBE(file, "EMPX", chunk_size);
7736 SaveLevel_EMPX(file, level, element);
7743 SetFilePermissions(filename, PERMS_PRIVATE);
7746 void SaveLevel(int nr)
7748 char *filename = getDefaultLevelFilename(nr);
7750 SaveLevelFromFilename(&level, filename, FALSE);
7753 void SaveLevelTemplate(void)
7755 char *filename = getLocalLevelTemplateFilename();
7757 SaveLevelFromFilename(&level, filename, TRUE);
7760 boolean SaveLevelChecked(int nr)
7762 char *filename = getDefaultLevelFilename(nr);
7763 boolean new_level = !fileExists(filename);
7764 boolean level_saved = FALSE;
7766 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7771 Request("Level saved!", REQ_CONFIRM);
7779 void DumpLevel(struct LevelInfo *level)
7781 if (level->no_level_file || level->no_valid_file)
7783 Warn("cannot dump -- no valid level file found");
7789 Print("Level xxx (file version %08d, game version %08d)\n",
7790 level->file_version, level->game_version);
7793 Print("Level author: '%s'\n", level->author);
7794 Print("Level title: '%s'\n", level->name);
7796 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7798 Print("Level time: %d seconds\n", level->time);
7799 Print("Gems needed: %d\n", level->gems_needed);
7801 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7802 Print("Time for wheel: %d seconds\n", level->time_wheel);
7803 Print("Time for light: %d seconds\n", level->time_light);
7804 Print("Time for timegate: %d seconds\n", level->time_timegate);
7806 Print("Amoeba speed: %d\n", level->amoeba_speed);
7809 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7810 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7811 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7812 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7813 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7814 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7820 for (i = 0; i < NUM_ENVELOPES; i++)
7822 char *text = level->envelope[i].text;
7823 int text_len = strlen(text);
7824 boolean has_text = FALSE;
7826 for (j = 0; j < text_len; j++)
7827 if (text[j] != ' ' && text[j] != '\n')
7833 Print("Envelope %d:\n'%s'\n", i + 1, text);
7841 void DumpLevels(void)
7843 static LevelDirTree *dumplevel_leveldir = NULL;
7845 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7846 global.dumplevel_leveldir);
7848 if (dumplevel_leveldir == NULL)
7849 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7851 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7852 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7853 Fail("no such level number: %d", global.dumplevel_level_nr);
7855 leveldir_current = dumplevel_leveldir;
7857 LoadLevel(global.dumplevel_level_nr);
7864 // ============================================================================
7865 // tape file functions
7866 // ============================================================================
7868 static void setTapeInfoToDefaults(void)
7872 // always start with reliable default values (empty tape)
7875 // default values (also for pre-1.2 tapes) with only the first player
7876 tape.player_participates[0] = TRUE;
7877 for (i = 1; i < MAX_PLAYERS; i++)
7878 tape.player_participates[i] = FALSE;
7880 // at least one (default: the first) player participates in every tape
7881 tape.num_participating_players = 1;
7883 tape.property_bits = TAPE_PROPERTY_NONE;
7885 tape.level_nr = level_nr;
7887 tape.changed = FALSE;
7888 tape.solved = FALSE;
7890 tape.recording = FALSE;
7891 tape.playing = FALSE;
7892 tape.pausing = FALSE;
7894 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7895 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7897 tape.no_info_chunk = TRUE;
7898 tape.no_valid_file = FALSE;
7901 static int getTapePosSize(struct TapeInfo *tape)
7903 int tape_pos_size = 0;
7905 if (tape->use_key_actions)
7906 tape_pos_size += tape->num_participating_players;
7908 if (tape->use_mouse_actions)
7909 tape_pos_size += 3; // x and y position and mouse button mask
7911 tape_pos_size += 1; // tape action delay value
7913 return tape_pos_size;
7916 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7918 tape->use_key_actions = FALSE;
7919 tape->use_mouse_actions = FALSE;
7921 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7922 tape->use_key_actions = TRUE;
7924 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7925 tape->use_mouse_actions = TRUE;
7928 static int getTapeActionValue(struct TapeInfo *tape)
7930 return (tape->use_key_actions &&
7931 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7932 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7933 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7934 TAPE_ACTIONS_DEFAULT);
7937 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7939 tape->file_version = getFileVersion(file);
7940 tape->game_version = getFileVersion(file);
7945 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7949 tape->random_seed = getFile32BitBE(file);
7950 tape->date = getFile32BitBE(file);
7951 tape->length = getFile32BitBE(file);
7953 // read header fields that are new since version 1.2
7954 if (tape->file_version >= FILE_VERSION_1_2)
7956 byte store_participating_players = getFile8Bit(file);
7959 // since version 1.2, tapes store which players participate in the tape
7960 tape->num_participating_players = 0;
7961 for (i = 0; i < MAX_PLAYERS; i++)
7963 tape->player_participates[i] = FALSE;
7965 if (store_participating_players & (1 << i))
7967 tape->player_participates[i] = TRUE;
7968 tape->num_participating_players++;
7972 setTapeActionFlags(tape, getFile8Bit(file));
7974 tape->property_bits = getFile8Bit(file);
7975 tape->solved = getFile8Bit(file);
7977 engine_version = getFileVersion(file);
7978 if (engine_version > 0)
7979 tape->engine_version = engine_version;
7981 tape->engine_version = tape->game_version;
7987 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7989 tape->scr_fieldx = getFile8Bit(file);
7990 tape->scr_fieldy = getFile8Bit(file);
7995 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7997 char *level_identifier = NULL;
7998 int level_identifier_size;
8001 tape->no_info_chunk = FALSE;
8003 level_identifier_size = getFile16BitBE(file);
8005 level_identifier = checked_malloc(level_identifier_size);
8007 for (i = 0; i < level_identifier_size; i++)
8008 level_identifier[i] = getFile8Bit(file);
8010 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8011 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8013 checked_free(level_identifier);
8015 tape->level_nr = getFile16BitBE(file);
8017 chunk_size = 2 + level_identifier_size + 2;
8022 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8025 int tape_pos_size = getTapePosSize(tape);
8026 int chunk_size_expected = tape_pos_size * tape->length;
8028 if (chunk_size_expected != chunk_size)
8030 ReadUnusedBytesFromFile(file, chunk_size);
8031 return chunk_size_expected;
8034 for (i = 0; i < tape->length; i++)
8036 if (i >= MAX_TAPE_LEN)
8038 Warn("tape truncated -- size exceeds maximum tape size %d",
8041 // tape too large; read and ignore remaining tape data from this chunk
8042 for (;i < tape->length; i++)
8043 ReadUnusedBytesFromFile(file, tape_pos_size);
8048 if (tape->use_key_actions)
8050 for (j = 0; j < MAX_PLAYERS; j++)
8052 tape->pos[i].action[j] = MV_NONE;
8054 if (tape->player_participates[j])
8055 tape->pos[i].action[j] = getFile8Bit(file);
8059 if (tape->use_mouse_actions)
8061 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8062 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8063 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8066 tape->pos[i].delay = getFile8Bit(file);
8068 if (tape->file_version == FILE_VERSION_1_0)
8070 // eliminate possible diagonal moves in old tapes
8071 // this is only for backward compatibility
8073 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8074 byte action = tape->pos[i].action[0];
8075 int k, num_moves = 0;
8077 for (k = 0; k<4; k++)
8079 if (action & joy_dir[k])
8081 tape->pos[i + num_moves].action[0] = joy_dir[k];
8083 tape->pos[i + num_moves].delay = 0;
8092 tape->length += num_moves;
8095 else if (tape->file_version < FILE_VERSION_2_0)
8097 // convert pre-2.0 tapes to new tape format
8099 if (tape->pos[i].delay > 1)
8102 tape->pos[i + 1] = tape->pos[i];
8103 tape->pos[i + 1].delay = 1;
8106 for (j = 0; j < MAX_PLAYERS; j++)
8107 tape->pos[i].action[j] = MV_NONE;
8108 tape->pos[i].delay--;
8115 if (checkEndOfFile(file))
8119 if (i != tape->length)
8120 chunk_size = tape_pos_size * i;
8125 static void LoadTape_SokobanSolution(char *filename)
8128 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8130 if (!(file = openFile(filename, MODE_READ)))
8132 tape.no_valid_file = TRUE;
8137 while (!checkEndOfFile(file))
8139 unsigned char c = getByteFromFile(file);
8141 if (checkEndOfFile(file))
8148 tape.pos[tape.length].action[0] = MV_UP;
8149 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8155 tape.pos[tape.length].action[0] = MV_DOWN;
8156 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8162 tape.pos[tape.length].action[0] = MV_LEFT;
8163 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8169 tape.pos[tape.length].action[0] = MV_RIGHT;
8170 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8178 // ignore white-space characters
8182 tape.no_valid_file = TRUE;
8184 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8192 if (tape.no_valid_file)
8195 tape.length_frames = GetTapeLengthFrames();
8196 tape.length_seconds = GetTapeLengthSeconds();
8199 void LoadTapeFromFilename(char *filename)
8201 char cookie[MAX_LINE_LEN];
8202 char chunk_name[CHUNK_ID_LEN + 1];
8206 // always start with reliable default values
8207 setTapeInfoToDefaults();
8209 if (strSuffix(filename, ".sln"))
8211 LoadTape_SokobanSolution(filename);
8216 if (!(file = openFile(filename, MODE_READ)))
8218 tape.no_valid_file = TRUE;
8223 getFileChunkBE(file, chunk_name, NULL);
8224 if (strEqual(chunk_name, "RND1"))
8226 getFile32BitBE(file); // not used
8228 getFileChunkBE(file, chunk_name, NULL);
8229 if (!strEqual(chunk_name, "TAPE"))
8231 tape.no_valid_file = TRUE;
8233 Warn("unknown format of tape file '%s'", filename);
8240 else // check for pre-2.0 file format with cookie string
8242 strcpy(cookie, chunk_name);
8243 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8245 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8246 cookie[strlen(cookie) - 1] = '\0';
8248 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8250 tape.no_valid_file = TRUE;
8252 Warn("unknown format of tape file '%s'", filename);
8259 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8261 tape.no_valid_file = TRUE;
8263 Warn("unsupported version of tape file '%s'", filename);
8270 // pre-2.0 tape files have no game version, so use file version here
8271 tape.game_version = tape.file_version;
8274 if (tape.file_version < FILE_VERSION_1_2)
8276 // tape files from versions before 1.2.0 without chunk structure
8277 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8278 LoadTape_BODY(file, 2 * tape.length, &tape);
8286 int (*loader)(File *, int, struct TapeInfo *);
8290 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8291 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8292 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8293 { "INFO", -1, LoadTape_INFO },
8294 { "BODY", -1, LoadTape_BODY },
8298 while (getFileChunkBE(file, chunk_name, &chunk_size))
8302 while (chunk_info[i].name != NULL &&
8303 !strEqual(chunk_name, chunk_info[i].name))
8306 if (chunk_info[i].name == NULL)
8308 Warn("unknown chunk '%s' in tape file '%s'",
8309 chunk_name, filename);
8311 ReadUnusedBytesFromFile(file, chunk_size);
8313 else if (chunk_info[i].size != -1 &&
8314 chunk_info[i].size != chunk_size)
8316 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8317 chunk_size, chunk_name, filename);
8319 ReadUnusedBytesFromFile(file, chunk_size);
8323 // call function to load this tape chunk
8324 int chunk_size_expected =
8325 (chunk_info[i].loader)(file, chunk_size, &tape);
8327 // the size of some chunks cannot be checked before reading other
8328 // chunks first (like "HEAD" and "BODY") that contain some header
8329 // information, so check them here
8330 if (chunk_size_expected != chunk_size)
8332 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8333 chunk_size, chunk_name, filename);
8341 tape.length_frames = GetTapeLengthFrames();
8342 tape.length_seconds = GetTapeLengthSeconds();
8345 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8347 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8349 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8350 tape.engine_version);
8354 void LoadTape(int nr)
8356 char *filename = getTapeFilename(nr);
8358 LoadTapeFromFilename(filename);
8361 void LoadSolutionTape(int nr)
8363 char *filename = getSolutionTapeFilename(nr);
8365 LoadTapeFromFilename(filename);
8367 if (TAPE_IS_EMPTY(tape) &&
8368 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8369 level.native_sp_level->demo.is_available)
8370 CopyNativeTape_SP_to_RND(&level);
8373 void LoadScoreTape(char *score_tape_basename, int nr)
8375 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8377 LoadTapeFromFilename(filename);
8380 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8382 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8384 LoadTapeFromFilename(filename);
8387 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8389 // chunk required for team mode tapes with non-default screen size
8390 return (tape->num_participating_players > 1 &&
8391 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8392 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8395 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8397 putFileVersion(file, tape->file_version);
8398 putFileVersion(file, tape->game_version);
8401 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8404 byte store_participating_players = 0;
8406 // set bits for participating players for compact storage
8407 for (i = 0; i < MAX_PLAYERS; i++)
8408 if (tape->player_participates[i])
8409 store_participating_players |= (1 << i);
8411 putFile32BitBE(file, tape->random_seed);
8412 putFile32BitBE(file, tape->date);
8413 putFile32BitBE(file, tape->length);
8415 putFile8Bit(file, store_participating_players);
8417 putFile8Bit(file, getTapeActionValue(tape));
8419 putFile8Bit(file, tape->property_bits);
8420 putFile8Bit(file, tape->solved);
8422 putFileVersion(file, tape->engine_version);
8425 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8427 putFile8Bit(file, tape->scr_fieldx);
8428 putFile8Bit(file, tape->scr_fieldy);
8431 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8433 int level_identifier_size = strlen(tape->level_identifier) + 1;
8436 putFile16BitBE(file, level_identifier_size);
8438 for (i = 0; i < level_identifier_size; i++)
8439 putFile8Bit(file, tape->level_identifier[i]);
8441 putFile16BitBE(file, tape->level_nr);
8444 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8448 for (i = 0; i < tape->length; i++)
8450 if (tape->use_key_actions)
8452 for (j = 0; j < MAX_PLAYERS; j++)
8453 if (tape->player_participates[j])
8454 putFile8Bit(file, tape->pos[i].action[j]);
8457 if (tape->use_mouse_actions)
8459 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8460 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8461 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8464 putFile8Bit(file, tape->pos[i].delay);
8468 void SaveTapeToFilename(char *filename)
8472 int info_chunk_size;
8473 int body_chunk_size;
8475 if (!(file = fopen(filename, MODE_WRITE)))
8477 Warn("cannot save level recording file '%s'", filename);
8482 tape_pos_size = getTapePosSize(&tape);
8484 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8485 body_chunk_size = tape_pos_size * tape.length;
8487 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8488 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8490 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8491 SaveTape_VERS(file, &tape);
8493 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8494 SaveTape_HEAD(file, &tape);
8496 if (checkSaveTape_SCRN(&tape))
8498 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8499 SaveTape_SCRN(file, &tape);
8502 putFileChunkBE(file, "INFO", info_chunk_size);
8503 SaveTape_INFO(file, &tape);
8505 putFileChunkBE(file, "BODY", body_chunk_size);
8506 SaveTape_BODY(file, &tape);
8510 SetFilePermissions(filename, PERMS_PRIVATE);
8513 static void SaveTapeExt(char *filename)
8517 tape.file_version = FILE_VERSION_ACTUAL;
8518 tape.game_version = GAME_VERSION_ACTUAL;
8520 tape.num_participating_players = 0;
8522 // count number of participating players
8523 for (i = 0; i < MAX_PLAYERS; i++)
8524 if (tape.player_participates[i])
8525 tape.num_participating_players++;
8527 SaveTapeToFilename(filename);
8529 tape.changed = FALSE;
8532 void SaveTape(int nr)
8534 char *filename = getTapeFilename(nr);
8536 InitTapeDirectory(leveldir_current->subdir);
8538 SaveTapeExt(filename);
8541 void SaveScoreTape(int nr)
8543 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8545 // used instead of "leveldir_current->subdir" (for network games)
8546 InitScoreTapeDirectory(levelset.identifier, nr);
8548 SaveTapeExt(filename);
8551 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8552 unsigned int req_state_added)
8554 char *filename = getTapeFilename(nr);
8555 boolean new_tape = !fileExists(filename);
8556 boolean tape_saved = FALSE;
8558 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8563 Request(msg_saved, REQ_CONFIRM | req_state_added);
8571 boolean SaveTapeChecked(int nr)
8573 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8576 boolean SaveTapeChecked_LevelSolved(int nr)
8578 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8579 "Level solved! Tape saved!", REQ_STAY_OPEN);
8582 void DumpTape(struct TapeInfo *tape)
8584 int tape_frame_counter;
8587 if (tape->no_valid_file)
8589 Warn("cannot dump -- no valid tape file found");
8596 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8597 tape->level_nr, tape->file_version, tape->game_version);
8598 Print(" (effective engine version %08d)\n",
8599 tape->engine_version);
8600 Print("Level series identifier: '%s'\n", tape->level_identifier);
8602 Print("Solution tape: %s\n",
8603 tape->solved ? "yes" :
8604 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8606 Print("Special tape properties: ");
8607 if (tape->property_bits == TAPE_PROPERTY_NONE)
8609 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8610 Print("[em_random_bug]");
8611 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8612 Print("[game_speed]");
8613 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8615 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8616 Print("[single_step]");
8617 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8618 Print("[snapshot]");
8619 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8620 Print("[replayed]");
8621 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8622 Print("[tas_keys]");
8623 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8624 Print("[small_graphics]");
8627 int year2 = tape->date / 10000;
8628 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8629 int month_index_raw = (tape->date / 100) % 100;
8630 int month_index = month_index_raw % 12; // prevent invalid index
8631 int month = month_index + 1;
8632 int day = tape->date % 100;
8634 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8638 tape_frame_counter = 0;
8640 for (i = 0; i < tape->length; i++)
8642 if (i >= MAX_TAPE_LEN)
8647 for (j = 0; j < MAX_PLAYERS; j++)
8649 if (tape->player_participates[j])
8651 int action = tape->pos[i].action[j];
8653 Print("%d:%02x ", j, action);
8654 Print("[%c%c%c%c|%c%c] - ",
8655 (action & JOY_LEFT ? '<' : ' '),
8656 (action & JOY_RIGHT ? '>' : ' '),
8657 (action & JOY_UP ? '^' : ' '),
8658 (action & JOY_DOWN ? 'v' : ' '),
8659 (action & JOY_BUTTON_1 ? '1' : ' '),
8660 (action & JOY_BUTTON_2 ? '2' : ' '));
8664 Print("(%03d) ", tape->pos[i].delay);
8665 Print("[%05d]\n", tape_frame_counter);
8667 tape_frame_counter += tape->pos[i].delay;
8673 void DumpTapes(void)
8675 static LevelDirTree *dumptape_leveldir = NULL;
8677 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8678 global.dumptape_leveldir);
8680 if (dumptape_leveldir == NULL)
8681 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8683 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8684 global.dumptape_level_nr > dumptape_leveldir->last_level)
8685 Fail("no such level number: %d", global.dumptape_level_nr);
8687 leveldir_current = dumptape_leveldir;
8689 if (options.mytapes)
8690 LoadTape(global.dumptape_level_nr);
8692 LoadSolutionTape(global.dumptape_level_nr);
8700 // ============================================================================
8701 // score file functions
8702 // ============================================================================
8704 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8708 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8710 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8711 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8712 scores->entry[i].score = 0;
8713 scores->entry[i].time = 0;
8715 scores->entry[i].id = -1;
8716 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8717 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8718 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8719 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8720 strcpy(scores->entry[i].country_code, "??");
8723 scores->num_entries = 0;
8724 scores->last_added = -1;
8725 scores->last_added_local = -1;
8727 scores->updated = FALSE;
8728 scores->uploaded = FALSE;
8729 scores->tape_downloaded = FALSE;
8730 scores->force_last_added = FALSE;
8732 // The following values are intentionally not reset here:
8736 // - continue_playing
8737 // - continue_on_return
8740 static void setScoreInfoToDefaults(void)
8742 setScoreInfoToDefaultsExt(&scores);
8745 static void setServerScoreInfoToDefaults(void)
8747 setScoreInfoToDefaultsExt(&server_scores);
8750 static void LoadScore_OLD(int nr)
8753 char *filename = getScoreFilename(nr);
8754 char cookie[MAX_LINE_LEN];
8755 char line[MAX_LINE_LEN];
8759 if (!(file = fopen(filename, MODE_READ)))
8762 // check file identifier
8763 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8765 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8766 cookie[strlen(cookie) - 1] = '\0';
8768 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8770 Warn("unknown format of score file '%s'", filename);
8777 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8779 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8780 Warn("fscanf() failed; %s", strerror(errno));
8782 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8785 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8786 line[strlen(line) - 1] = '\0';
8788 for (line_ptr = line; *line_ptr; line_ptr++)
8790 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8792 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8793 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8802 static void ConvertScore_OLD(void)
8804 // only convert score to time for levels that rate playing time over score
8805 if (!level.rate_time_over_score)
8808 // convert old score to playing time for score-less levels (like Supaplex)
8809 int time_final_max = 999;
8812 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8814 int score = scores.entry[i].score;
8816 if (score > 0 && score < time_final_max)
8817 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8821 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8823 scores->file_version = getFileVersion(file);
8824 scores->game_version = getFileVersion(file);
8829 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8831 char *level_identifier = NULL;
8832 int level_identifier_size;
8835 level_identifier_size = getFile16BitBE(file);
8837 level_identifier = checked_malloc(level_identifier_size);
8839 for (i = 0; i < level_identifier_size; i++)
8840 level_identifier[i] = getFile8Bit(file);
8842 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8843 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8845 checked_free(level_identifier);
8847 scores->level_nr = getFile16BitBE(file);
8848 scores->num_entries = getFile16BitBE(file);
8850 chunk_size = 2 + level_identifier_size + 2 + 2;
8855 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8859 for (i = 0; i < scores->num_entries; i++)
8861 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8862 scores->entry[i].name[j] = getFile8Bit(file);
8864 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8867 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8872 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8876 for (i = 0; i < scores->num_entries; i++)
8877 scores->entry[i].score = getFile16BitBE(file);
8879 chunk_size = scores->num_entries * 2;
8884 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8888 for (i = 0; i < scores->num_entries; i++)
8889 scores->entry[i].score = getFile32BitBE(file);
8891 chunk_size = scores->num_entries * 4;
8896 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8900 for (i = 0; i < scores->num_entries; i++)
8901 scores->entry[i].time = getFile32BitBE(file);
8903 chunk_size = scores->num_entries * 4;
8908 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8912 for (i = 0; i < scores->num_entries; i++)
8914 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8915 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8917 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8920 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8925 void LoadScore(int nr)
8927 char *filename = getScoreFilename(nr);
8928 char cookie[MAX_LINE_LEN];
8929 char chunk_name[CHUNK_ID_LEN + 1];
8931 boolean old_score_file_format = FALSE;
8934 // always start with reliable default values
8935 setScoreInfoToDefaults();
8937 if (!(file = openFile(filename, MODE_READ)))
8940 getFileChunkBE(file, chunk_name, NULL);
8941 if (strEqual(chunk_name, "RND1"))
8943 getFile32BitBE(file); // not used
8945 getFileChunkBE(file, chunk_name, NULL);
8946 if (!strEqual(chunk_name, "SCOR"))
8948 Warn("unknown format of score file '%s'", filename);
8955 else // check for old file format with cookie string
8957 strcpy(cookie, chunk_name);
8958 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8960 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8961 cookie[strlen(cookie) - 1] = '\0';
8963 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8965 Warn("unknown format of score file '%s'", filename);
8972 old_score_file_format = TRUE;
8975 if (old_score_file_format)
8977 // score files from versions before 4.2.4.0 without chunk structure
8980 // convert score to time, if possible (mainly for Supaplex levels)
8989 int (*loader)(File *, int, struct ScoreInfo *);
8993 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8994 { "INFO", -1, LoadScore_INFO },
8995 { "NAME", -1, LoadScore_NAME },
8996 { "SCOR", -1, LoadScore_SCOR },
8997 { "SC4R", -1, LoadScore_SC4R },
8998 { "TIME", -1, LoadScore_TIME },
8999 { "TAPE", -1, LoadScore_TAPE },
9004 while (getFileChunkBE(file, chunk_name, &chunk_size))
9008 while (chunk_info[i].name != NULL &&
9009 !strEqual(chunk_name, chunk_info[i].name))
9012 if (chunk_info[i].name == NULL)
9014 Warn("unknown chunk '%s' in score file '%s'",
9015 chunk_name, filename);
9017 ReadUnusedBytesFromFile(file, chunk_size);
9019 else if (chunk_info[i].size != -1 &&
9020 chunk_info[i].size != chunk_size)
9022 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9023 chunk_size, chunk_name, filename);
9025 ReadUnusedBytesFromFile(file, chunk_size);
9029 // call function to load this score chunk
9030 int chunk_size_expected =
9031 (chunk_info[i].loader)(file, chunk_size, &scores);
9033 // the size of some chunks cannot be checked before reading other
9034 // chunks first (like "HEAD" and "BODY") that contain some header
9035 // information, so check them here
9036 if (chunk_size_expected != chunk_size)
9038 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9039 chunk_size, chunk_name, filename);
9048 #if ENABLE_HISTORIC_CHUNKS
9049 void SaveScore_OLD(int nr)
9052 char *filename = getScoreFilename(nr);
9055 // used instead of "leveldir_current->subdir" (for network games)
9056 InitScoreDirectory(levelset.identifier);
9058 if (!(file = fopen(filename, MODE_WRITE)))
9060 Warn("cannot save score for level %d", nr);
9065 fprintf(file, "%s\n\n", SCORE_COOKIE);
9067 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9068 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9072 SetFilePermissions(filename, PERMS_PRIVATE);
9076 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9078 putFileVersion(file, scores->file_version);
9079 putFileVersion(file, scores->game_version);
9082 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9084 int level_identifier_size = strlen(scores->level_identifier) + 1;
9087 putFile16BitBE(file, level_identifier_size);
9089 for (i = 0; i < level_identifier_size; i++)
9090 putFile8Bit(file, scores->level_identifier[i]);
9092 putFile16BitBE(file, scores->level_nr);
9093 putFile16BitBE(file, scores->num_entries);
9096 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9100 for (i = 0; i < scores->num_entries; i++)
9102 int name_size = strlen(scores->entry[i].name);
9104 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9105 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9109 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9113 for (i = 0; i < scores->num_entries; i++)
9114 putFile16BitBE(file, scores->entry[i].score);
9117 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9121 for (i = 0; i < scores->num_entries; i++)
9122 putFile32BitBE(file, scores->entry[i].score);
9125 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9129 for (i = 0; i < scores->num_entries; i++)
9130 putFile32BitBE(file, scores->entry[i].time);
9133 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9137 for (i = 0; i < scores->num_entries; i++)
9139 int size = strlen(scores->entry[i].tape_basename);
9141 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9142 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9146 static void SaveScoreToFilename(char *filename)
9149 int info_chunk_size;
9150 int name_chunk_size;
9151 int scor_chunk_size;
9152 int sc4r_chunk_size;
9153 int time_chunk_size;
9154 int tape_chunk_size;
9155 boolean has_large_score_values;
9158 if (!(file = fopen(filename, MODE_WRITE)))
9160 Warn("cannot save score file '%s'", filename);
9165 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9166 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9167 scor_chunk_size = scores.num_entries * 2;
9168 sc4r_chunk_size = scores.num_entries * 4;
9169 time_chunk_size = scores.num_entries * 4;
9170 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9172 has_large_score_values = FALSE;
9173 for (i = 0; i < scores.num_entries; i++)
9174 if (scores.entry[i].score > 0xffff)
9175 has_large_score_values = TRUE;
9177 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9178 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9180 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9181 SaveScore_VERS(file, &scores);
9183 putFileChunkBE(file, "INFO", info_chunk_size);
9184 SaveScore_INFO(file, &scores);
9186 putFileChunkBE(file, "NAME", name_chunk_size);
9187 SaveScore_NAME(file, &scores);
9189 if (has_large_score_values)
9191 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9192 SaveScore_SC4R(file, &scores);
9196 putFileChunkBE(file, "SCOR", scor_chunk_size);
9197 SaveScore_SCOR(file, &scores);
9200 putFileChunkBE(file, "TIME", time_chunk_size);
9201 SaveScore_TIME(file, &scores);
9203 putFileChunkBE(file, "TAPE", tape_chunk_size);
9204 SaveScore_TAPE(file, &scores);
9208 SetFilePermissions(filename, PERMS_PRIVATE);
9211 void SaveScore(int nr)
9213 char *filename = getScoreFilename(nr);
9216 // used instead of "leveldir_current->subdir" (for network games)
9217 InitScoreDirectory(levelset.identifier);
9219 scores.file_version = FILE_VERSION_ACTUAL;
9220 scores.game_version = GAME_VERSION_ACTUAL;
9222 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9223 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9224 scores.level_nr = level_nr;
9226 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9227 if (scores.entry[i].score == 0 &&
9228 scores.entry[i].time == 0 &&
9229 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9232 scores.num_entries = i;
9234 if (scores.num_entries == 0)
9237 SaveScoreToFilename(filename);
9240 static void LoadServerScoreFromCache(int nr)
9242 struct ScoreEntry score_entry;
9251 { &score_entry.score, FALSE, 0 },
9252 { &score_entry.time, FALSE, 0 },
9253 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9254 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9255 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9256 { &score_entry.id, FALSE, 0 },
9257 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9258 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9259 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9260 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9264 char *filename = getScoreCacheFilename(nr);
9265 SetupFileHash *score_hash = loadSetupFileHash(filename);
9268 server_scores.num_entries = 0;
9270 if (score_hash == NULL)
9273 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9275 score_entry = server_scores.entry[i];
9277 for (j = 0; score_mapping[j].value != NULL; j++)
9281 sprintf(token, "%02d.%d", i, j);
9283 char *value = getHashEntry(score_hash, token);
9288 if (score_mapping[j].is_string)
9290 char *score_value = (char *)score_mapping[j].value;
9291 int value_size = score_mapping[j].string_size;
9293 strncpy(score_value, value, value_size);
9294 score_value[value_size] = '\0';
9298 int *score_value = (int *)score_mapping[j].value;
9300 *score_value = atoi(value);
9303 server_scores.num_entries = i + 1;
9306 server_scores.entry[i] = score_entry;
9309 freeSetupFileHash(score_hash);
9312 void LoadServerScore(int nr, boolean download_score)
9314 if (!setup.use_api_server)
9317 // always start with reliable default values
9318 setServerScoreInfoToDefaults();
9320 // 1st step: load server scores from cache file (which may not exist)
9321 // (this should prevent reading it while the thread is writing to it)
9322 LoadServerScoreFromCache(nr);
9324 if (download_score && runtime.use_api_server)
9326 // 2nd step: download server scores from score server to cache file
9327 // (as thread, as it might time out if the server is not reachable)
9328 ApiGetScoreAsThread(nr);
9332 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9334 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9336 // if score tape not uploaded, ask for uploading missing tapes later
9337 if (!setup.has_remaining_tapes)
9338 setup.ask_for_remaining_tapes = TRUE;
9340 setup.provide_uploading_tapes = TRUE;
9341 setup.has_remaining_tapes = TRUE;
9343 SaveSetup_ServerSetup();
9346 void SaveServerScore(int nr, boolean tape_saved)
9348 if (!runtime.use_api_server)
9350 PrepareScoreTapesForUpload(leveldir_current->subdir);
9355 ApiAddScoreAsThread(nr, tape_saved, NULL);
9358 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9359 char *score_tape_filename)
9361 if (!runtime.use_api_server)
9364 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9367 void LoadLocalAndServerScore(int nr, boolean download_score)
9369 int last_added_local = scores.last_added_local;
9370 boolean force_last_added = scores.force_last_added;
9372 // needed if only showing server scores
9373 setScoreInfoToDefaults();
9375 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9378 // restore last added local score entry (before merging server scores)
9379 scores.last_added = scores.last_added_local = last_added_local;
9381 if (setup.use_api_server &&
9382 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9384 // load server scores from cache file and trigger update from server
9385 LoadServerScore(nr, download_score);
9387 // merge local scores with scores from server
9391 if (force_last_added)
9392 scores.force_last_added = force_last_added;
9396 // ============================================================================
9397 // setup file functions
9398 // ============================================================================
9400 #define TOKEN_STR_PLAYER_PREFIX "player_"
9403 static struct TokenInfo global_setup_tokens[] =
9407 &setup.player_name, "player_name"
9411 &setup.multiple_users, "multiple_users"
9415 &setup.sound, "sound"
9419 &setup.sound_loops, "repeating_sound_loops"
9423 &setup.sound_music, "background_music"
9427 &setup.sound_simple, "simple_sound_effects"
9431 &setup.toons, "toons"
9435 &setup.scroll_delay, "scroll_delay"
9439 &setup.forced_scroll_delay, "forced_scroll_delay"
9443 &setup.scroll_delay_value, "scroll_delay_value"
9447 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9451 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9455 &setup.fade_screens, "fade_screens"
9459 &setup.autorecord, "automatic_tape_recording"
9463 &setup.auto_pause_on_start, "auto_pause_on_start"
9467 &setup.show_titlescreen, "show_titlescreen"
9471 &setup.quick_doors, "quick_doors"
9475 &setup.team_mode, "team_mode"
9479 &setup.handicap, "handicap"
9483 &setup.skip_levels, "skip_levels"
9487 &setup.increment_levels, "increment_levels"
9491 &setup.auto_play_next_level, "auto_play_next_level"
9495 &setup.count_score_after_game, "count_score_after_game"
9499 &setup.show_scores_after_game, "show_scores_after_game"
9503 &setup.time_limit, "time_limit"
9507 &setup.fullscreen, "fullscreen"
9511 &setup.window_scaling_percent, "window_scaling_percent"
9515 &setup.window_scaling_quality, "window_scaling_quality"
9519 &setup.screen_rendering_mode, "screen_rendering_mode"
9523 &setup.vsync_mode, "vsync_mode"
9527 &setup.ask_on_escape, "ask_on_escape"
9531 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9535 &setup.ask_on_game_over, "ask_on_game_over"
9539 &setup.ask_on_quit_game, "ask_on_quit_game"
9543 &setup.ask_on_quit_program, "ask_on_quit_program"
9547 &setup.quick_switch, "quick_player_switch"
9551 &setup.input_on_focus, "input_on_focus"
9555 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9559 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9563 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9567 &setup.game_speed_extended, "game_speed_extended"
9571 &setup.game_frame_delay, "game_frame_delay"
9575 &setup.sp_show_border_elements, "sp_show_border_elements"
9579 &setup.small_game_graphics, "small_game_graphics"
9583 &setup.show_load_save_buttons, "show_load_save_buttons"
9587 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9591 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9595 &setup.graphics_set, "graphics_set"
9599 &setup.sounds_set, "sounds_set"
9603 &setup.music_set, "music_set"
9607 &setup.override_level_graphics, "override_level_graphics"
9611 &setup.override_level_sounds, "override_level_sounds"
9615 &setup.override_level_music, "override_level_music"
9619 &setup.volume_simple, "volume_simple"
9623 &setup.volume_loops, "volume_loops"
9627 &setup.volume_music, "volume_music"
9631 &setup.network_mode, "network_mode"
9635 &setup.network_player_nr, "network_player"
9639 &setup.network_server_hostname, "network_server_hostname"
9643 &setup.touch.control_type, "touch.control_type"
9647 &setup.touch.move_distance, "touch.move_distance"
9651 &setup.touch.drop_distance, "touch.drop_distance"
9655 &setup.touch.transparency, "touch.transparency"
9659 &setup.touch.draw_outlined, "touch.draw_outlined"
9663 &setup.touch.draw_pressed, "touch.draw_pressed"
9667 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9671 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9675 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9679 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9683 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9687 static struct TokenInfo auto_setup_tokens[] =
9691 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9695 static struct TokenInfo server_setup_tokens[] =
9699 &setup.player_uuid, "player_uuid"
9703 &setup.player_version, "player_version"
9707 &setup.use_api_server, TEST_PREFIX "use_api_server"
9711 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9715 &setup.api_server_password, TEST_PREFIX "api_server_password"
9719 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9723 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9727 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9731 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9735 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9739 static struct TokenInfo editor_setup_tokens[] =
9743 &setup.editor.el_classic, "editor.el_classic"
9747 &setup.editor.el_custom, "editor.el_custom"
9751 &setup.editor.el_user_defined, "editor.el_user_defined"
9755 &setup.editor.el_dynamic, "editor.el_dynamic"
9759 &setup.editor.el_headlines, "editor.el_headlines"
9763 &setup.editor.show_element_token, "editor.show_element_token"
9767 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9771 static struct TokenInfo editor_cascade_setup_tokens[] =
9775 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9779 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9783 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9787 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9791 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9795 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9799 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9803 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9807 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9811 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9815 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9819 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9823 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9827 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9831 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9835 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9839 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9843 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9847 static struct TokenInfo shortcut_setup_tokens[] =
9851 &setup.shortcut.save_game, "shortcut.save_game"
9855 &setup.shortcut.load_game, "shortcut.load_game"
9859 &setup.shortcut.restart_game, "shortcut.restart_game"
9863 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9867 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9871 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9875 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9879 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9883 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9887 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9891 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9895 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9899 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9903 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9907 &setup.shortcut.tape_record, "shortcut.tape_record"
9911 &setup.shortcut.tape_play, "shortcut.tape_play"
9915 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9919 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9923 &setup.shortcut.sound_music, "shortcut.sound_music"
9927 &setup.shortcut.snap_left, "shortcut.snap_left"
9931 &setup.shortcut.snap_right, "shortcut.snap_right"
9935 &setup.shortcut.snap_up, "shortcut.snap_up"
9939 &setup.shortcut.snap_down, "shortcut.snap_down"
9943 static struct SetupInputInfo setup_input;
9944 static struct TokenInfo player_setup_tokens[] =
9948 &setup_input.use_joystick, ".use_joystick"
9952 &setup_input.joy.device_name, ".joy.device_name"
9956 &setup_input.joy.xleft, ".joy.xleft"
9960 &setup_input.joy.xmiddle, ".joy.xmiddle"
9964 &setup_input.joy.xright, ".joy.xright"
9968 &setup_input.joy.yupper, ".joy.yupper"
9972 &setup_input.joy.ymiddle, ".joy.ymiddle"
9976 &setup_input.joy.ylower, ".joy.ylower"
9980 &setup_input.joy.snap, ".joy.snap_field"
9984 &setup_input.joy.drop, ".joy.place_bomb"
9988 &setup_input.key.left, ".key.move_left"
9992 &setup_input.key.right, ".key.move_right"
9996 &setup_input.key.up, ".key.move_up"
10000 &setup_input.key.down, ".key.move_down"
10004 &setup_input.key.snap, ".key.snap_field"
10008 &setup_input.key.drop, ".key.place_bomb"
10012 static struct TokenInfo system_setup_tokens[] =
10016 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10020 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10024 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10028 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10032 static struct TokenInfo internal_setup_tokens[] =
10036 &setup.internal.program_title, "program_title"
10040 &setup.internal.program_version, "program_version"
10044 &setup.internal.program_author, "program_author"
10048 &setup.internal.program_email, "program_email"
10052 &setup.internal.program_website, "program_website"
10056 &setup.internal.program_copyright, "program_copyright"
10060 &setup.internal.program_company, "program_company"
10064 &setup.internal.program_icon_file, "program_icon_file"
10068 &setup.internal.default_graphics_set, "default_graphics_set"
10072 &setup.internal.default_sounds_set, "default_sounds_set"
10076 &setup.internal.default_music_set, "default_music_set"
10080 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10084 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10088 &setup.internal.fallback_music_file, "fallback_music_file"
10092 &setup.internal.default_level_series, "default_level_series"
10096 &setup.internal.default_window_width, "default_window_width"
10100 &setup.internal.default_window_height, "default_window_height"
10104 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10108 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10112 &setup.internal.create_user_levelset, "create_user_levelset"
10116 &setup.internal.menu_game, "menu_game"
10120 &setup.internal.menu_engines, "menu_engines"
10124 &setup.internal.menu_editor, "menu_editor"
10128 &setup.internal.menu_graphics, "menu_graphics"
10132 &setup.internal.menu_sound, "menu_sound"
10136 &setup.internal.menu_artwork, "menu_artwork"
10140 &setup.internal.menu_input, "menu_input"
10144 &setup.internal.menu_touch, "menu_touch"
10148 &setup.internal.menu_shortcuts, "menu_shortcuts"
10152 &setup.internal.menu_exit, "menu_exit"
10156 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10160 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10164 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10168 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10172 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10176 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10180 &setup.internal.info_title, "info_title"
10184 &setup.internal.info_elements, "info_elements"
10188 &setup.internal.info_music, "info_music"
10192 &setup.internal.info_credits, "info_credits"
10196 &setup.internal.info_program, "info_program"
10200 &setup.internal.info_version, "info_version"
10204 &setup.internal.info_levelset, "info_levelset"
10208 &setup.internal.info_exit, "info_exit"
10212 static struct TokenInfo debug_setup_tokens[] =
10216 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10220 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10224 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10228 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10232 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10236 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10240 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10244 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10248 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10252 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10256 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10260 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10264 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10268 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10272 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10276 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10280 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10284 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10288 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10292 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10296 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10299 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10303 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10307 &setup.debug.xsn_mode, "debug.xsn_mode"
10311 &setup.debug.xsn_percent, "debug.xsn_percent"
10315 static struct TokenInfo options_setup_tokens[] =
10319 &setup.options.verbose, "options.verbose"
10323 static void setSetupInfoToDefaults(struct SetupInfo *si)
10327 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10329 si->multiple_users = TRUE;
10332 si->sound_loops = TRUE;
10333 si->sound_music = TRUE;
10334 si->sound_simple = TRUE;
10336 si->scroll_delay = TRUE;
10337 si->forced_scroll_delay = FALSE;
10338 si->scroll_delay_value = STD_SCROLL_DELAY;
10339 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10340 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10341 si->fade_screens = TRUE;
10342 si->autorecord = TRUE;
10343 si->auto_pause_on_start = FALSE;
10344 si->show_titlescreen = TRUE;
10345 si->quick_doors = FALSE;
10346 si->team_mode = FALSE;
10347 si->handicap = TRUE;
10348 si->skip_levels = TRUE;
10349 si->increment_levels = TRUE;
10350 si->auto_play_next_level = TRUE;
10351 si->count_score_after_game = TRUE;
10352 si->show_scores_after_game = TRUE;
10353 si->time_limit = TRUE;
10354 si->fullscreen = FALSE;
10355 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10356 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10357 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10358 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10359 si->ask_on_escape = TRUE;
10360 si->ask_on_escape_editor = TRUE;
10361 si->ask_on_game_over = TRUE;
10362 si->ask_on_quit_game = TRUE;
10363 si->ask_on_quit_program = TRUE;
10364 si->quick_switch = FALSE;
10365 si->input_on_focus = FALSE;
10366 si->prefer_aga_graphics = TRUE;
10367 si->prefer_lowpass_sounds = FALSE;
10368 si->prefer_extra_panel_items = TRUE;
10369 si->game_speed_extended = FALSE;
10370 si->game_frame_delay = GAME_FRAME_DELAY;
10371 si->sp_show_border_elements = FALSE;
10372 si->small_game_graphics = FALSE;
10373 si->show_load_save_buttons = FALSE;
10374 si->show_undo_redo_buttons = FALSE;
10375 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10377 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10378 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10379 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10381 si->override_level_graphics = FALSE;
10382 si->override_level_sounds = FALSE;
10383 si->override_level_music = FALSE;
10385 si->volume_simple = 100; // percent
10386 si->volume_loops = 100; // percent
10387 si->volume_music = 100; // percent
10389 si->network_mode = FALSE;
10390 si->network_player_nr = 0; // first player
10391 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10393 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10394 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10395 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10396 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10397 si->touch.draw_outlined = TRUE;
10398 si->touch.draw_pressed = TRUE;
10400 for (i = 0; i < 2; i++)
10402 char *default_grid_button[6][2] =
10408 { "111222", " vv " },
10409 { "111222", " vv " }
10411 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10412 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10413 int min_xsize = MIN(6, grid_xsize);
10414 int min_ysize = MIN(6, grid_ysize);
10415 int startx = grid_xsize - min_xsize;
10416 int starty = grid_ysize - min_ysize;
10419 // virtual buttons grid can only be set to defaults if video is initialized
10420 // (this will be repeated if virtual buttons are not loaded from setup file)
10421 if (video.initialized)
10423 si->touch.grid_xsize[i] = grid_xsize;
10424 si->touch.grid_ysize[i] = grid_ysize;
10428 si->touch.grid_xsize[i] = -1;
10429 si->touch.grid_ysize[i] = -1;
10432 for (x = 0; x < MAX_GRID_XSIZE; x++)
10433 for (y = 0; y < MAX_GRID_YSIZE; y++)
10434 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10436 for (x = 0; x < min_xsize; x++)
10437 for (y = 0; y < min_ysize; y++)
10438 si->touch.grid_button[i][x][starty + y] =
10439 default_grid_button[y][0][x];
10441 for (x = 0; x < min_xsize; x++)
10442 for (y = 0; y < min_ysize; y++)
10443 si->touch.grid_button[i][startx + x][starty + y] =
10444 default_grid_button[y][1][x];
10447 si->touch.grid_initialized = video.initialized;
10449 si->touch.overlay_buttons = FALSE;
10451 si->editor.el_boulderdash = TRUE;
10452 si->editor.el_emerald_mine = TRUE;
10453 si->editor.el_emerald_mine_club = TRUE;
10454 si->editor.el_more = TRUE;
10455 si->editor.el_sokoban = TRUE;
10456 si->editor.el_supaplex = TRUE;
10457 si->editor.el_diamond_caves = TRUE;
10458 si->editor.el_dx_boulderdash = TRUE;
10460 si->editor.el_mirror_magic = TRUE;
10461 si->editor.el_deflektor = TRUE;
10463 si->editor.el_chars = TRUE;
10464 si->editor.el_steel_chars = TRUE;
10466 si->editor.el_classic = TRUE;
10467 si->editor.el_custom = TRUE;
10469 si->editor.el_user_defined = FALSE;
10470 si->editor.el_dynamic = TRUE;
10472 si->editor.el_headlines = TRUE;
10474 si->editor.show_element_token = FALSE;
10476 si->editor.show_read_only_warning = TRUE;
10478 si->editor.use_template_for_new_levels = TRUE;
10480 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10481 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10482 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10483 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10484 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10486 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10487 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10488 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10489 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10490 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10492 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10493 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10494 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10495 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10496 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10497 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10499 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10500 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10501 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10503 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10504 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10505 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10506 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10508 for (i = 0; i < MAX_PLAYERS; i++)
10510 si->input[i].use_joystick = FALSE;
10511 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10512 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10513 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10514 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10515 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10516 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10517 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10518 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10519 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10520 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10521 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10522 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10523 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10524 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10525 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10528 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10529 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10530 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10531 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10533 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10534 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10535 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10536 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10537 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10538 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10539 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10541 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10543 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10544 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10545 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10547 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10548 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10549 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10551 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10552 si->internal.choose_from_top_leveldir = FALSE;
10553 si->internal.show_scaling_in_title = TRUE;
10554 si->internal.create_user_levelset = TRUE;
10556 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10557 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10559 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10560 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10561 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10562 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10563 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10564 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10565 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10566 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10567 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10568 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10570 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10571 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10572 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10573 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10574 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10575 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10576 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10577 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10578 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10579 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10581 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10582 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10584 si->debug.show_frames_per_second = FALSE;
10586 si->debug.xsn_mode = AUTO;
10587 si->debug.xsn_percent = 0;
10589 si->options.verbose = FALSE;
10591 #if defined(PLATFORM_ANDROID)
10592 si->fullscreen = TRUE;
10593 si->touch.overlay_buttons = TRUE;
10596 setHideSetupEntry(&setup.debug.xsn_mode);
10599 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10601 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10604 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10606 si->player_uuid = NULL; // (will be set later)
10607 si->player_version = 1; // (will be set later)
10609 si->use_api_server = TRUE;
10610 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10611 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10612 si->ask_for_uploading_tapes = TRUE;
10613 si->ask_for_remaining_tapes = FALSE;
10614 si->provide_uploading_tapes = TRUE;
10615 si->ask_for_using_api_server = TRUE;
10616 si->has_remaining_tapes = FALSE;
10619 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10621 si->editor_cascade.el_bd = TRUE;
10622 si->editor_cascade.el_em = TRUE;
10623 si->editor_cascade.el_emc = TRUE;
10624 si->editor_cascade.el_rnd = TRUE;
10625 si->editor_cascade.el_sb = TRUE;
10626 si->editor_cascade.el_sp = TRUE;
10627 si->editor_cascade.el_dc = TRUE;
10628 si->editor_cascade.el_dx = TRUE;
10630 si->editor_cascade.el_mm = TRUE;
10631 si->editor_cascade.el_df = TRUE;
10633 si->editor_cascade.el_chars = FALSE;
10634 si->editor_cascade.el_steel_chars = FALSE;
10635 si->editor_cascade.el_ce = FALSE;
10636 si->editor_cascade.el_ge = FALSE;
10637 si->editor_cascade.el_es = FALSE;
10638 si->editor_cascade.el_ref = FALSE;
10639 si->editor_cascade.el_user = FALSE;
10640 si->editor_cascade.el_dynamic = FALSE;
10643 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10645 static char *getHideSetupToken(void *setup_value)
10647 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10649 if (setup_value != NULL)
10650 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10652 return hide_setup_token;
10655 void setHideSetupEntry(void *setup_value)
10657 char *hide_setup_token = getHideSetupToken(setup_value);
10659 if (hide_setup_hash == NULL)
10660 hide_setup_hash = newSetupFileHash();
10662 if (setup_value != NULL)
10663 setHashEntry(hide_setup_hash, hide_setup_token, "");
10666 void removeHideSetupEntry(void *setup_value)
10668 char *hide_setup_token = getHideSetupToken(setup_value);
10670 if (setup_value != NULL)
10671 removeHashEntry(hide_setup_hash, hide_setup_token);
10674 boolean hideSetupEntry(void *setup_value)
10676 char *hide_setup_token = getHideSetupToken(setup_value);
10678 return (setup_value != NULL &&
10679 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10682 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10683 struct TokenInfo *token_info,
10684 int token_nr, char *token_text)
10686 char *token_hide_text = getStringCat2(token_text, ".hide");
10687 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10689 // set the value of this setup option in the setup option structure
10690 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10692 // check if this setup option should be hidden in the setup menu
10693 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10694 setHideSetupEntry(token_info[token_nr].value);
10696 free(token_hide_text);
10699 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10700 struct TokenInfo *token_info,
10703 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10704 token_info[token_nr].text);
10707 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10711 if (!setup_file_hash)
10714 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10715 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10717 setup.touch.grid_initialized = TRUE;
10718 for (i = 0; i < 2; i++)
10720 int grid_xsize = setup.touch.grid_xsize[i];
10721 int grid_ysize = setup.touch.grid_ysize[i];
10724 // if virtual buttons are not loaded from setup file, repeat initializing
10725 // virtual buttons grid with default values later when video is initialized
10726 if (grid_xsize == -1 ||
10729 setup.touch.grid_initialized = FALSE;
10734 for (y = 0; y < grid_ysize; y++)
10736 char token_string[MAX_LINE_LEN];
10738 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10740 char *value_string = getHashEntry(setup_file_hash, token_string);
10742 if (value_string == NULL)
10745 for (x = 0; x < grid_xsize; x++)
10747 char c = value_string[x];
10749 setup.touch.grid_button[i][x][y] =
10750 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10755 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10756 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10758 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10759 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10761 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10765 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10767 setup_input = setup.input[pnr];
10768 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10770 char full_token[100];
10772 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10773 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10776 setup.input[pnr] = setup_input;
10779 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10780 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10782 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10783 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10785 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10786 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10788 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10789 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10791 setHideRelatedSetupEntries();
10794 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10798 if (!setup_file_hash)
10801 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10802 setSetupInfo(auto_setup_tokens, i,
10803 getHashEntry(setup_file_hash,
10804 auto_setup_tokens[i].text));
10807 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10811 if (!setup_file_hash)
10814 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10815 setSetupInfo(server_setup_tokens, i,
10816 getHashEntry(setup_file_hash,
10817 server_setup_tokens[i].text));
10820 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10824 if (!setup_file_hash)
10827 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10828 setSetupInfo(editor_cascade_setup_tokens, i,
10829 getHashEntry(setup_file_hash,
10830 editor_cascade_setup_tokens[i].text));
10833 void LoadUserNames(void)
10835 int last_user_nr = user.nr;
10838 if (global.user_names != NULL)
10840 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10841 checked_free(global.user_names[i]);
10843 checked_free(global.user_names);
10846 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10848 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10852 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10854 if (setup_file_hash)
10856 char *player_name = getHashEntry(setup_file_hash, "player_name");
10858 global.user_names[i] = getFixedUserName(player_name);
10860 freeSetupFileHash(setup_file_hash);
10863 if (global.user_names[i] == NULL)
10864 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10867 user.nr = last_user_nr;
10870 void LoadSetupFromFilename(char *filename)
10872 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10874 if (setup_file_hash)
10876 decodeSetupFileHash_Default(setup_file_hash);
10878 freeSetupFileHash(setup_file_hash);
10882 Debug("setup", "using default setup values");
10886 static void LoadSetup_SpecialPostProcessing(void)
10888 char *player_name_new;
10890 // needed to work around problems with fixed length strings
10891 player_name_new = getFixedUserName(setup.player_name);
10892 free(setup.player_name);
10893 setup.player_name = player_name_new;
10895 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10896 if (setup.scroll_delay == FALSE)
10898 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10899 setup.scroll_delay = TRUE; // now always "on"
10902 // make sure that scroll delay value stays inside valid range
10903 setup.scroll_delay_value =
10904 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10907 void LoadSetup_Default(void)
10911 // always start with reliable default values
10912 setSetupInfoToDefaults(&setup);
10914 // try to load setup values from default setup file
10915 filename = getDefaultSetupFilename();
10917 if (fileExists(filename))
10918 LoadSetupFromFilename(filename);
10920 // try to load setup values from platform setup file
10921 filename = getPlatformSetupFilename();
10923 if (fileExists(filename))
10924 LoadSetupFromFilename(filename);
10926 // try to load setup values from user setup file
10927 filename = getSetupFilename();
10929 LoadSetupFromFilename(filename);
10931 LoadSetup_SpecialPostProcessing();
10934 void LoadSetup_AutoSetup(void)
10936 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10937 SetupFileHash *setup_file_hash = NULL;
10939 // always start with reliable default values
10940 setSetupInfoToDefaults_AutoSetup(&setup);
10942 setup_file_hash = loadSetupFileHash(filename);
10944 if (setup_file_hash)
10946 decodeSetupFileHash_AutoSetup(setup_file_hash);
10948 freeSetupFileHash(setup_file_hash);
10954 void LoadSetup_ServerSetup(void)
10956 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10957 SetupFileHash *setup_file_hash = NULL;
10959 // always start with reliable default values
10960 setSetupInfoToDefaults_ServerSetup(&setup);
10962 setup_file_hash = loadSetupFileHash(filename);
10964 if (setup_file_hash)
10966 decodeSetupFileHash_ServerSetup(setup_file_hash);
10968 freeSetupFileHash(setup_file_hash);
10973 if (setup.player_uuid == NULL)
10975 // player UUID does not yet exist in setup file
10976 setup.player_uuid = getStringCopy(getUUID());
10977 setup.player_version = 2;
10979 SaveSetup_ServerSetup();
10983 void LoadSetup_EditorCascade(void)
10985 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10986 SetupFileHash *setup_file_hash = NULL;
10988 // always start with reliable default values
10989 setSetupInfoToDefaults_EditorCascade(&setup);
10991 setup_file_hash = loadSetupFileHash(filename);
10993 if (setup_file_hash)
10995 decodeSetupFileHash_EditorCascade(setup_file_hash);
10997 freeSetupFileHash(setup_file_hash);
11003 void LoadSetup(void)
11005 LoadSetup_Default();
11006 LoadSetup_AutoSetup();
11007 LoadSetup_ServerSetup();
11008 LoadSetup_EditorCascade();
11011 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11012 char *mapping_line)
11014 char mapping_guid[MAX_LINE_LEN];
11015 char *mapping_start, *mapping_end;
11017 // get GUID from game controller mapping line: copy complete line
11018 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11019 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11021 // get GUID from game controller mapping line: cut after GUID part
11022 mapping_start = strchr(mapping_guid, ',');
11023 if (mapping_start != NULL)
11024 *mapping_start = '\0';
11026 // cut newline from game controller mapping line
11027 mapping_end = strchr(mapping_line, '\n');
11028 if (mapping_end != NULL)
11029 *mapping_end = '\0';
11031 // add mapping entry to game controller mappings hash
11032 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11035 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11040 if (!(file = fopen(filename, MODE_READ)))
11042 Warn("cannot read game controller mappings file '%s'", filename);
11047 while (!feof(file))
11049 char line[MAX_LINE_LEN];
11051 if (!fgets(line, MAX_LINE_LEN, file))
11054 addGameControllerMappingToHash(mappings_hash, line);
11060 void SaveSetup_Default(void)
11062 char *filename = getSetupFilename();
11066 InitUserDataDirectory();
11068 if (!(file = fopen(filename, MODE_WRITE)))
11070 Warn("cannot write setup file '%s'", filename);
11075 fprintFileHeader(file, SETUP_FILENAME);
11077 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11079 // just to make things nicer :)
11080 if (global_setup_tokens[i].value == &setup.multiple_users ||
11081 global_setup_tokens[i].value == &setup.sound ||
11082 global_setup_tokens[i].value == &setup.graphics_set ||
11083 global_setup_tokens[i].value == &setup.volume_simple ||
11084 global_setup_tokens[i].value == &setup.network_mode ||
11085 global_setup_tokens[i].value == &setup.touch.control_type ||
11086 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11087 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11088 fprintf(file, "\n");
11090 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11093 for (i = 0; i < 2; i++)
11095 int grid_xsize = setup.touch.grid_xsize[i];
11096 int grid_ysize = setup.touch.grid_ysize[i];
11099 fprintf(file, "\n");
11101 for (y = 0; y < grid_ysize; y++)
11103 char token_string[MAX_LINE_LEN];
11104 char value_string[MAX_LINE_LEN];
11106 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11108 for (x = 0; x < grid_xsize; x++)
11110 char c = setup.touch.grid_button[i][x][y];
11112 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11115 value_string[grid_xsize] = '\0';
11117 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11121 fprintf(file, "\n");
11122 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11123 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11125 fprintf(file, "\n");
11126 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11127 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11129 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11133 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11134 fprintf(file, "\n");
11136 setup_input = setup.input[pnr];
11137 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11138 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11141 fprintf(file, "\n");
11142 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11143 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11145 // (internal setup values not saved to user setup file)
11147 fprintf(file, "\n");
11148 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11149 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11150 setup.debug.xsn_mode != AUTO)
11151 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11153 fprintf(file, "\n");
11154 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11155 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11159 SetFilePermissions(filename, PERMS_PRIVATE);
11162 void SaveSetup_AutoSetup(void)
11164 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11168 InitUserDataDirectory();
11170 if (!(file = fopen(filename, MODE_WRITE)))
11172 Warn("cannot write auto setup file '%s'", filename);
11179 fprintFileHeader(file, AUTOSETUP_FILENAME);
11181 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11182 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11186 SetFilePermissions(filename, PERMS_PRIVATE);
11191 void SaveSetup_ServerSetup(void)
11193 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11197 InitUserDataDirectory();
11199 if (!(file = fopen(filename, MODE_WRITE)))
11201 Warn("cannot write server setup file '%s'", filename);
11208 fprintFileHeader(file, SERVERSETUP_FILENAME);
11210 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11212 // just to make things nicer :)
11213 if (server_setup_tokens[i].value == &setup.use_api_server)
11214 fprintf(file, "\n");
11216 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11221 SetFilePermissions(filename, PERMS_PRIVATE);
11226 void SaveSetup_EditorCascade(void)
11228 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11232 InitUserDataDirectory();
11234 if (!(file = fopen(filename, MODE_WRITE)))
11236 Warn("cannot write editor cascade state file '%s'", filename);
11243 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11245 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11246 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11250 SetFilePermissions(filename, PERMS_PRIVATE);
11255 void SaveSetup(void)
11257 SaveSetup_Default();
11258 SaveSetup_AutoSetup();
11259 SaveSetup_ServerSetup();
11260 SaveSetup_EditorCascade();
11263 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11268 if (!(file = fopen(filename, MODE_WRITE)))
11270 Warn("cannot write game controller mappings file '%s'", filename);
11275 BEGIN_HASH_ITERATION(mappings_hash, itr)
11277 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11279 END_HASH_ITERATION(mappings_hash, itr)
11284 void SaveSetup_AddGameControllerMapping(char *mapping)
11286 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11287 SetupFileHash *mappings_hash = newSetupFileHash();
11289 InitUserDataDirectory();
11291 // load existing personal game controller mappings
11292 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11294 // add new mapping to personal game controller mappings
11295 addGameControllerMappingToHash(mappings_hash, mapping);
11297 // save updated personal game controller mappings
11298 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11300 freeSetupFileHash(mappings_hash);
11304 void LoadCustomElementDescriptions(void)
11306 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11307 SetupFileHash *setup_file_hash;
11310 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11312 if (element_info[i].custom_description != NULL)
11314 free(element_info[i].custom_description);
11315 element_info[i].custom_description = NULL;
11319 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11322 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11324 char *token = getStringCat2(element_info[i].token_name, ".name");
11325 char *value = getHashEntry(setup_file_hash, token);
11328 element_info[i].custom_description = getStringCopy(value);
11333 freeSetupFileHash(setup_file_hash);
11336 static int getElementFromToken(char *token)
11338 char *value = getHashEntry(element_token_hash, token);
11341 return atoi(value);
11343 Warn("unknown element token '%s'", token);
11345 return EL_UNDEFINED;
11348 void FreeGlobalAnimEventInfo(void)
11350 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11352 if (gaei->event_list == NULL)
11357 for (i = 0; i < gaei->num_event_lists; i++)
11359 checked_free(gaei->event_list[i]->event_value);
11360 checked_free(gaei->event_list[i]);
11363 checked_free(gaei->event_list);
11365 gaei->event_list = NULL;
11366 gaei->num_event_lists = 0;
11369 static int AddGlobalAnimEventList(void)
11371 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11372 int list_pos = gaei->num_event_lists++;
11374 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11375 sizeof(struct GlobalAnimEventListInfo *));
11377 gaei->event_list[list_pos] =
11378 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11380 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11382 gaeli->event_value = NULL;
11383 gaeli->num_event_values = 0;
11388 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11390 // do not add empty global animation events
11391 if (event_value == ANIM_EVENT_NONE)
11394 // if list position is undefined, create new list
11395 if (list_pos == ANIM_EVENT_UNDEFINED)
11396 list_pos = AddGlobalAnimEventList();
11398 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11399 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11400 int value_pos = gaeli->num_event_values++;
11402 gaeli->event_value = checked_realloc(gaeli->event_value,
11403 gaeli->num_event_values * sizeof(int *));
11405 gaeli->event_value[value_pos] = event_value;
11410 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11412 if (list_pos == ANIM_EVENT_UNDEFINED)
11413 return ANIM_EVENT_NONE;
11415 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11416 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11418 return gaeli->event_value[value_pos];
11421 int GetGlobalAnimEventValueCount(int list_pos)
11423 if (list_pos == ANIM_EVENT_UNDEFINED)
11426 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11427 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11429 return gaeli->num_event_values;
11432 // This function checks if a string <s> of the format "string1, string2, ..."
11433 // exactly contains a string <s_contained>.
11435 static boolean string_has_parameter(char *s, char *s_contained)
11439 if (s == NULL || s_contained == NULL)
11442 if (strlen(s_contained) > strlen(s))
11445 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11447 char next_char = s[strlen(s_contained)];
11449 // check if next character is delimiter or whitespace
11450 return (next_char == ',' || next_char == '\0' ||
11451 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11454 // check if string contains another parameter string after a comma
11455 substring = strchr(s, ',');
11456 if (substring == NULL) // string does not contain a comma
11459 // advance string pointer to next character after the comma
11462 // skip potential whitespaces after the comma
11463 while (*substring == ' ' || *substring == '\t')
11466 return string_has_parameter(substring, s_contained);
11469 static int get_anim_parameter_value(char *s)
11471 int event_value[] =
11479 char *pattern_1[] =
11487 char *pattern_2 = ".part_";
11488 char *matching_char = NULL;
11490 int pattern_1_len = 0;
11491 int result = ANIM_EVENT_NONE;
11494 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11496 matching_char = strstr(s_ptr, pattern_1[i]);
11497 pattern_1_len = strlen(pattern_1[i]);
11498 result = event_value[i];
11500 if (matching_char != NULL)
11504 if (matching_char == NULL)
11505 return ANIM_EVENT_NONE;
11507 s_ptr = matching_char + pattern_1_len;
11509 // check for main animation number ("anim_X" or "anim_XX")
11510 if (*s_ptr >= '0' && *s_ptr <= '9')
11512 int gic_anim_nr = (*s_ptr++ - '0');
11514 if (*s_ptr >= '0' && *s_ptr <= '9')
11515 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11517 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11518 return ANIM_EVENT_NONE;
11520 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11524 // invalid main animation number specified
11526 return ANIM_EVENT_NONE;
11529 // check for animation part number ("part_X" or "part_XX") (optional)
11530 if (strPrefix(s_ptr, pattern_2))
11532 s_ptr += strlen(pattern_2);
11534 if (*s_ptr >= '0' && *s_ptr <= '9')
11536 int gic_part_nr = (*s_ptr++ - '0');
11538 if (*s_ptr >= '0' && *s_ptr <= '9')
11539 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11541 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11542 return ANIM_EVENT_NONE;
11544 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11548 // invalid animation part number specified
11550 return ANIM_EVENT_NONE;
11554 // discard result if next character is neither delimiter nor whitespace
11555 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11556 *s_ptr == ' ' || *s_ptr == '\t'))
11557 return ANIM_EVENT_NONE;
11562 static int get_anim_parameter_values(char *s)
11564 int list_pos = ANIM_EVENT_UNDEFINED;
11565 int event_value = ANIM_EVENT_DEFAULT;
11567 if (string_has_parameter(s, "any"))
11568 event_value |= ANIM_EVENT_ANY;
11570 if (string_has_parameter(s, "click:self") ||
11571 string_has_parameter(s, "click") ||
11572 string_has_parameter(s, "self"))
11573 event_value |= ANIM_EVENT_SELF;
11575 if (string_has_parameter(s, "unclick:any"))
11576 event_value |= ANIM_EVENT_UNCLICK_ANY;
11578 // if animation event found, add it to global animation event list
11579 if (event_value != ANIM_EVENT_NONE)
11580 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11584 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11585 event_value = get_anim_parameter_value(s);
11587 // if animation event found, add it to global animation event list
11588 if (event_value != ANIM_EVENT_NONE)
11589 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11591 // continue with next part of the string, starting with next comma
11592 s = strchr(s + 1, ',');
11598 static int get_anim_action_parameter_value(char *token)
11600 // check most common default case first to massively speed things up
11601 if (strEqual(token, ARG_UNDEFINED))
11602 return ANIM_EVENT_ACTION_NONE;
11604 int result = getImageIDFromToken(token);
11608 char *gfx_token = getStringCat2("gfx.", token);
11610 result = getImageIDFromToken(gfx_token);
11612 checked_free(gfx_token);
11617 Key key = getKeyFromX11KeyName(token);
11619 if (key != KSYM_UNDEFINED)
11620 result = -(int)key;
11627 result = get_hash_from_key(token); // unsigned int => int
11628 result = ABS(result); // may be negative now
11629 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11631 setHashEntry(anim_url_hash, int2str(result, 0), token);
11636 result = ANIM_EVENT_ACTION_NONE;
11641 int get_parameter_value(char *value_raw, char *suffix, int type)
11643 char *value = getStringToLower(value_raw);
11644 int result = 0; // probably a save default value
11646 if (strEqual(suffix, ".direction"))
11648 result = (strEqual(value, "left") ? MV_LEFT :
11649 strEqual(value, "right") ? MV_RIGHT :
11650 strEqual(value, "up") ? MV_UP :
11651 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11653 else if (strEqual(suffix, ".position"))
11655 result = (strEqual(value, "left") ? POS_LEFT :
11656 strEqual(value, "right") ? POS_RIGHT :
11657 strEqual(value, "top") ? POS_TOP :
11658 strEqual(value, "upper") ? POS_UPPER :
11659 strEqual(value, "middle") ? POS_MIDDLE :
11660 strEqual(value, "lower") ? POS_LOWER :
11661 strEqual(value, "bottom") ? POS_BOTTOM :
11662 strEqual(value, "any") ? POS_ANY :
11663 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11665 else if (strEqual(suffix, ".align"))
11667 result = (strEqual(value, "left") ? ALIGN_LEFT :
11668 strEqual(value, "right") ? ALIGN_RIGHT :
11669 strEqual(value, "center") ? ALIGN_CENTER :
11670 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11672 else if (strEqual(suffix, ".valign"))
11674 result = (strEqual(value, "top") ? VALIGN_TOP :
11675 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11676 strEqual(value, "middle") ? VALIGN_MIDDLE :
11677 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11679 else if (strEqual(suffix, ".anim_mode"))
11681 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11682 string_has_parameter(value, "loop") ? ANIM_LOOP :
11683 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11684 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11685 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11686 string_has_parameter(value, "random") ? ANIM_RANDOM :
11687 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11688 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11689 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11690 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11691 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11692 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11693 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11694 string_has_parameter(value, "all") ? ANIM_ALL :
11695 string_has_parameter(value, "tiled") ? ANIM_TILED :
11698 if (string_has_parameter(value, "once"))
11699 result |= ANIM_ONCE;
11701 if (string_has_parameter(value, "reverse"))
11702 result |= ANIM_REVERSE;
11704 if (string_has_parameter(value, "opaque_player"))
11705 result |= ANIM_OPAQUE_PLAYER;
11707 if (string_has_parameter(value, "static_panel"))
11708 result |= ANIM_STATIC_PANEL;
11710 else if (strEqual(suffix, ".init_event") ||
11711 strEqual(suffix, ".anim_event"))
11713 result = get_anim_parameter_values(value);
11715 else if (strEqual(suffix, ".init_delay_action") ||
11716 strEqual(suffix, ".anim_delay_action") ||
11717 strEqual(suffix, ".post_delay_action") ||
11718 strEqual(suffix, ".init_event_action") ||
11719 strEqual(suffix, ".anim_event_action"))
11721 result = get_anim_action_parameter_value(value_raw);
11723 else if (strEqual(suffix, ".class"))
11725 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11726 get_hash_from_key(value));
11728 else if (strEqual(suffix, ".style"))
11730 result = STYLE_DEFAULT;
11732 if (string_has_parameter(value, "accurate_borders"))
11733 result |= STYLE_ACCURATE_BORDERS;
11735 if (string_has_parameter(value, "inner_corners"))
11736 result |= STYLE_INNER_CORNERS;
11738 if (string_has_parameter(value, "reverse"))
11739 result |= STYLE_REVERSE;
11741 if (string_has_parameter(value, "leftmost_position"))
11742 result |= STYLE_LEFTMOST_POSITION;
11744 if (string_has_parameter(value, "block_clicks"))
11745 result |= STYLE_BLOCK;
11747 if (string_has_parameter(value, "passthrough_clicks"))
11748 result |= STYLE_PASSTHROUGH;
11750 if (string_has_parameter(value, "multiple_actions"))
11751 result |= STYLE_MULTIPLE_ACTIONS;
11753 else if (strEqual(suffix, ".fade_mode"))
11755 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11756 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11757 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11758 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11759 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11760 FADE_MODE_DEFAULT);
11762 else if (strEqual(suffix, ".auto_delay_unit"))
11764 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11765 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11766 AUTO_DELAY_UNIT_DEFAULT);
11768 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11770 result = gfx.get_font_from_token_function(value);
11772 else // generic parameter of type integer or boolean
11774 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11775 type == TYPE_INTEGER ? get_integer_from_string(value) :
11776 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11777 ARG_UNDEFINED_VALUE);
11785 static int get_token_parameter_value(char *token, char *value_raw)
11789 if (token == NULL || value_raw == NULL)
11790 return ARG_UNDEFINED_VALUE;
11792 suffix = strrchr(token, '.');
11793 if (suffix == NULL)
11796 if (strEqual(suffix, ".element"))
11797 return getElementFromToken(value_raw);
11799 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11800 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11803 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11804 boolean ignore_defaults)
11808 for (i = 0; image_config_vars[i].token != NULL; i++)
11810 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11812 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11813 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11817 *image_config_vars[i].value =
11818 get_token_parameter_value(image_config_vars[i].token, value);
11822 void InitMenuDesignSettings_Static(void)
11824 // always start with reliable default values from static default config
11825 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11828 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11832 // the following initializes hierarchical values from static configuration
11834 // special case: initialize "ARG_DEFAULT" values in static default config
11835 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11836 titlescreen_initial_first_default.fade_mode =
11837 title_initial_first_default.fade_mode;
11838 titlescreen_initial_first_default.fade_delay =
11839 title_initial_first_default.fade_delay;
11840 titlescreen_initial_first_default.post_delay =
11841 title_initial_first_default.post_delay;
11842 titlescreen_initial_first_default.auto_delay =
11843 title_initial_first_default.auto_delay;
11844 titlescreen_initial_first_default.auto_delay_unit =
11845 title_initial_first_default.auto_delay_unit;
11846 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11847 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11848 titlescreen_first_default.post_delay = title_first_default.post_delay;
11849 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11850 titlescreen_first_default.auto_delay_unit =
11851 title_first_default.auto_delay_unit;
11852 titlemessage_initial_first_default.fade_mode =
11853 title_initial_first_default.fade_mode;
11854 titlemessage_initial_first_default.fade_delay =
11855 title_initial_first_default.fade_delay;
11856 titlemessage_initial_first_default.post_delay =
11857 title_initial_first_default.post_delay;
11858 titlemessage_initial_first_default.auto_delay =
11859 title_initial_first_default.auto_delay;
11860 titlemessage_initial_first_default.auto_delay_unit =
11861 title_initial_first_default.auto_delay_unit;
11862 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11863 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11864 titlemessage_first_default.post_delay = title_first_default.post_delay;
11865 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11866 titlemessage_first_default.auto_delay_unit =
11867 title_first_default.auto_delay_unit;
11869 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11870 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11871 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11872 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11873 titlescreen_initial_default.auto_delay_unit =
11874 title_initial_default.auto_delay_unit;
11875 titlescreen_default.fade_mode = title_default.fade_mode;
11876 titlescreen_default.fade_delay = title_default.fade_delay;
11877 titlescreen_default.post_delay = title_default.post_delay;
11878 titlescreen_default.auto_delay = title_default.auto_delay;
11879 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11880 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11881 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11882 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11883 titlemessage_initial_default.auto_delay_unit =
11884 title_initial_default.auto_delay_unit;
11885 titlemessage_default.fade_mode = title_default.fade_mode;
11886 titlemessage_default.fade_delay = title_default.fade_delay;
11887 titlemessage_default.post_delay = title_default.post_delay;
11888 titlemessage_default.auto_delay = title_default.auto_delay;
11889 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11891 // special case: initialize "ARG_DEFAULT" values in static default config
11892 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11893 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11895 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11896 titlescreen_first[i] = titlescreen_first_default;
11897 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11898 titlemessage_first[i] = titlemessage_first_default;
11900 titlescreen_initial[i] = titlescreen_initial_default;
11901 titlescreen[i] = titlescreen_default;
11902 titlemessage_initial[i] = titlemessage_initial_default;
11903 titlemessage[i] = titlemessage_default;
11906 // special case: initialize "ARG_DEFAULT" values in static default config
11907 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11908 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11910 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11913 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11914 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11915 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11918 // special case: initialize "ARG_DEFAULT" values in static default config
11919 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11920 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11922 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11923 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11924 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11926 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11929 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11933 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11937 struct XY *dst, *src;
11939 game_buttons_xy[] =
11941 { &game.button.save, &game.button.stop },
11942 { &game.button.pause2, &game.button.pause },
11943 { &game.button.load, &game.button.play },
11944 { &game.button.undo, &game.button.stop },
11945 { &game.button.redo, &game.button.play },
11951 // special case: initialize later added SETUP list size from LEVELS value
11952 if (menu.list_size[GAME_MODE_SETUP] == -1)
11953 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11955 // set default position for snapshot buttons to stop/pause/play buttons
11956 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11957 if ((*game_buttons_xy[i].dst).x == -1 &&
11958 (*game_buttons_xy[i].dst).y == -1)
11959 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11961 // --------------------------------------------------------------------------
11962 // dynamic viewports (including playfield margins, borders and alignments)
11963 // --------------------------------------------------------------------------
11965 // dynamic viewports currently only supported for landscape mode
11966 int display_width = MAX(video.display_width, video.display_height);
11967 int display_height = MIN(video.display_width, video.display_height);
11969 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11971 struct RectWithBorder *vp_window = &viewport.window[i];
11972 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11973 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11974 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11975 boolean dynamic_window_width = (vp_window->min_width != -1);
11976 boolean dynamic_window_height = (vp_window->min_height != -1);
11977 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11978 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11980 // adjust window size if min/max width/height is specified
11982 if (vp_window->min_width != -1)
11984 int window_width = display_width;
11986 // when using static window height, use aspect ratio of display
11987 if (vp_window->min_height == -1)
11988 window_width = vp_window->height * display_width / display_height;
11990 vp_window->width = MAX(vp_window->min_width, window_width);
11993 if (vp_window->min_height != -1)
11995 int window_height = display_height;
11997 // when using static window width, use aspect ratio of display
11998 if (vp_window->min_width == -1)
11999 window_height = vp_window->width * display_height / display_width;
12001 vp_window->height = MAX(vp_window->min_height, window_height);
12004 if (vp_window->max_width != -1)
12005 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12007 if (vp_window->max_height != -1)
12008 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12010 int playfield_width = vp_window->width;
12011 int playfield_height = vp_window->height;
12013 // adjust playfield size and position according to specified margins
12015 playfield_width -= vp_playfield->margin_left;
12016 playfield_width -= vp_playfield->margin_right;
12018 playfield_height -= vp_playfield->margin_top;
12019 playfield_height -= vp_playfield->margin_bottom;
12021 // adjust playfield size if min/max width/height is specified
12023 if (vp_playfield->min_width != -1)
12024 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12026 if (vp_playfield->min_height != -1)
12027 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12029 if (vp_playfield->max_width != -1)
12030 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12032 if (vp_playfield->max_height != -1)
12033 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12035 // adjust playfield position according to specified alignment
12037 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12038 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12039 else if (vp_playfield->align == ALIGN_CENTER)
12040 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12041 else if (vp_playfield->align == ALIGN_RIGHT)
12042 vp_playfield->x += playfield_width - vp_playfield->width;
12044 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12045 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12046 else if (vp_playfield->valign == VALIGN_MIDDLE)
12047 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12048 else if (vp_playfield->valign == VALIGN_BOTTOM)
12049 vp_playfield->y += playfield_height - vp_playfield->height;
12051 vp_playfield->x += vp_playfield->margin_left;
12052 vp_playfield->y += vp_playfield->margin_top;
12054 // adjust individual playfield borders if only default border is specified
12056 if (vp_playfield->border_left == -1)
12057 vp_playfield->border_left = vp_playfield->border_size;
12058 if (vp_playfield->border_right == -1)
12059 vp_playfield->border_right = vp_playfield->border_size;
12060 if (vp_playfield->border_top == -1)
12061 vp_playfield->border_top = vp_playfield->border_size;
12062 if (vp_playfield->border_bottom == -1)
12063 vp_playfield->border_bottom = vp_playfield->border_size;
12065 // set dynamic playfield borders if borders are specified as undefined
12066 // (but only if window size was dynamic and playfield size was static)
12068 if (dynamic_window_width && !dynamic_playfield_width)
12070 if (vp_playfield->border_left == -1)
12072 vp_playfield->border_left = (vp_playfield->x -
12073 vp_playfield->margin_left);
12074 vp_playfield->x -= vp_playfield->border_left;
12075 vp_playfield->width += vp_playfield->border_left;
12078 if (vp_playfield->border_right == -1)
12080 vp_playfield->border_right = (vp_window->width -
12082 vp_playfield->width -
12083 vp_playfield->margin_right);
12084 vp_playfield->width += vp_playfield->border_right;
12088 if (dynamic_window_height && !dynamic_playfield_height)
12090 if (vp_playfield->border_top == -1)
12092 vp_playfield->border_top = (vp_playfield->y -
12093 vp_playfield->margin_top);
12094 vp_playfield->y -= vp_playfield->border_top;
12095 vp_playfield->height += vp_playfield->border_top;
12098 if (vp_playfield->border_bottom == -1)
12100 vp_playfield->border_bottom = (vp_window->height -
12102 vp_playfield->height -
12103 vp_playfield->margin_bottom);
12104 vp_playfield->height += vp_playfield->border_bottom;
12108 // adjust playfield size to be a multiple of a defined alignment tile size
12110 int align_size = vp_playfield->align_size;
12111 int playfield_xtiles = vp_playfield->width / align_size;
12112 int playfield_ytiles = vp_playfield->height / align_size;
12113 int playfield_width_corrected = playfield_xtiles * align_size;
12114 int playfield_height_corrected = playfield_ytiles * align_size;
12115 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12116 i == GFX_SPECIAL_ARG_EDITOR);
12118 if (is_playfield_mode &&
12119 dynamic_playfield_width &&
12120 vp_playfield->width != playfield_width_corrected)
12122 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12124 vp_playfield->width = playfield_width_corrected;
12126 if (vp_playfield->align == ALIGN_LEFT)
12128 vp_playfield->border_left += playfield_xdiff;
12130 else if (vp_playfield->align == ALIGN_RIGHT)
12132 vp_playfield->border_right += playfield_xdiff;
12134 else if (vp_playfield->align == ALIGN_CENTER)
12136 int border_left_diff = playfield_xdiff / 2;
12137 int border_right_diff = playfield_xdiff - border_left_diff;
12139 vp_playfield->border_left += border_left_diff;
12140 vp_playfield->border_right += border_right_diff;
12144 if (is_playfield_mode &&
12145 dynamic_playfield_height &&
12146 vp_playfield->height != playfield_height_corrected)
12148 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12150 vp_playfield->height = playfield_height_corrected;
12152 if (vp_playfield->valign == VALIGN_TOP)
12154 vp_playfield->border_top += playfield_ydiff;
12156 else if (vp_playfield->align == VALIGN_BOTTOM)
12158 vp_playfield->border_right += playfield_ydiff;
12160 else if (vp_playfield->align == VALIGN_MIDDLE)
12162 int border_top_diff = playfield_ydiff / 2;
12163 int border_bottom_diff = playfield_ydiff - border_top_diff;
12165 vp_playfield->border_top += border_top_diff;
12166 vp_playfield->border_bottom += border_bottom_diff;
12170 // adjust door positions according to specified alignment
12172 for (j = 0; j < 2; j++)
12174 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12176 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12177 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12178 else if (vp_door->align == ALIGN_CENTER)
12179 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12180 else if (vp_door->align == ALIGN_RIGHT)
12181 vp_door->x += vp_window->width - vp_door->width;
12183 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12184 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12185 else if (vp_door->valign == VALIGN_MIDDLE)
12186 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12187 else if (vp_door->valign == VALIGN_BOTTOM)
12188 vp_door->y += vp_window->height - vp_door->height;
12193 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12197 struct XYTileSize *dst, *src;
12200 editor_buttons_xy[] =
12203 &editor.button.element_left, &editor.palette.element_left,
12204 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12207 &editor.button.element_middle, &editor.palette.element_middle,
12208 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12211 &editor.button.element_right, &editor.palette.element_right,
12212 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12219 // set default position for element buttons to element graphics
12220 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12222 if ((*editor_buttons_xy[i].dst).x == -1 &&
12223 (*editor_buttons_xy[i].dst).y == -1)
12225 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12227 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12229 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12233 // adjust editor palette rows and columns if specified to be dynamic
12235 if (editor.palette.cols == -1)
12237 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12238 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12239 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12241 editor.palette.cols = (vp_width - sc_width) / bt_width;
12243 if (editor.palette.x == -1)
12245 int palette_width = editor.palette.cols * bt_width + sc_width;
12247 editor.palette.x = (vp_width - palette_width) / 2;
12251 if (editor.palette.rows == -1)
12253 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12254 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12255 int tx_height = getFontHeight(FONT_TEXT_2);
12257 editor.palette.rows = (vp_height - tx_height) / bt_height;
12259 if (editor.palette.y == -1)
12261 int palette_height = editor.palette.rows * bt_height + tx_height;
12263 editor.palette.y = (vp_height - palette_height) / 2;
12268 static void LoadMenuDesignSettingsFromFilename(char *filename)
12270 static struct TitleFadingInfo tfi;
12271 static struct TitleMessageInfo tmi;
12272 static struct TokenInfo title_tokens[] =
12274 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12275 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12276 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12277 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12278 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12282 static struct TokenInfo titlemessage_tokens[] =
12284 { TYPE_INTEGER, &tmi.x, ".x" },
12285 { TYPE_INTEGER, &tmi.y, ".y" },
12286 { TYPE_INTEGER, &tmi.width, ".width" },
12287 { TYPE_INTEGER, &tmi.height, ".height" },
12288 { TYPE_INTEGER, &tmi.chars, ".chars" },
12289 { TYPE_INTEGER, &tmi.lines, ".lines" },
12290 { TYPE_INTEGER, &tmi.align, ".align" },
12291 { TYPE_INTEGER, &tmi.valign, ".valign" },
12292 { TYPE_INTEGER, &tmi.font, ".font" },
12293 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12294 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12295 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12296 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12297 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12298 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12299 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12300 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12301 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12307 struct TitleFadingInfo *info;
12312 // initialize first titles from "enter screen" definitions, if defined
12313 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12314 { &title_first_default, "menu.enter_screen.TITLE" },
12316 // initialize title screens from "next screen" definitions, if defined
12317 { &title_initial_default, "menu.next_screen.TITLE" },
12318 { &title_default, "menu.next_screen.TITLE" },
12324 struct TitleMessageInfo *array;
12327 titlemessage_arrays[] =
12329 // initialize first titles from "enter screen" definitions, if defined
12330 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12331 { titlescreen_first, "menu.enter_screen.TITLE" },
12332 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12333 { titlemessage_first, "menu.enter_screen.TITLE" },
12335 // initialize titles from "next screen" definitions, if defined
12336 { titlescreen_initial, "menu.next_screen.TITLE" },
12337 { titlescreen, "menu.next_screen.TITLE" },
12338 { titlemessage_initial, "menu.next_screen.TITLE" },
12339 { titlemessage, "menu.next_screen.TITLE" },
12341 // overwrite titles with title definitions, if defined
12342 { titlescreen_initial_first, "[title_initial]" },
12343 { titlescreen_first, "[title]" },
12344 { titlemessage_initial_first, "[title_initial]" },
12345 { titlemessage_first, "[title]" },
12347 { titlescreen_initial, "[title_initial]" },
12348 { titlescreen, "[title]" },
12349 { titlemessage_initial, "[title_initial]" },
12350 { titlemessage, "[title]" },
12352 // overwrite titles with title screen/message definitions, if defined
12353 { titlescreen_initial_first, "[titlescreen_initial]" },
12354 { titlescreen_first, "[titlescreen]" },
12355 { titlemessage_initial_first, "[titlemessage_initial]" },
12356 { titlemessage_first, "[titlemessage]" },
12358 { titlescreen_initial, "[titlescreen_initial]" },
12359 { titlescreen, "[titlescreen]" },
12360 { titlemessage_initial, "[titlemessage_initial]" },
12361 { titlemessage, "[titlemessage]" },
12365 SetupFileHash *setup_file_hash;
12368 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12371 // the following initializes hierarchical values from dynamic configuration
12373 // special case: initialize with default values that may be overwritten
12374 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12375 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12377 struct TokenIntPtrInfo menu_config[] =
12379 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12380 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12381 { "menu.list_size", &menu.list_size[i] }
12384 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12386 char *token = menu_config[j].token;
12387 char *value = getHashEntry(setup_file_hash, token);
12390 *menu_config[j].value = get_integer_from_string(value);
12394 // special case: initialize with default values that may be overwritten
12395 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12396 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12398 struct TokenIntPtrInfo menu_config[] =
12400 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12401 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12402 { "menu.list_size.INFO", &menu.list_size_info[i] }
12405 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12407 char *token = menu_config[j].token;
12408 char *value = getHashEntry(setup_file_hash, token);
12411 *menu_config[j].value = get_integer_from_string(value);
12415 // special case: initialize with default values that may be overwritten
12416 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12417 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12419 struct TokenIntPtrInfo menu_config[] =
12421 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12422 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12425 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12427 char *token = menu_config[j].token;
12428 char *value = getHashEntry(setup_file_hash, token);
12431 *menu_config[j].value = get_integer_from_string(value);
12435 // special case: initialize with default values that may be overwritten
12436 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12437 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12439 struct TokenIntPtrInfo menu_config[] =
12441 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12442 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12443 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12444 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12445 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12446 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12447 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12448 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12449 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12452 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12454 char *token = menu_config[j].token;
12455 char *value = getHashEntry(setup_file_hash, token);
12458 *menu_config[j].value = get_integer_from_string(value);
12462 // special case: initialize with default values that may be overwritten
12463 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12464 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12466 struct TokenIntPtrInfo menu_config[] =
12468 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12469 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12470 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12471 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12472 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12473 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12474 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12475 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12476 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12479 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12481 char *token = menu_config[j].token;
12482 char *value = getHashEntry(setup_file_hash, token);
12485 *menu_config[j].value = get_token_parameter_value(token, value);
12489 // special case: initialize with default values that may be overwritten
12490 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12491 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12495 char *token_prefix;
12496 struct RectWithBorder *struct_ptr;
12500 { "viewport.window", &viewport.window[i] },
12501 { "viewport.playfield", &viewport.playfield[i] },
12502 { "viewport.door_1", &viewport.door_1[i] },
12503 { "viewport.door_2", &viewport.door_2[i] }
12506 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12508 struct TokenIntPtrInfo vp_config[] =
12510 { ".x", &vp_struct[j].struct_ptr->x },
12511 { ".y", &vp_struct[j].struct_ptr->y },
12512 { ".width", &vp_struct[j].struct_ptr->width },
12513 { ".height", &vp_struct[j].struct_ptr->height },
12514 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12515 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12516 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12517 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12518 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12519 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12520 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12521 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12522 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12523 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12524 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12525 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12526 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12527 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12528 { ".align", &vp_struct[j].struct_ptr->align },
12529 { ".valign", &vp_struct[j].struct_ptr->valign }
12532 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12534 char *token = getStringCat2(vp_struct[j].token_prefix,
12535 vp_config[k].token);
12536 char *value = getHashEntry(setup_file_hash, token);
12539 *vp_config[k].value = get_token_parameter_value(token, value);
12546 // special case: initialize with default values that may be overwritten
12547 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12548 for (i = 0; title_info[i].info != NULL; i++)
12550 struct TitleFadingInfo *info = title_info[i].info;
12551 char *base_token = title_info[i].text;
12553 for (j = 0; title_tokens[j].type != -1; j++)
12555 char *token = getStringCat2(base_token, title_tokens[j].text);
12556 char *value = getHashEntry(setup_file_hash, token);
12560 int parameter_value = get_token_parameter_value(token, value);
12564 *(int *)title_tokens[j].value = (int)parameter_value;
12573 // special case: initialize with default values that may be overwritten
12574 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12575 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12577 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12578 char *base_token = titlemessage_arrays[i].text;
12580 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12582 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12583 char *value = getHashEntry(setup_file_hash, token);
12587 int parameter_value = get_token_parameter_value(token, value);
12589 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12593 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12594 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12596 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12606 // special case: check if network and preview player positions are redefined,
12607 // to compare this later against the main menu level preview being redefined
12608 struct TokenIntPtrInfo menu_config_players[] =
12610 { "main.network_players.x", &menu.main.network_players.redefined },
12611 { "main.network_players.y", &menu.main.network_players.redefined },
12612 { "main.preview_players.x", &menu.main.preview_players.redefined },
12613 { "main.preview_players.y", &menu.main.preview_players.redefined },
12614 { "preview.x", &preview.redefined },
12615 { "preview.y", &preview.redefined }
12618 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12619 *menu_config_players[i].value = FALSE;
12621 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12622 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12623 *menu_config_players[i].value = TRUE;
12625 // read (and overwrite with) values that may be specified in config file
12626 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12628 freeSetupFileHash(setup_file_hash);
12631 void LoadMenuDesignSettings(void)
12633 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12635 InitMenuDesignSettings_Static();
12636 InitMenuDesignSettings_SpecialPreProcessing();
12638 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12640 // first look for special settings configured in level series config
12641 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12643 if (fileExists(filename_base))
12644 LoadMenuDesignSettingsFromFilename(filename_base);
12647 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12649 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12650 LoadMenuDesignSettingsFromFilename(filename_local);
12652 InitMenuDesignSettings_SpecialPostProcessing();
12655 void LoadMenuDesignSettings_AfterGraphics(void)
12657 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12660 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12662 char *filename = getEditorSetupFilename();
12663 SetupFileList *setup_file_list, *list;
12664 SetupFileHash *element_hash;
12665 int num_unknown_tokens = 0;
12668 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12671 element_hash = newSetupFileHash();
12673 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12674 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12676 // determined size may be larger than needed (due to unknown elements)
12678 for (list = setup_file_list; list != NULL; list = list->next)
12681 // add space for up to 3 more elements for padding that may be needed
12682 *num_elements += 3;
12684 // free memory for old list of elements, if needed
12685 checked_free(*elements);
12687 // allocate memory for new list of elements
12688 *elements = checked_malloc(*num_elements * sizeof(int));
12691 for (list = setup_file_list; list != NULL; list = list->next)
12693 char *value = getHashEntry(element_hash, list->token);
12695 if (value == NULL) // try to find obsolete token mapping
12697 char *mapped_token = get_mapped_token(list->token);
12699 if (mapped_token != NULL)
12701 value = getHashEntry(element_hash, mapped_token);
12703 free(mapped_token);
12709 (*elements)[(*num_elements)++] = atoi(value);
12713 if (num_unknown_tokens == 0)
12716 Warn("unknown token(s) found in config file:");
12717 Warn("- config file: '%s'", filename);
12719 num_unknown_tokens++;
12722 Warn("- token: '%s'", list->token);
12726 if (num_unknown_tokens > 0)
12729 while (*num_elements % 4) // pad with empty elements, if needed
12730 (*elements)[(*num_elements)++] = EL_EMPTY;
12732 freeSetupFileList(setup_file_list);
12733 freeSetupFileHash(element_hash);
12736 for (i = 0; i < *num_elements; i++)
12737 Debug("editor", "element '%s' [%d]\n",
12738 element_info[(*elements)[i]].token_name, (*elements)[i]);
12742 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12745 SetupFileHash *setup_file_hash = NULL;
12746 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12747 char *filename_music, *filename_prefix, *filename_info;
12753 token_to_value_ptr[] =
12755 { "title_header", &tmp_music_file_info.title_header },
12756 { "artist_header", &tmp_music_file_info.artist_header },
12757 { "album_header", &tmp_music_file_info.album_header },
12758 { "year_header", &tmp_music_file_info.year_header },
12760 { "title", &tmp_music_file_info.title },
12761 { "artist", &tmp_music_file_info.artist },
12762 { "album", &tmp_music_file_info.album },
12763 { "year", &tmp_music_file_info.year },
12769 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12770 getCustomMusicFilename(basename));
12772 if (filename_music == NULL)
12775 // ---------- try to replace file extension ----------
12777 filename_prefix = getStringCopy(filename_music);
12778 if (strrchr(filename_prefix, '.') != NULL)
12779 *strrchr(filename_prefix, '.') = '\0';
12780 filename_info = getStringCat2(filename_prefix, ".txt");
12782 if (fileExists(filename_info))
12783 setup_file_hash = loadSetupFileHash(filename_info);
12785 free(filename_prefix);
12786 free(filename_info);
12788 if (setup_file_hash == NULL)
12790 // ---------- try to add file extension ----------
12792 filename_prefix = getStringCopy(filename_music);
12793 filename_info = getStringCat2(filename_prefix, ".txt");
12795 if (fileExists(filename_info))
12796 setup_file_hash = loadSetupFileHash(filename_info);
12798 free(filename_prefix);
12799 free(filename_info);
12802 if (setup_file_hash == NULL)
12805 // ---------- music file info found ----------
12807 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12809 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12811 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12813 *token_to_value_ptr[i].value_ptr =
12814 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12817 tmp_music_file_info.basename = getStringCopy(basename);
12818 tmp_music_file_info.music = music;
12819 tmp_music_file_info.is_sound = is_sound;
12821 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12822 *new_music_file_info = tmp_music_file_info;
12824 return new_music_file_info;
12827 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12829 return get_music_file_info_ext(basename, music, FALSE);
12832 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12834 return get_music_file_info_ext(basename, sound, TRUE);
12837 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12838 char *basename, boolean is_sound)
12840 for (; list != NULL; list = list->next)
12841 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12847 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12849 return music_info_listed_ext(list, basename, FALSE);
12852 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12854 return music_info_listed_ext(list, basename, TRUE);
12857 void LoadMusicInfo(void)
12859 char *music_directory = getCustomMusicDirectory();
12860 int num_music = getMusicListSize();
12861 int num_music_noconf = 0;
12862 int num_sounds = getSoundListSize();
12864 DirectoryEntry *dir_entry;
12865 struct FileInfo *music, *sound;
12866 struct MusicFileInfo *next, **new;
12869 while (music_file_info != NULL)
12871 next = music_file_info->next;
12873 checked_free(music_file_info->basename);
12875 checked_free(music_file_info->title_header);
12876 checked_free(music_file_info->artist_header);
12877 checked_free(music_file_info->album_header);
12878 checked_free(music_file_info->year_header);
12880 checked_free(music_file_info->title);
12881 checked_free(music_file_info->artist);
12882 checked_free(music_file_info->album);
12883 checked_free(music_file_info->year);
12885 free(music_file_info);
12887 music_file_info = next;
12890 new = &music_file_info;
12892 for (i = 0; i < num_music; i++)
12894 music = getMusicListEntry(i);
12896 if (music->filename == NULL)
12899 if (strEqual(music->filename, UNDEFINED_FILENAME))
12902 // a configured file may be not recognized as music
12903 if (!FileIsMusic(music->filename))
12906 if (!music_info_listed(music_file_info, music->filename))
12908 *new = get_music_file_info(music->filename, i);
12911 new = &(*new)->next;
12915 if ((dir = openDirectory(music_directory)) == NULL)
12917 Warn("cannot read music directory '%s'", music_directory);
12922 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12924 char *basename = dir_entry->basename;
12925 boolean music_already_used = FALSE;
12928 // skip all music files that are configured in music config file
12929 for (i = 0; i < num_music; i++)
12931 music = getMusicListEntry(i);
12933 if (music->filename == NULL)
12936 if (strEqual(basename, music->filename))
12938 music_already_used = TRUE;
12943 if (music_already_used)
12946 if (!FileIsMusic(dir_entry->filename))
12949 if (!music_info_listed(music_file_info, basename))
12951 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12954 new = &(*new)->next;
12957 num_music_noconf++;
12960 closeDirectory(dir);
12962 for (i = 0; i < num_sounds; i++)
12964 sound = getSoundListEntry(i);
12966 if (sound->filename == NULL)
12969 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12972 // a configured file may be not recognized as sound
12973 if (!FileIsSound(sound->filename))
12976 if (!sound_info_listed(music_file_info, sound->filename))
12978 *new = get_sound_file_info(sound->filename, i);
12980 new = &(*new)->next;
12984 // add pointers to previous list nodes
12986 struct MusicFileInfo *node = music_file_info;
12988 while (node != NULL)
12991 node->next->prev = node;
12997 static void add_helpanim_entry(int element, int action, int direction,
12998 int delay, int *num_list_entries)
13000 struct HelpAnimInfo *new_list_entry;
13001 (*num_list_entries)++;
13004 checked_realloc(helpanim_info,
13005 *num_list_entries * sizeof(struct HelpAnimInfo));
13006 new_list_entry = &helpanim_info[*num_list_entries - 1];
13008 new_list_entry->element = element;
13009 new_list_entry->action = action;
13010 new_list_entry->direction = direction;
13011 new_list_entry->delay = delay;
13014 static void print_unknown_token(char *filename, char *token, int token_nr)
13019 Warn("unknown token(s) found in config file:");
13020 Warn("- config file: '%s'", filename);
13023 Warn("- token: '%s'", token);
13026 static void print_unknown_token_end(int token_nr)
13032 void LoadHelpAnimInfo(void)
13034 char *filename = getHelpAnimFilename();
13035 SetupFileList *setup_file_list = NULL, *list;
13036 SetupFileHash *element_hash, *action_hash, *direction_hash;
13037 int num_list_entries = 0;
13038 int num_unknown_tokens = 0;
13041 if (fileExists(filename))
13042 setup_file_list = loadSetupFileList(filename);
13044 if (setup_file_list == NULL)
13046 // use reliable default values from static configuration
13047 SetupFileList *insert_ptr;
13049 insert_ptr = setup_file_list =
13050 newSetupFileList(helpanim_config[0].token,
13051 helpanim_config[0].value);
13053 for (i = 1; helpanim_config[i].token; i++)
13054 insert_ptr = addListEntry(insert_ptr,
13055 helpanim_config[i].token,
13056 helpanim_config[i].value);
13059 element_hash = newSetupFileHash();
13060 action_hash = newSetupFileHash();
13061 direction_hash = newSetupFileHash();
13063 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13064 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13066 for (i = 0; i < NUM_ACTIONS; i++)
13067 setHashEntry(action_hash, element_action_info[i].suffix,
13068 i_to_a(element_action_info[i].value));
13070 // do not store direction index (bit) here, but direction value!
13071 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13072 setHashEntry(direction_hash, element_direction_info[i].suffix,
13073 i_to_a(1 << element_direction_info[i].value));
13075 for (list = setup_file_list; list != NULL; list = list->next)
13077 char *element_token, *action_token, *direction_token;
13078 char *element_value, *action_value, *direction_value;
13079 int delay = atoi(list->value);
13081 if (strEqual(list->token, "end"))
13083 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13088 /* first try to break element into element/action/direction parts;
13089 if this does not work, also accept combined "element[.act][.dir]"
13090 elements (like "dynamite.active"), which are unique elements */
13092 if (strchr(list->token, '.') == NULL) // token contains no '.'
13094 element_value = getHashEntry(element_hash, list->token);
13095 if (element_value != NULL) // element found
13096 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13097 &num_list_entries);
13100 // no further suffixes found -- this is not an element
13101 print_unknown_token(filename, list->token, num_unknown_tokens++);
13107 // token has format "<prefix>.<something>"
13109 action_token = strchr(list->token, '.'); // suffix may be action ...
13110 direction_token = action_token; // ... or direction
13112 element_token = getStringCopy(list->token);
13113 *strchr(element_token, '.') = '\0';
13115 element_value = getHashEntry(element_hash, element_token);
13117 if (element_value == NULL) // this is no element
13119 element_value = getHashEntry(element_hash, list->token);
13120 if (element_value != NULL) // combined element found
13121 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13122 &num_list_entries);
13124 print_unknown_token(filename, list->token, num_unknown_tokens++);
13126 free(element_token);
13131 action_value = getHashEntry(action_hash, action_token);
13133 if (action_value != NULL) // action found
13135 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13136 &num_list_entries);
13138 free(element_token);
13143 direction_value = getHashEntry(direction_hash, direction_token);
13145 if (direction_value != NULL) // direction found
13147 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13148 &num_list_entries);
13150 free(element_token);
13155 if (strchr(action_token + 1, '.') == NULL)
13157 // no further suffixes found -- this is not an action nor direction
13159 element_value = getHashEntry(element_hash, list->token);
13160 if (element_value != NULL) // combined element found
13161 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13162 &num_list_entries);
13164 print_unknown_token(filename, list->token, num_unknown_tokens++);
13166 free(element_token);
13171 // token has format "<prefix>.<suffix>.<something>"
13173 direction_token = strchr(action_token + 1, '.');
13175 action_token = getStringCopy(action_token);
13176 *strchr(action_token + 1, '.') = '\0';
13178 action_value = getHashEntry(action_hash, action_token);
13180 if (action_value == NULL) // this is no action
13182 element_value = getHashEntry(element_hash, list->token);
13183 if (element_value != NULL) // combined element found
13184 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13185 &num_list_entries);
13187 print_unknown_token(filename, list->token, num_unknown_tokens++);
13189 free(element_token);
13190 free(action_token);
13195 direction_value = getHashEntry(direction_hash, direction_token);
13197 if (direction_value != NULL) // direction found
13199 add_helpanim_entry(atoi(element_value), atoi(action_value),
13200 atoi(direction_value), delay, &num_list_entries);
13202 free(element_token);
13203 free(action_token);
13208 // this is no direction
13210 element_value = getHashEntry(element_hash, list->token);
13211 if (element_value != NULL) // combined element found
13212 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13213 &num_list_entries);
13215 print_unknown_token(filename, list->token, num_unknown_tokens++);
13217 free(element_token);
13218 free(action_token);
13221 print_unknown_token_end(num_unknown_tokens);
13223 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13224 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13226 freeSetupFileList(setup_file_list);
13227 freeSetupFileHash(element_hash);
13228 freeSetupFileHash(action_hash);
13229 freeSetupFileHash(direction_hash);
13232 for (i = 0; i < num_list_entries; i++)
13233 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13234 EL_NAME(helpanim_info[i].element),
13235 helpanim_info[i].element,
13236 helpanim_info[i].action,
13237 helpanim_info[i].direction,
13238 helpanim_info[i].delay);
13242 void LoadHelpTextInfo(void)
13244 char *filename = getHelpTextFilename();
13247 if (helptext_info != NULL)
13249 freeSetupFileHash(helptext_info);
13250 helptext_info = NULL;
13253 if (fileExists(filename))
13254 helptext_info = loadSetupFileHash(filename);
13256 if (helptext_info == NULL)
13258 // use reliable default values from static configuration
13259 helptext_info = newSetupFileHash();
13261 for (i = 0; helptext_config[i].token; i++)
13262 setHashEntry(helptext_info,
13263 helptext_config[i].token,
13264 helptext_config[i].value);
13268 BEGIN_HASH_ITERATION(helptext_info, itr)
13270 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13271 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13273 END_HASH_ITERATION(hash, itr)
13278 // ----------------------------------------------------------------------------
13280 // ----------------------------------------------------------------------------
13282 #define MAX_NUM_CONVERT_LEVELS 1000
13284 void ConvertLevels(void)
13286 static LevelDirTree *convert_leveldir = NULL;
13287 static int convert_level_nr = -1;
13288 static int num_levels_handled = 0;
13289 static int num_levels_converted = 0;
13290 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13293 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13294 global.convert_leveldir);
13296 if (convert_leveldir == NULL)
13297 Fail("no such level identifier: '%s'", global.convert_leveldir);
13299 leveldir_current = convert_leveldir;
13301 if (global.convert_level_nr != -1)
13303 convert_leveldir->first_level = global.convert_level_nr;
13304 convert_leveldir->last_level = global.convert_level_nr;
13307 convert_level_nr = convert_leveldir->first_level;
13309 PrintLine("=", 79);
13310 Print("Converting levels\n");
13311 PrintLine("-", 79);
13312 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13313 Print("Level series name: '%s'\n", convert_leveldir->name);
13314 Print("Level series author: '%s'\n", convert_leveldir->author);
13315 Print("Number of levels: %d\n", convert_leveldir->levels);
13316 PrintLine("=", 79);
13319 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13320 levels_failed[i] = FALSE;
13322 while (convert_level_nr <= convert_leveldir->last_level)
13324 char *level_filename;
13327 level_nr = convert_level_nr++;
13329 Print("Level %03d: ", level_nr);
13331 LoadLevel(level_nr);
13332 if (level.no_level_file || level.no_valid_file)
13334 Print("(no level)\n");
13338 Print("converting level ... ");
13341 // special case: conversion of some EMC levels as requested by ACME
13342 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13345 level_filename = getDefaultLevelFilename(level_nr);
13346 new_level = !fileExists(level_filename);
13350 SaveLevel(level_nr);
13352 num_levels_converted++;
13354 Print("converted.\n");
13358 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13359 levels_failed[level_nr] = TRUE;
13361 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13364 num_levels_handled++;
13368 PrintLine("=", 79);
13369 Print("Number of levels handled: %d\n", num_levels_handled);
13370 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13371 (num_levels_handled ?
13372 num_levels_converted * 100 / num_levels_handled : 0));
13373 PrintLine("-", 79);
13374 Print("Summary (for automatic parsing by scripts):\n");
13375 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13376 convert_leveldir->identifier, num_levels_converted,
13377 num_levels_handled,
13378 (num_levels_handled ?
13379 num_levels_converted * 100 / num_levels_handled : 0));
13381 if (num_levels_handled != num_levels_converted)
13383 Print(", FAILED:");
13384 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13385 if (levels_failed[i])
13390 PrintLine("=", 79);
13392 CloseAllAndExit(0);
13396 // ----------------------------------------------------------------------------
13397 // create and save images for use in level sketches (raw BMP format)
13398 // ----------------------------------------------------------------------------
13400 void CreateLevelSketchImages(void)
13406 InitElementPropertiesGfxElement();
13408 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13409 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13411 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13413 int element = getMappedElement(i);
13414 char basename1[16];
13415 char basename2[16];
13419 sprintf(basename1, "%04d.bmp", i);
13420 sprintf(basename2, "%04ds.bmp", i);
13422 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13423 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13425 DrawSizedElement(0, 0, element, TILESIZE);
13426 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13428 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13429 Fail("cannot save level sketch image file '%s'", filename1);
13431 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13432 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13434 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13435 Fail("cannot save level sketch image file '%s'", filename2);
13440 // create corresponding SQL statements (for normal and small images)
13443 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13444 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13447 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13448 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13450 // optional: create content for forum level sketch demonstration post
13452 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13455 FreeBitmap(bitmap1);
13456 FreeBitmap(bitmap2);
13459 fprintf(stderr, "\n");
13461 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13463 CloseAllAndExit(0);
13467 // ----------------------------------------------------------------------------
13468 // create and save images for element collecting animations (raw BMP format)
13469 // ----------------------------------------------------------------------------
13471 static boolean createCollectImage(int element)
13473 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13476 void CreateCollectElementImages(void)
13480 int anim_frames = num_steps - 1;
13481 int tile_size = TILESIZE;
13482 int anim_width = tile_size * anim_frames;
13483 int anim_height = tile_size;
13484 int num_collect_images = 0;
13485 int pos_collect_images = 0;
13487 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13488 if (createCollectImage(i))
13489 num_collect_images++;
13491 Info("Creating %d element collecting animation images ...",
13492 num_collect_images);
13494 int dst_width = anim_width * 2;
13495 int dst_height = anim_height * num_collect_images / 2;
13496 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13497 char *basename_bmp = "RocksCollect.bmp";
13498 char *basename_png = "RocksCollect.png";
13499 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13500 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13501 int len_filename_bmp = strlen(filename_bmp);
13502 int len_filename_png = strlen(filename_png);
13503 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13504 char cmd_convert[max_command_len];
13506 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13510 // force using RGBA surface for destination bitmap
13511 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13512 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13514 dst_bitmap->surface =
13515 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13517 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13519 if (!createCollectImage(i))
13522 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13523 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13524 int graphic = el2img(i);
13525 char *token_name = element_info[i].token_name;
13526 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13527 Bitmap *src_bitmap;
13530 Info("- creating collecting image for '%s' ...", token_name);
13532 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13534 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13535 tile_size, tile_size, 0, 0);
13537 // force using RGBA surface for temporary bitmap (using transparent black)
13538 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13539 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13541 tmp_bitmap->surface =
13542 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13544 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13546 for (j = 0; j < anim_frames; j++)
13548 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13549 int frame_size = frame_size_final * num_steps;
13550 int offset = (tile_size - frame_size_final) / 2;
13551 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13553 while (frame_size > frame_size_final)
13557 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13559 FreeBitmap(frame_bitmap);
13561 frame_bitmap = half_bitmap;
13564 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13565 frame_size_final, frame_size_final,
13566 dst_x + j * tile_size + offset, dst_y + offset);
13568 FreeBitmap(frame_bitmap);
13571 tmp_bitmap->surface_masked = NULL;
13573 FreeBitmap(tmp_bitmap);
13575 pos_collect_images++;
13578 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13579 Fail("cannot save element collecting image file '%s'", filename_bmp);
13581 FreeBitmap(dst_bitmap);
13583 Info("Converting image file from BMP to PNG ...");
13585 if (system(cmd_convert) != 0)
13586 Fail("converting image file failed");
13588 unlink(filename_bmp);
13592 CloseAllAndExit(0);
13596 // ----------------------------------------------------------------------------
13597 // create and save images for custom and group elements (raw BMP format)
13598 // ----------------------------------------------------------------------------
13600 void CreateCustomElementImages(char *directory)
13602 char *src_basename = "RocksCE-template.ilbm";
13603 char *dst_basename = "RocksCE.bmp";
13604 char *src_filename = getPath2(directory, src_basename);
13605 char *dst_filename = getPath2(directory, dst_basename);
13606 Bitmap *src_bitmap;
13608 int yoffset_ce = 0;
13609 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13612 InitVideoDefaults();
13614 ReCreateBitmap(&backbuffer, video.width, video.height);
13616 src_bitmap = LoadImage(src_filename);
13618 bitmap = CreateBitmap(TILEX * 16 * 2,
13619 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13622 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13629 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13630 TILEX * x, TILEY * y + yoffset_ce);
13632 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13634 TILEX * x + TILEX * 16,
13635 TILEY * y + yoffset_ce);
13637 for (j = 2; j >= 0; j--)
13641 BlitBitmap(src_bitmap, bitmap,
13642 TILEX + c * 7, 0, 6, 10,
13643 TILEX * x + 6 + j * 7,
13644 TILEY * y + 11 + yoffset_ce);
13646 BlitBitmap(src_bitmap, bitmap,
13647 TILEX + c * 8, TILEY, 6, 10,
13648 TILEX * 16 + TILEX * x + 6 + j * 8,
13649 TILEY * y + 10 + yoffset_ce);
13655 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13662 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13663 TILEX * x, TILEY * y + yoffset_ge);
13665 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13667 TILEX * x + TILEX * 16,
13668 TILEY * y + yoffset_ge);
13670 for (j = 1; j >= 0; j--)
13674 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13675 TILEX * x + 6 + j * 10,
13676 TILEY * y + 11 + yoffset_ge);
13678 BlitBitmap(src_bitmap, bitmap,
13679 TILEX + c * 8, TILEY + 12, 6, 10,
13680 TILEX * 16 + TILEX * x + 10 + j * 8,
13681 TILEY * y + 10 + yoffset_ge);
13687 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13688 Fail("cannot save CE graphics file '%s'", dst_filename);
13690 FreeBitmap(bitmap);
13692 CloseAllAndExit(0);