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 & (1 << j))
2852 ei->change->has_event[j] = TRUE;
2854 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2856 ei->change->delay_fixed = getFile16BitBE(file);
2857 ei->change->delay_random = getFile16BitBE(file);
2858 ei->change->delay_frames = getFile16BitBE(file);
2860 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2862 ei->change->explode = getFile8Bit(file);
2863 ei->change->use_target_content = getFile8Bit(file);
2864 ei->change->only_if_complete = getFile8Bit(file);
2865 ei->change->use_random_replace = getFile8Bit(file);
2867 ei->change->random_percentage = getFile8Bit(file);
2868 ei->change->replace_when = getFile8Bit(file);
2870 for (y = 0; y < 3; y++)
2871 for (x = 0; x < 3; x++)
2872 ei->change->target_content.e[x][y] =
2873 getMappedElement(getFile16BitBE(file));
2875 ei->slippery_type = getFile8Bit(file);
2877 // some free bytes for future properties and padding
2878 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2880 // mark that this custom element has been modified
2881 ei->modified_settings = TRUE;
2884 level->file_has_custom_elements = TRUE;
2889 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2891 struct ElementInfo *ei;
2892 int chunk_size_expected;
2896 // ---------- custom element base property values (96 bytes) ----------------
2898 element = getMappedElement(getFile16BitBE(file));
2900 if (!IS_CUSTOM_ELEMENT(element))
2902 Warn("invalid custom element number %d", element);
2904 ReadUnusedBytesFromFile(file, chunk_size - 2);
2909 ei = &element_info[element];
2911 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2912 ei->description[i] = getFile8Bit(file);
2913 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2915 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2917 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2919 ei->num_change_pages = getFile8Bit(file);
2921 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2922 if (chunk_size_expected != chunk_size)
2924 ReadUnusedBytesFromFile(file, chunk_size - 43);
2925 return chunk_size_expected;
2928 ei->ce_value_fixed_initial = getFile16BitBE(file);
2929 ei->ce_value_random_initial = getFile16BitBE(file);
2930 ei->use_last_ce_value = getFile8Bit(file);
2932 ei->use_gfx_element = getFile8Bit(file);
2933 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2935 ei->collect_score_initial = getFile8Bit(file);
2936 ei->collect_count_initial = getFile8Bit(file);
2938 ei->drop_delay_fixed = getFile8Bit(file);
2939 ei->push_delay_fixed = getFile8Bit(file);
2940 ei->drop_delay_random = getFile8Bit(file);
2941 ei->push_delay_random = getFile8Bit(file);
2942 ei->move_delay_fixed = getFile16BitBE(file);
2943 ei->move_delay_random = getFile16BitBE(file);
2945 // bits 0 - 15 of "move_pattern" ...
2946 ei->move_pattern = getFile16BitBE(file);
2947 ei->move_direction_initial = getFile8Bit(file);
2948 ei->move_stepsize = getFile8Bit(file);
2950 ei->slippery_type = getFile8Bit(file);
2952 for (y = 0; y < 3; y++)
2953 for (x = 0; x < 3; x++)
2954 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2956 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2957 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2958 ei->move_leave_type = getFile8Bit(file);
2960 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2961 ei->move_pattern |= (getFile16BitBE(file) << 16);
2963 ei->access_direction = getFile8Bit(file);
2965 ei->explosion_delay = getFile8Bit(file);
2966 ei->ignition_delay = getFile8Bit(file);
2967 ei->explosion_type = getFile8Bit(file);
2969 // some free bytes for future custom property values and padding
2970 ReadUnusedBytesFromFile(file, 1);
2972 // ---------- change page property values (48 bytes) ------------------------
2974 setElementChangePages(ei, ei->num_change_pages);
2976 for (i = 0; i < ei->num_change_pages; i++)
2978 struct ElementChangeInfo *change = &ei->change_page[i];
2979 unsigned int event_bits;
2981 // always start with reliable default values
2982 setElementChangeInfoToDefaults(change);
2984 // bits 0 - 31 of "has_event[]" ...
2985 event_bits = getFile32BitBE(file);
2986 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2987 if (event_bits & (1 << j))
2988 change->has_event[j] = TRUE;
2990 change->target_element = getMappedElement(getFile16BitBE(file));
2992 change->delay_fixed = getFile16BitBE(file);
2993 change->delay_random = getFile16BitBE(file);
2994 change->delay_frames = getFile16BitBE(file);
2996 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2998 change->explode = getFile8Bit(file);
2999 change->use_target_content = getFile8Bit(file);
3000 change->only_if_complete = getFile8Bit(file);
3001 change->use_random_replace = getFile8Bit(file);
3003 change->random_percentage = getFile8Bit(file);
3004 change->replace_when = getFile8Bit(file);
3006 for (y = 0; y < 3; y++)
3007 for (x = 0; x < 3; x++)
3008 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3010 change->can_change = getFile8Bit(file);
3012 change->trigger_side = getFile8Bit(file);
3014 change->trigger_player = getFile8Bit(file);
3015 change->trigger_page = getFile8Bit(file);
3017 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3018 CH_PAGE_ANY : (1 << change->trigger_page));
3020 change->has_action = getFile8Bit(file);
3021 change->action_type = getFile8Bit(file);
3022 change->action_mode = getFile8Bit(file);
3023 change->action_arg = getFile16BitBE(file);
3025 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3026 event_bits = getFile8Bit(file);
3027 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3028 if (event_bits & (1 << (j - 32)))
3029 change->has_event[j] = TRUE;
3032 // mark this custom element as modified
3033 ei->modified_settings = TRUE;
3035 level->file_has_custom_elements = TRUE;
3040 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3042 struct ElementInfo *ei;
3043 struct ElementGroupInfo *group;
3047 element = getMappedElement(getFile16BitBE(file));
3049 if (!IS_GROUP_ELEMENT(element))
3051 Warn("invalid group element number %d", element);
3053 ReadUnusedBytesFromFile(file, chunk_size - 2);
3058 ei = &element_info[element];
3060 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3061 ei->description[i] = getFile8Bit(file);
3062 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3064 group = element_info[element].group;
3066 group->num_elements = getFile8Bit(file);
3068 ei->use_gfx_element = getFile8Bit(file);
3069 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3071 group->choice_mode = getFile8Bit(file);
3073 // some free bytes for future values and padding
3074 ReadUnusedBytesFromFile(file, 3);
3076 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3077 group->element[i] = getMappedElement(getFile16BitBE(file));
3079 // mark this group element as modified
3080 element_info[element].modified_settings = TRUE;
3082 level->file_has_custom_elements = TRUE;
3087 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3088 int element, int real_element)
3090 int micro_chunk_size = 0;
3091 int conf_type = getFile8Bit(file);
3092 int byte_mask = conf_type & CONF_MASK_BYTES;
3093 boolean element_found = FALSE;
3096 micro_chunk_size += 1;
3098 if (byte_mask == CONF_MASK_MULTI_BYTES)
3100 int num_bytes = getFile16BitBE(file);
3101 byte *buffer = checked_malloc(num_bytes);
3103 ReadBytesFromFile(file, buffer, num_bytes);
3105 for (i = 0; conf[i].data_type != -1; i++)
3107 if (conf[i].element == element &&
3108 conf[i].conf_type == conf_type)
3110 int data_type = conf[i].data_type;
3111 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3112 int max_num_entities = conf[i].max_num_entities;
3114 if (num_entities > max_num_entities)
3116 Warn("truncating number of entities for element %d from %d to %d",
3117 element, num_entities, max_num_entities);
3119 num_entities = max_num_entities;
3122 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3123 data_type == TYPE_CONTENT_LIST))
3125 // for element and content lists, zero entities are not allowed
3126 Warn("found empty list of entities for element %d", element);
3128 // do not set "num_entities" here to prevent reading behind buffer
3130 *(int *)(conf[i].num_entities) = 1; // at least one is required
3134 *(int *)(conf[i].num_entities) = num_entities;
3137 element_found = TRUE;
3139 if (data_type == TYPE_STRING)
3141 char *string = (char *)(conf[i].value);
3144 for (j = 0; j < max_num_entities; j++)
3145 string[j] = (j < num_entities ? buffer[j] : '\0');
3147 else if (data_type == TYPE_ELEMENT_LIST)
3149 int *element_array = (int *)(conf[i].value);
3152 for (j = 0; j < num_entities; j++)
3154 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3156 else if (data_type == TYPE_CONTENT_LIST)
3158 struct Content *content= (struct Content *)(conf[i].value);
3161 for (c = 0; c < num_entities; c++)
3162 for (y = 0; y < 3; y++)
3163 for (x = 0; x < 3; x++)
3164 content[c].e[x][y] =
3165 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3168 element_found = FALSE;
3174 checked_free(buffer);
3176 micro_chunk_size += 2 + num_bytes;
3178 else // constant size configuration data (1, 2 or 4 bytes)
3180 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3181 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3182 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3184 for (i = 0; conf[i].data_type != -1; i++)
3186 if (conf[i].element == element &&
3187 conf[i].conf_type == conf_type)
3189 int data_type = conf[i].data_type;
3191 if (data_type == TYPE_ELEMENT)
3192 value = getMappedElement(value);
3194 if (data_type == TYPE_BOOLEAN)
3195 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3197 *(int *) (conf[i].value) = value;
3199 element_found = TRUE;
3205 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3210 char *error_conf_chunk_bytes =
3211 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3212 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3213 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3214 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3215 int error_element = real_element;
3217 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3218 error_conf_chunk_bytes, error_conf_chunk_token,
3219 error_element, EL_NAME(error_element));
3222 return micro_chunk_size;
3225 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3227 int real_chunk_size = 0;
3229 li = *level; // copy level data into temporary buffer
3231 while (!checkEndOfFile(file))
3233 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3235 if (real_chunk_size >= chunk_size)
3239 *level = li; // copy temporary buffer back to level data
3241 return real_chunk_size;
3244 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3246 int real_chunk_size = 0;
3248 li = *level; // copy level data into temporary buffer
3250 while (!checkEndOfFile(file))
3252 int element = getMappedElement(getFile16BitBE(file));
3254 real_chunk_size += 2;
3255 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3257 if (real_chunk_size >= chunk_size)
3261 *level = li; // copy temporary buffer back to level data
3263 return real_chunk_size;
3266 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3268 int real_chunk_size = 0;
3270 li = *level; // copy level data into temporary buffer
3272 while (!checkEndOfFile(file))
3274 int element = getMappedElement(getFile16BitBE(file));
3276 real_chunk_size += 2;
3277 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3279 if (real_chunk_size >= chunk_size)
3283 *level = li; // copy temporary buffer back to level data
3285 return real_chunk_size;
3288 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3290 int element = getMappedElement(getFile16BitBE(file));
3291 int envelope_nr = element - EL_ENVELOPE_1;
3292 int real_chunk_size = 2;
3294 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3296 while (!checkEndOfFile(file))
3298 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3301 if (real_chunk_size >= chunk_size)
3305 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3307 return real_chunk_size;
3310 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3312 int element = getMappedElement(getFile16BitBE(file));
3313 int real_chunk_size = 2;
3314 struct ElementInfo *ei = &element_info[element];
3317 xx_ei = *ei; // copy element data into temporary buffer
3319 xx_ei.num_change_pages = -1;
3321 while (!checkEndOfFile(file))
3323 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3325 if (xx_ei.num_change_pages != -1)
3328 if (real_chunk_size >= chunk_size)
3334 if (ei->num_change_pages == -1)
3336 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3339 ei->num_change_pages = 1;
3341 setElementChangePages(ei, 1);
3342 setElementChangeInfoToDefaults(ei->change);
3344 return real_chunk_size;
3347 // initialize number of change pages stored for this custom element
3348 setElementChangePages(ei, ei->num_change_pages);
3349 for (i = 0; i < ei->num_change_pages; i++)
3350 setElementChangeInfoToDefaults(&ei->change_page[i]);
3352 // start with reading properties for the first change page
3353 xx_current_change_page = 0;
3355 while (!checkEndOfFile(file))
3357 // 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,
5684 byte header[DC_LEVEL_HEADER_SIZE];
5686 int envelope_header_pos = 62;
5687 int envelope_content_pos = 94;
5688 int level_name_pos = 251;
5689 int level_author_pos = 292;
5690 int envelope_header_len;
5691 int envelope_content_len;
5693 int level_author_len;
5695 int num_yamyam_contents;
5698 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5700 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5702 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5704 header[i * 2 + 0] = header_word >> 8;
5705 header[i * 2 + 1] = header_word & 0xff;
5708 // read some values from level header to check level decoding integrity
5709 fieldx = header[6] | (header[7] << 8);
5710 fieldy = header[8] | (header[9] << 8);
5711 num_yamyam_contents = header[60] | (header[61] << 8);
5713 // do some simple sanity checks to ensure that level was correctly decoded
5714 if (fieldx < 1 || fieldx > 256 ||
5715 fieldy < 1 || fieldy > 256 ||
5716 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5718 level->no_valid_file = TRUE;
5720 Warn("cannot decode level from stream -- using empty level");
5725 // maximum envelope header size is 31 bytes
5726 envelope_header_len = header[envelope_header_pos];
5727 // maximum envelope content size is 110 (156?) bytes
5728 envelope_content_len = header[envelope_content_pos];
5730 // maximum level title size is 40 bytes
5731 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5732 // maximum level author size is 30 (51?) bytes
5733 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5737 for (i = 0; i < envelope_header_len; i++)
5738 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5739 level->envelope[0].text[envelope_size++] =
5740 header[envelope_header_pos + 1 + i];
5742 if (envelope_header_len > 0 && envelope_content_len > 0)
5744 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5745 level->envelope[0].text[envelope_size++] = '\n';
5746 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5747 level->envelope[0].text[envelope_size++] = '\n';
5750 for (i = 0; i < envelope_content_len; i++)
5751 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5752 level->envelope[0].text[envelope_size++] =
5753 header[envelope_content_pos + 1 + i];
5755 level->envelope[0].text[envelope_size] = '\0';
5757 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5758 level->envelope[0].ysize = 10;
5759 level->envelope[0].autowrap = TRUE;
5760 level->envelope[0].centered = TRUE;
5762 for (i = 0; i < level_name_len; i++)
5763 level->name[i] = header[level_name_pos + 1 + i];
5764 level->name[level_name_len] = '\0';
5766 for (i = 0; i < level_author_len; i++)
5767 level->author[i] = header[level_author_pos + 1 + i];
5768 level->author[level_author_len] = '\0';
5770 num_yamyam_contents = header[60] | (header[61] << 8);
5771 level->num_yamyam_contents =
5772 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5774 for (i = 0; i < num_yamyam_contents; i++)
5776 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5778 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5779 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5781 if (i < MAX_ELEMENT_CONTENTS)
5782 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5786 fieldx = header[6] | (header[7] << 8);
5787 fieldy = header[8] | (header[9] << 8);
5788 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5789 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5791 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5793 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5794 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5796 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5797 level->field[x][y] = getMappedElement_DC(element_dc);
5800 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5801 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5802 level->field[x][y] = EL_PLAYER_1;
5804 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5805 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5806 level->field[x][y] = EL_PLAYER_2;
5808 level->gems_needed = header[18] | (header[19] << 8);
5810 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5811 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5812 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5813 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5814 level->score[SC_NUT] = header[28] | (header[29] << 8);
5815 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5816 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5817 level->score[SC_BUG] = header[34] | (header[35] << 8);
5818 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5819 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5820 level->score[SC_KEY] = header[40] | (header[41] << 8);
5821 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5823 level->time = header[44] | (header[45] << 8);
5825 level->amoeba_speed = header[46] | (header[47] << 8);
5826 level->time_light = header[48] | (header[49] << 8);
5827 level->time_timegate = header[50] | (header[51] << 8);
5828 level->time_wheel = header[52] | (header[53] << 8);
5829 level->time_magic_wall = header[54] | (header[55] << 8);
5830 level->extra_time = header[56] | (header[57] << 8);
5831 level->shield_normal_time = header[58] | (header[59] << 8);
5833 // shield and extra time elements do not have a score
5834 level->score[SC_SHIELD] = 0;
5835 level->extra_time_score = 0;
5837 // set time for normal and deadly shields to the same value
5838 level->shield_deadly_time = level->shield_normal_time;
5840 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5841 // can slip down from flat walls, like normal walls and steel walls
5842 level->em_slippery_gems = TRUE;
5844 // time score is counted for each 10 seconds left in Diamond Caves levels
5845 level->time_score_base = 10;
5848 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5849 struct LevelFileInfo *level_file_info,
5850 boolean level_info_only)
5852 char *filename = level_file_info->filename;
5854 int num_magic_bytes = 8;
5855 char magic_bytes[num_magic_bytes + 1];
5856 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5858 if (!(file = openFile(filename, MODE_READ)))
5860 level->no_valid_file = TRUE;
5862 if (!level_info_only)
5863 Warn("cannot read level '%s' -- using empty level", filename);
5868 // fseek(file, 0x0000, SEEK_SET);
5870 if (level_file_info->packed)
5872 // read "magic bytes" from start of file
5873 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5874 magic_bytes[0] = '\0';
5876 // check "magic bytes" for correct file format
5877 if (!strPrefix(magic_bytes, "DC2"))
5879 level->no_valid_file = TRUE;
5881 Warn("unknown DC level file '%s' -- using empty level", filename);
5886 if (strPrefix(magic_bytes, "DC2Win95") ||
5887 strPrefix(magic_bytes, "DC2Win98"))
5889 int position_first_level = 0x00fa;
5890 int extra_bytes = 4;
5893 // advance file stream to first level inside the level package
5894 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5896 // each block of level data is followed by block of non-level data
5897 num_levels_to_skip *= 2;
5899 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5900 while (num_levels_to_skip >= 0)
5902 // advance file stream to next level inside the level package
5903 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5905 level->no_valid_file = TRUE;
5907 Warn("cannot fseek in file '%s' -- using empty level", filename);
5912 // skip apparently unused extra bytes following each level
5913 ReadUnusedBytesFromFile(file, extra_bytes);
5915 // read size of next level in level package
5916 skip_bytes = getFile32BitLE(file);
5918 num_levels_to_skip--;
5923 level->no_valid_file = TRUE;
5925 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5931 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5937 // ----------------------------------------------------------------------------
5938 // functions for loading SB level
5939 // ----------------------------------------------------------------------------
5941 int getMappedElement_SB(int element_ascii, boolean use_ces)
5949 sb_element_mapping[] =
5951 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5952 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5953 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5954 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5955 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5956 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5957 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5958 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5965 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5966 if (element_ascii == sb_element_mapping[i].ascii)
5967 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5969 return EL_UNDEFINED;
5972 static void SetLevelSettings_SB(struct LevelInfo *level)
5976 level->use_step_counter = TRUE;
5979 level->score[SC_TIME_BONUS] = 0;
5980 level->time_score_base = 1;
5981 level->rate_time_over_score = TRUE;
5984 level->auto_exit_sokoban = TRUE;
5987 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5988 struct LevelFileInfo *level_file_info,
5989 boolean level_info_only)
5991 char *filename = level_file_info->filename;
5992 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5993 char last_comment[MAX_LINE_LEN];
5994 char level_name[MAX_LINE_LEN];
5997 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5998 boolean read_continued_line = FALSE;
5999 boolean reading_playfield = FALSE;
6000 boolean got_valid_playfield_line = FALSE;
6001 boolean invalid_playfield_char = FALSE;
6002 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6003 int file_level_nr = 0;
6005 int x = 0, y = 0; // initialized to make compilers happy
6007 last_comment[0] = '\0';
6008 level_name[0] = '\0';
6010 if (!(file = openFile(filename, MODE_READ)))
6012 level->no_valid_file = TRUE;
6014 if (!level_info_only)
6015 Warn("cannot read level '%s' -- using empty level", filename);
6020 while (!checkEndOfFile(file))
6022 // level successfully read, but next level may follow here
6023 if (!got_valid_playfield_line && reading_playfield)
6025 // read playfield from single level file -- skip remaining file
6026 if (!level_file_info->packed)
6029 if (file_level_nr >= num_levels_to_skip)
6034 last_comment[0] = '\0';
6035 level_name[0] = '\0';
6037 reading_playfield = FALSE;
6040 got_valid_playfield_line = FALSE;
6042 // read next line of input file
6043 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6046 // check if line was completely read and is terminated by line break
6047 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6050 // cut trailing line break (this can be newline and/or carriage return)
6051 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6052 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6055 // copy raw input line for later use (mainly debugging output)
6056 strcpy(line_raw, line);
6058 if (read_continued_line)
6060 // append new line to existing line, if there is enough space
6061 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6062 strcat(previous_line, line_ptr);
6064 strcpy(line, previous_line); // copy storage buffer to line
6066 read_continued_line = FALSE;
6069 // if the last character is '\', continue at next line
6070 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6072 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6073 strcpy(previous_line, line); // copy line to storage buffer
6075 read_continued_line = TRUE;
6081 if (line[0] == '\0')
6084 // extract comment text from comment line
6087 for (line_ptr = line; *line_ptr; line_ptr++)
6088 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6091 strcpy(last_comment, line_ptr);
6096 // extract level title text from line containing level title
6097 if (line[0] == '\'')
6099 strcpy(level_name, &line[1]);
6101 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6102 level_name[strlen(level_name) - 1] = '\0';
6107 // skip lines containing only spaces (or empty lines)
6108 for (line_ptr = line; *line_ptr; line_ptr++)
6109 if (*line_ptr != ' ')
6111 if (*line_ptr == '\0')
6114 // at this point, we have found a line containing part of a playfield
6116 got_valid_playfield_line = TRUE;
6118 if (!reading_playfield)
6120 reading_playfield = TRUE;
6121 invalid_playfield_char = FALSE;
6123 for (x = 0; x < MAX_LEV_FIELDX; x++)
6124 for (y = 0; y < MAX_LEV_FIELDY; y++)
6125 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6130 // start with topmost tile row
6134 // skip playfield line if larger row than allowed
6135 if (y >= MAX_LEV_FIELDY)
6138 // start with leftmost tile column
6141 // read playfield elements from line
6142 for (line_ptr = line; *line_ptr; line_ptr++)
6144 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6146 // stop parsing playfield line if larger column than allowed
6147 if (x >= MAX_LEV_FIELDX)
6150 if (mapped_sb_element == EL_UNDEFINED)
6152 invalid_playfield_char = TRUE;
6157 level->field[x][y] = mapped_sb_element;
6159 // continue with next tile column
6162 level->fieldx = MAX(x, level->fieldx);
6165 if (invalid_playfield_char)
6167 // if first playfield line, treat invalid lines as comment lines
6169 reading_playfield = FALSE;
6174 // continue with next tile row
6182 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6183 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6185 if (!reading_playfield)
6187 level->no_valid_file = TRUE;
6189 Warn("cannot read level '%s' -- using empty level", filename);
6194 if (*level_name != '\0')
6196 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6197 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6199 else if (*last_comment != '\0')
6201 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6202 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6206 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6209 // set all empty fields beyond the border walls to invisible steel wall
6210 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6212 if ((x == 0 || x == level->fieldx - 1 ||
6213 y == 0 || y == level->fieldy - 1) &&
6214 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6215 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6216 level->field, level->fieldx, level->fieldy);
6219 // set special level settings for Sokoban levels
6220 SetLevelSettings_SB(level);
6222 if (load_xsb_to_ces)
6224 // special global settings can now be set in level template
6225 level->use_custom_template = TRUE;
6230 // -------------------------------------------------------------------------
6231 // functions for handling native levels
6232 // -------------------------------------------------------------------------
6234 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6235 struct LevelFileInfo *level_file_info,
6236 boolean level_info_only)
6238 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6239 level->no_valid_file = TRUE;
6242 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6243 struct LevelFileInfo *level_file_info,
6244 boolean level_info_only)
6248 // determine position of requested level inside level package
6249 if (level_file_info->packed)
6250 pos = level_file_info->nr - leveldir_current->first_level;
6252 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6253 level->no_valid_file = TRUE;
6256 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6257 struct LevelFileInfo *level_file_info,
6258 boolean level_info_only)
6260 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6261 level->no_valid_file = TRUE;
6264 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6266 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6267 CopyNativeLevel_RND_to_EM(level);
6268 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6269 CopyNativeLevel_RND_to_SP(level);
6270 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6271 CopyNativeLevel_RND_to_MM(level);
6274 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6276 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6277 CopyNativeLevel_EM_to_RND(level);
6278 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6279 CopyNativeLevel_SP_to_RND(level);
6280 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6281 CopyNativeLevel_MM_to_RND(level);
6284 void SaveNativeLevel(struct LevelInfo *level)
6286 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6288 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6289 char *filename = getLevelFilenameFromBasename(basename);
6291 CopyNativeLevel_RND_to_SP(level);
6292 CopyNativeTape_RND_to_SP(level);
6294 SaveNativeLevel_SP(filename);
6299 // ----------------------------------------------------------------------------
6300 // functions for loading generic level
6301 // ----------------------------------------------------------------------------
6303 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6304 struct LevelFileInfo *level_file_info,
6305 boolean level_info_only)
6307 // always start with reliable default values
6308 setLevelInfoToDefaults(level, level_info_only, TRUE);
6310 switch (level_file_info->type)
6312 case LEVEL_FILE_TYPE_RND:
6313 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6316 case LEVEL_FILE_TYPE_EM:
6317 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6318 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6321 case LEVEL_FILE_TYPE_SP:
6322 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6323 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6326 case LEVEL_FILE_TYPE_MM:
6327 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6328 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6331 case LEVEL_FILE_TYPE_DC:
6332 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6335 case LEVEL_FILE_TYPE_SB:
6336 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6340 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6344 // if level file is invalid, restore level structure to default values
6345 if (level->no_valid_file)
6346 setLevelInfoToDefaults(level, level_info_only, FALSE);
6348 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6349 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6351 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6352 CopyNativeLevel_Native_to_RND(level);
6355 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6357 static struct LevelFileInfo level_file_info;
6359 // always start with reliable default values
6360 setFileInfoToDefaults(&level_file_info);
6362 level_file_info.nr = 0; // unknown level number
6363 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6365 setString(&level_file_info.filename, filename);
6367 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6370 static void LoadLevel_InitVersion(struct LevelInfo *level)
6374 if (leveldir_current == NULL) // only when dumping level
6377 // all engine modifications also valid for levels which use latest engine
6378 if (level->game_version < VERSION_IDENT(3,2,0,5))
6380 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6381 level->time_score_base = 10;
6384 if (leveldir_current->latest_engine)
6386 // ---------- use latest game engine --------------------------------------
6388 /* For all levels which are forced to use the latest game engine version
6389 (normally all but user contributed, private and undefined levels), set
6390 the game engine version to the actual version; this allows for actual
6391 corrections in the game engine to take effect for existing, converted
6392 levels (from "classic" or other existing games) to make the emulation
6393 of the corresponding game more accurate, while (hopefully) not breaking
6394 existing levels created from other players. */
6396 level->game_version = GAME_VERSION_ACTUAL;
6398 /* Set special EM style gems behaviour: EM style gems slip down from
6399 normal, steel and growing wall. As this is a more fundamental change,
6400 it seems better to set the default behaviour to "off" (as it is more
6401 natural) and make it configurable in the level editor (as a property
6402 of gem style elements). Already existing converted levels (neither
6403 private nor contributed levels) are changed to the new behaviour. */
6405 if (level->file_version < FILE_VERSION_2_0)
6406 level->em_slippery_gems = TRUE;
6411 // ---------- use game engine the level was created with --------------------
6413 /* For all levels which are not forced to use the latest game engine
6414 version (normally user contributed, private and undefined levels),
6415 use the version of the game engine the levels were created for.
6417 Since 2.0.1, the game engine version is now directly stored
6418 in the level file (chunk "VERS"), so there is no need anymore
6419 to set the game version from the file version (except for old,
6420 pre-2.0 levels, where the game version is still taken from the
6421 file format version used to store the level -- see above). */
6423 // player was faster than enemies in 1.0.0 and before
6424 if (level->file_version == FILE_VERSION_1_0)
6425 for (i = 0; i < MAX_PLAYERS; i++)
6426 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6428 // default behaviour for EM style gems was "slippery" only in 2.0.1
6429 if (level->game_version == VERSION_IDENT(2,0,1,0))
6430 level->em_slippery_gems = TRUE;
6432 // springs could be pushed over pits before (pre-release version) 2.2.0
6433 if (level->game_version < VERSION_IDENT(2,2,0,0))
6434 level->use_spring_bug = TRUE;
6436 if (level->game_version < VERSION_IDENT(3,2,0,5))
6438 // time orb caused limited time in endless time levels before 3.2.0-5
6439 level->use_time_orb_bug = TRUE;
6441 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6442 level->block_snap_field = FALSE;
6444 // extra time score was same value as time left score before 3.2.0-5
6445 level->extra_time_score = level->score[SC_TIME_BONUS];
6448 if (level->game_version < VERSION_IDENT(3,2,0,7))
6450 // default behaviour for snapping was "not continuous" before 3.2.0-7
6451 level->continuous_snapping = FALSE;
6454 // only few elements were able to actively move into acid before 3.1.0
6455 // trigger settings did not exist before 3.1.0; set to default "any"
6456 if (level->game_version < VERSION_IDENT(3,1,0,0))
6458 // correct "can move into acid" settings (all zero in old levels)
6460 level->can_move_into_acid_bits = 0; // nothing can move into acid
6461 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6463 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6464 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6465 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6466 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6468 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6469 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6471 // correct trigger settings (stored as zero == "none" in old levels)
6473 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6475 int element = EL_CUSTOM_START + i;
6476 struct ElementInfo *ei = &element_info[element];
6478 for (j = 0; j < ei->num_change_pages; j++)
6480 struct ElementChangeInfo *change = &ei->change_page[j];
6482 change->trigger_player = CH_PLAYER_ANY;
6483 change->trigger_page = CH_PAGE_ANY;
6488 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6490 int element = EL_CUSTOM_256;
6491 struct ElementInfo *ei = &element_info[element];
6492 struct ElementChangeInfo *change = &ei->change_page[0];
6494 /* This is needed to fix a problem that was caused by a bugfix in function
6495 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6496 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6497 not replace walkable elements, but instead just placed the player on it,
6498 without placing the Sokoban field under the player). Unfortunately, this
6499 breaks "Snake Bite" style levels when the snake is halfway through a door
6500 that just closes (the snake head is still alive and can be moved in this
6501 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6502 player (without Sokoban element) which then gets killed as designed). */
6504 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6505 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6506 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6507 change->target_element = EL_PLAYER_1;
6510 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6511 if (level->game_version < VERSION_IDENT(3,2,5,0))
6513 /* This is needed to fix a problem that was caused by a bugfix in function
6514 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6515 corrects the behaviour when a custom element changes to another custom
6516 element with a higher element number that has change actions defined.
6517 Normally, only one change per frame is allowed for custom elements.
6518 Therefore, it is checked if a custom element already changed in the
6519 current frame; if it did, subsequent changes are suppressed.
6520 Unfortunately, this is only checked for element changes, but not for
6521 change actions, which are still executed. As the function above loops
6522 through all custom elements from lower to higher, an element change
6523 resulting in a lower CE number won't be checked again, while a target
6524 element with a higher number will also be checked, and potential change
6525 actions will get executed for this CE, too (which is wrong), while
6526 further changes are ignored (which is correct). As this bugfix breaks
6527 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6528 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6529 behaviour for existing levels and tapes that make use of this bug */
6531 level->use_action_after_change_bug = TRUE;
6534 // not centering level after relocating player was default only in 3.2.3
6535 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6536 level->shifted_relocation = TRUE;
6538 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6539 if (level->game_version < VERSION_IDENT(3,2,6,0))
6540 level->em_explodes_by_fire = TRUE;
6542 // levels were solved by the first player entering an exit up to 4.1.0.0
6543 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6544 level->solved_by_one_player = TRUE;
6546 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6547 if (level->game_version < VERSION_IDENT(4,1,1,1))
6548 level->use_life_bugs = TRUE;
6550 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6551 if (level->game_version < VERSION_IDENT(4,1,1,1))
6552 level->sb_objects_needed = FALSE;
6554 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6555 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6556 level->finish_dig_collect = FALSE;
6558 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6559 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6560 level->keep_walkable_ce = TRUE;
6563 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6565 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6568 // check if this level is (not) a Sokoban level
6569 for (y = 0; y < level->fieldy; y++)
6570 for (x = 0; x < level->fieldx; x++)
6571 if (!IS_SB_ELEMENT(Tile[x][y]))
6572 is_sokoban_level = FALSE;
6574 if (is_sokoban_level)
6576 // set special level settings for Sokoban levels
6577 SetLevelSettings_SB(level);
6581 static void LoadLevel_InitSettings(struct LevelInfo *level)
6583 // adjust level settings for (non-native) Sokoban-style levels
6584 LoadLevel_InitSettings_SB(level);
6586 // rename levels with title "nameless level" or if renaming is forced
6587 if (leveldir_current->empty_level_name != NULL &&
6588 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6589 leveldir_current->force_level_name))
6590 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6591 leveldir_current->empty_level_name, level_nr);
6594 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6598 // map elements that have changed in newer versions
6599 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6600 level->game_version);
6601 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6602 for (x = 0; x < 3; x++)
6603 for (y = 0; y < 3; y++)
6604 level->yamyam_content[i].e[x][y] =
6605 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6606 level->game_version);
6610 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6614 // map custom element change events that have changed in newer versions
6615 // (these following values were accidentally changed in version 3.0.1)
6616 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6617 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6619 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6621 int element = EL_CUSTOM_START + i;
6623 // order of checking and copying events to be mapped is important
6624 // (do not change the start and end value -- they are constant)
6625 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6627 if (HAS_CHANGE_EVENT(element, j - 2))
6629 SET_CHANGE_EVENT(element, j - 2, FALSE);
6630 SET_CHANGE_EVENT(element, j, TRUE);
6634 // order of checking and copying events to be mapped is important
6635 // (do not change the start and end value -- they are constant)
6636 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6638 if (HAS_CHANGE_EVENT(element, j - 1))
6640 SET_CHANGE_EVENT(element, j - 1, FALSE);
6641 SET_CHANGE_EVENT(element, j, TRUE);
6647 // initialize "can_change" field for old levels with only one change page
6648 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6650 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6652 int element = EL_CUSTOM_START + i;
6654 if (CAN_CHANGE(element))
6655 element_info[element].change->can_change = TRUE;
6659 // correct custom element values (for old levels without these options)
6660 if (level->game_version < VERSION_IDENT(3,1,1,0))
6662 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6664 int element = EL_CUSTOM_START + i;
6665 struct ElementInfo *ei = &element_info[element];
6667 if (ei->access_direction == MV_NO_DIRECTION)
6668 ei->access_direction = MV_ALL_DIRECTIONS;
6672 // correct custom element values (fix invalid values for all versions)
6675 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6677 int element = EL_CUSTOM_START + i;
6678 struct ElementInfo *ei = &element_info[element];
6680 for (j = 0; j < ei->num_change_pages; j++)
6682 struct ElementChangeInfo *change = &ei->change_page[j];
6684 if (change->trigger_player == CH_PLAYER_NONE)
6685 change->trigger_player = CH_PLAYER_ANY;
6687 if (change->trigger_side == CH_SIDE_NONE)
6688 change->trigger_side = CH_SIDE_ANY;
6693 // initialize "can_explode" field for old levels which did not store this
6694 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6695 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6697 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6699 int element = EL_CUSTOM_START + i;
6701 if (EXPLODES_1X1_OLD(element))
6702 element_info[element].explosion_type = EXPLODES_1X1;
6704 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6705 EXPLODES_SMASHED(element) ||
6706 EXPLODES_IMPACT(element)));
6710 // correct previously hard-coded move delay values for maze runner style
6711 if (level->game_version < VERSION_IDENT(3,1,1,0))
6713 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6715 int element = EL_CUSTOM_START + i;
6717 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6719 // previously hard-coded and therefore ignored
6720 element_info[element].move_delay_fixed = 9;
6721 element_info[element].move_delay_random = 0;
6726 // set some other uninitialized values of custom elements in older levels
6727 if (level->game_version < VERSION_IDENT(3,1,0,0))
6729 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6731 int element = EL_CUSTOM_START + i;
6733 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6735 element_info[element].explosion_delay = 17;
6736 element_info[element].ignition_delay = 8;
6740 // set mouse click change events to work for left/middle/right mouse button
6741 if (level->game_version < VERSION_IDENT(4,2,3,0))
6743 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6745 int element = EL_CUSTOM_START + i;
6746 struct ElementInfo *ei = &element_info[element];
6748 for (j = 0; j < ei->num_change_pages; j++)
6750 struct ElementChangeInfo *change = &ei->change_page[j];
6752 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6753 change->has_event[CE_PRESSED_BY_MOUSE] ||
6754 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6755 change->has_event[CE_MOUSE_PRESSED_ON_X])
6756 change->trigger_side = CH_SIDE_ANY;
6762 static void LoadLevel_InitElements(struct LevelInfo *level)
6764 LoadLevel_InitStandardElements(level);
6766 if (level->file_has_custom_elements)
6767 LoadLevel_InitCustomElements(level);
6769 // initialize element properties for level editor etc.
6770 InitElementPropertiesEngine(level->game_version);
6771 InitElementPropertiesGfxElement();
6774 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6778 // map elements that have changed in newer versions
6779 for (y = 0; y < level->fieldy; y++)
6780 for (x = 0; x < level->fieldx; x++)
6781 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6782 level->game_version);
6784 // clear unused playfield data (nicer if level gets resized in editor)
6785 for (x = 0; x < MAX_LEV_FIELDX; x++)
6786 for (y = 0; y < MAX_LEV_FIELDY; y++)
6787 if (x >= level->fieldx || y >= level->fieldy)
6788 level->field[x][y] = EL_EMPTY;
6790 // copy elements to runtime playfield array
6791 for (x = 0; x < MAX_LEV_FIELDX; x++)
6792 for (y = 0; y < MAX_LEV_FIELDY; y++)
6793 Tile[x][y] = level->field[x][y];
6795 // initialize level size variables for faster access
6796 lev_fieldx = level->fieldx;
6797 lev_fieldy = level->fieldy;
6799 // determine border element for this level
6800 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6801 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6806 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6808 struct LevelFileInfo *level_file_info = &level->file_info;
6810 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6811 CopyNativeLevel_RND_to_Native(level);
6814 static void LoadLevelTemplate_LoadAndInit(void)
6816 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6818 LoadLevel_InitVersion(&level_template);
6819 LoadLevel_InitElements(&level_template);
6820 LoadLevel_InitSettings(&level_template);
6822 ActivateLevelTemplate();
6825 void LoadLevelTemplate(int nr)
6827 if (!fileExists(getGlobalLevelTemplateFilename()))
6829 Warn("no level template found for this level");
6834 setLevelFileInfo(&level_template.file_info, nr);
6836 LoadLevelTemplate_LoadAndInit();
6839 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6841 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6843 LoadLevelTemplate_LoadAndInit();
6846 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6848 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6850 if (level.use_custom_template)
6852 if (network_level != NULL)
6853 LoadNetworkLevelTemplate(network_level);
6855 LoadLevelTemplate(-1);
6858 LoadLevel_InitVersion(&level);
6859 LoadLevel_InitElements(&level);
6860 LoadLevel_InitPlayfield(&level);
6861 LoadLevel_InitSettings(&level);
6863 LoadLevel_InitNativeEngines(&level);
6866 void LoadLevel(int nr)
6868 SetLevelSetInfo(leveldir_current->identifier, nr);
6870 setLevelFileInfo(&level.file_info, nr);
6872 LoadLevel_LoadAndInit(NULL);
6875 void LoadLevelInfoOnly(int nr)
6877 setLevelFileInfo(&level.file_info, nr);
6879 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6882 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6884 SetLevelSetInfo(network_level->leveldir_identifier,
6885 network_level->file_info.nr);
6887 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6889 LoadLevel_LoadAndInit(network_level);
6892 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6896 chunk_size += putFileVersion(file, level->file_version);
6897 chunk_size += putFileVersion(file, level->game_version);
6902 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6906 chunk_size += putFile16BitBE(file, level->creation_date.year);
6907 chunk_size += putFile8Bit(file, level->creation_date.month);
6908 chunk_size += putFile8Bit(file, level->creation_date.day);
6913 #if ENABLE_HISTORIC_CHUNKS
6914 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6918 putFile8Bit(file, level->fieldx);
6919 putFile8Bit(file, level->fieldy);
6921 putFile16BitBE(file, level->time);
6922 putFile16BitBE(file, level->gems_needed);
6924 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6925 putFile8Bit(file, level->name[i]);
6927 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6928 putFile8Bit(file, level->score[i]);
6930 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6931 for (y = 0; y < 3; y++)
6932 for (x = 0; x < 3; x++)
6933 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6934 level->yamyam_content[i].e[x][y]));
6935 putFile8Bit(file, level->amoeba_speed);
6936 putFile8Bit(file, level->time_magic_wall);
6937 putFile8Bit(file, level->time_wheel);
6938 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6939 level->amoeba_content));
6940 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6941 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6942 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6943 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6945 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6947 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6948 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6949 putFile32BitBE(file, level->can_move_into_acid_bits);
6950 putFile8Bit(file, level->dont_collide_with_bits);
6952 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6953 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6955 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6956 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6957 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6959 putFile8Bit(file, level->game_engine_type);
6961 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6965 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6970 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6971 chunk_size += putFile8Bit(file, level->name[i]);
6976 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6981 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6982 chunk_size += putFile8Bit(file, level->author[i]);
6987 #if ENABLE_HISTORIC_CHUNKS
6988 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6993 for (y = 0; y < level->fieldy; y++)
6994 for (x = 0; x < level->fieldx; x++)
6995 if (level->encoding_16bit_field)
6996 chunk_size += putFile16BitBE(file, level->field[x][y]);
6998 chunk_size += putFile8Bit(file, level->field[x][y]);
7004 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7009 for (y = 0; y < level->fieldy; y++)
7010 for (x = 0; x < level->fieldx; x++)
7011 chunk_size += putFile16BitBE(file, level->field[x][y]);
7016 #if ENABLE_HISTORIC_CHUNKS
7017 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7021 putFile8Bit(file, EL_YAMYAM);
7022 putFile8Bit(file, level->num_yamyam_contents);
7023 putFile8Bit(file, 0);
7024 putFile8Bit(file, 0);
7026 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7027 for (y = 0; y < 3; y++)
7028 for (x = 0; x < 3; x++)
7029 if (level->encoding_16bit_field)
7030 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7032 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7036 #if ENABLE_HISTORIC_CHUNKS
7037 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7040 int num_contents, content_xsize, content_ysize;
7041 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7043 if (element == EL_YAMYAM)
7045 num_contents = level->num_yamyam_contents;
7049 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7050 for (y = 0; y < 3; y++)
7051 for (x = 0; x < 3; x++)
7052 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7054 else if (element == EL_BD_AMOEBA)
7060 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7061 for (y = 0; y < 3; y++)
7062 for (x = 0; x < 3; x++)
7063 content_array[i][x][y] = EL_EMPTY;
7064 content_array[0][0][0] = level->amoeba_content;
7068 // chunk header already written -- write empty chunk data
7069 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7071 Warn("cannot save content for element '%d'", element);
7076 putFile16BitBE(file, element);
7077 putFile8Bit(file, num_contents);
7078 putFile8Bit(file, content_xsize);
7079 putFile8Bit(file, content_ysize);
7081 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7083 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7084 for (y = 0; y < 3; y++)
7085 for (x = 0; x < 3; x++)
7086 putFile16BitBE(file, content_array[i][x][y]);
7090 #if ENABLE_HISTORIC_CHUNKS
7091 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7093 int envelope_nr = element - EL_ENVELOPE_1;
7094 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7098 chunk_size += putFile16BitBE(file, element);
7099 chunk_size += putFile16BitBE(file, envelope_len);
7100 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7101 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7103 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7104 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7106 for (i = 0; i < envelope_len; i++)
7107 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7113 #if ENABLE_HISTORIC_CHUNKS
7114 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7115 int num_changed_custom_elements)
7119 putFile16BitBE(file, num_changed_custom_elements);
7121 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7123 int element = EL_CUSTOM_START + i;
7125 struct ElementInfo *ei = &element_info[element];
7127 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7129 if (check < num_changed_custom_elements)
7131 putFile16BitBE(file, element);
7132 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7139 if (check != num_changed_custom_elements) // should not happen
7140 Warn("inconsistent number of custom element properties");
7144 #if ENABLE_HISTORIC_CHUNKS
7145 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7146 int num_changed_custom_elements)
7150 putFile16BitBE(file, num_changed_custom_elements);
7152 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7154 int element = EL_CUSTOM_START + i;
7156 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7158 if (check < num_changed_custom_elements)
7160 putFile16BitBE(file, element);
7161 putFile16BitBE(file, element_info[element].change->target_element);
7168 if (check != num_changed_custom_elements) // should not happen
7169 Warn("inconsistent number of custom target elements");
7173 #if ENABLE_HISTORIC_CHUNKS
7174 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7175 int num_changed_custom_elements)
7177 int i, j, x, y, check = 0;
7179 putFile16BitBE(file, num_changed_custom_elements);
7181 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7183 int element = EL_CUSTOM_START + i;
7184 struct ElementInfo *ei = &element_info[element];
7186 if (ei->modified_settings)
7188 if (check < num_changed_custom_elements)
7190 putFile16BitBE(file, element);
7192 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7193 putFile8Bit(file, ei->description[j]);
7195 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7197 // some free bytes for future properties and padding
7198 WriteUnusedBytesToFile(file, 7);
7200 putFile8Bit(file, ei->use_gfx_element);
7201 putFile16BitBE(file, ei->gfx_element_initial);
7203 putFile8Bit(file, ei->collect_score_initial);
7204 putFile8Bit(file, ei->collect_count_initial);
7206 putFile16BitBE(file, ei->push_delay_fixed);
7207 putFile16BitBE(file, ei->push_delay_random);
7208 putFile16BitBE(file, ei->move_delay_fixed);
7209 putFile16BitBE(file, ei->move_delay_random);
7211 putFile16BitBE(file, ei->move_pattern);
7212 putFile8Bit(file, ei->move_direction_initial);
7213 putFile8Bit(file, ei->move_stepsize);
7215 for (y = 0; y < 3; y++)
7216 for (x = 0; x < 3; x++)
7217 putFile16BitBE(file, ei->content.e[x][y]);
7219 putFile32BitBE(file, ei->change->events);
7221 putFile16BitBE(file, ei->change->target_element);
7223 putFile16BitBE(file, ei->change->delay_fixed);
7224 putFile16BitBE(file, ei->change->delay_random);
7225 putFile16BitBE(file, ei->change->delay_frames);
7227 putFile16BitBE(file, ei->change->initial_trigger_element);
7229 putFile8Bit(file, ei->change->explode);
7230 putFile8Bit(file, ei->change->use_target_content);
7231 putFile8Bit(file, ei->change->only_if_complete);
7232 putFile8Bit(file, ei->change->use_random_replace);
7234 putFile8Bit(file, ei->change->random_percentage);
7235 putFile8Bit(file, ei->change->replace_when);
7237 for (y = 0; y < 3; y++)
7238 for (x = 0; x < 3; x++)
7239 putFile16BitBE(file, ei->change->content.e[x][y]);
7241 putFile8Bit(file, ei->slippery_type);
7243 // some free bytes for future properties and padding
7244 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7251 if (check != num_changed_custom_elements) // should not happen
7252 Warn("inconsistent number of custom element properties");
7256 #if ENABLE_HISTORIC_CHUNKS
7257 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7259 struct ElementInfo *ei = &element_info[element];
7262 // ---------- custom element base property values (96 bytes) ----------------
7264 putFile16BitBE(file, element);
7266 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7267 putFile8Bit(file, ei->description[i]);
7269 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7271 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7273 putFile8Bit(file, ei->num_change_pages);
7275 putFile16BitBE(file, ei->ce_value_fixed_initial);
7276 putFile16BitBE(file, ei->ce_value_random_initial);
7277 putFile8Bit(file, ei->use_last_ce_value);
7279 putFile8Bit(file, ei->use_gfx_element);
7280 putFile16BitBE(file, ei->gfx_element_initial);
7282 putFile8Bit(file, ei->collect_score_initial);
7283 putFile8Bit(file, ei->collect_count_initial);
7285 putFile8Bit(file, ei->drop_delay_fixed);
7286 putFile8Bit(file, ei->push_delay_fixed);
7287 putFile8Bit(file, ei->drop_delay_random);
7288 putFile8Bit(file, ei->push_delay_random);
7289 putFile16BitBE(file, ei->move_delay_fixed);
7290 putFile16BitBE(file, ei->move_delay_random);
7292 // bits 0 - 15 of "move_pattern" ...
7293 putFile16BitBE(file, ei->move_pattern & 0xffff);
7294 putFile8Bit(file, ei->move_direction_initial);
7295 putFile8Bit(file, ei->move_stepsize);
7297 putFile8Bit(file, ei->slippery_type);
7299 for (y = 0; y < 3; y++)
7300 for (x = 0; x < 3; x++)
7301 putFile16BitBE(file, ei->content.e[x][y]);
7303 putFile16BitBE(file, ei->move_enter_element);
7304 putFile16BitBE(file, ei->move_leave_element);
7305 putFile8Bit(file, ei->move_leave_type);
7307 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7308 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7310 putFile8Bit(file, ei->access_direction);
7312 putFile8Bit(file, ei->explosion_delay);
7313 putFile8Bit(file, ei->ignition_delay);
7314 putFile8Bit(file, ei->explosion_type);
7316 // some free bytes for future custom property values and padding
7317 WriteUnusedBytesToFile(file, 1);
7319 // ---------- change page property values (48 bytes) ------------------------
7321 for (i = 0; i < ei->num_change_pages; i++)
7323 struct ElementChangeInfo *change = &ei->change_page[i];
7324 unsigned int event_bits;
7326 // bits 0 - 31 of "has_event[]" ...
7328 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7329 if (change->has_event[j])
7330 event_bits |= (1 << j);
7331 putFile32BitBE(file, event_bits);
7333 putFile16BitBE(file, change->target_element);
7335 putFile16BitBE(file, change->delay_fixed);
7336 putFile16BitBE(file, change->delay_random);
7337 putFile16BitBE(file, change->delay_frames);
7339 putFile16BitBE(file, change->initial_trigger_element);
7341 putFile8Bit(file, change->explode);
7342 putFile8Bit(file, change->use_target_content);
7343 putFile8Bit(file, change->only_if_complete);
7344 putFile8Bit(file, change->use_random_replace);
7346 putFile8Bit(file, change->random_percentage);
7347 putFile8Bit(file, change->replace_when);
7349 for (y = 0; y < 3; y++)
7350 for (x = 0; x < 3; x++)
7351 putFile16BitBE(file, change->target_content.e[x][y]);
7353 putFile8Bit(file, change->can_change);
7355 putFile8Bit(file, change->trigger_side);
7357 putFile8Bit(file, change->trigger_player);
7358 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7359 log_2(change->trigger_page)));
7361 putFile8Bit(file, change->has_action);
7362 putFile8Bit(file, change->action_type);
7363 putFile8Bit(file, change->action_mode);
7364 putFile16BitBE(file, change->action_arg);
7366 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7368 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7369 if (change->has_event[j])
7370 event_bits |= (1 << (j - 32));
7371 putFile8Bit(file, event_bits);
7376 #if ENABLE_HISTORIC_CHUNKS
7377 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7379 struct ElementInfo *ei = &element_info[element];
7380 struct ElementGroupInfo *group = ei->group;
7383 putFile16BitBE(file, element);
7385 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7386 putFile8Bit(file, ei->description[i]);
7388 putFile8Bit(file, group->num_elements);
7390 putFile8Bit(file, ei->use_gfx_element);
7391 putFile16BitBE(file, ei->gfx_element_initial);
7393 putFile8Bit(file, group->choice_mode);
7395 // some free bytes for future values and padding
7396 WriteUnusedBytesToFile(file, 3);
7398 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7399 putFile16BitBE(file, group->element[i]);
7403 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7404 boolean write_element)
7406 int save_type = entry->save_type;
7407 int data_type = entry->data_type;
7408 int conf_type = entry->conf_type;
7409 int byte_mask = conf_type & CONF_MASK_BYTES;
7410 int element = entry->element;
7411 int default_value = entry->default_value;
7413 boolean modified = FALSE;
7415 if (byte_mask != CONF_MASK_MULTI_BYTES)
7417 void *value_ptr = entry->value;
7418 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7421 // check if any settings have been modified before saving them
7422 if (value != default_value)
7425 // do not save if explicitly told or if unmodified default settings
7426 if ((save_type == SAVE_CONF_NEVER) ||
7427 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7431 num_bytes += putFile16BitBE(file, element);
7433 num_bytes += putFile8Bit(file, conf_type);
7434 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7435 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7436 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7439 else if (data_type == TYPE_STRING)
7441 char *default_string = entry->default_string;
7442 char *string = (char *)(entry->value);
7443 int string_length = strlen(string);
7446 // check if any settings have been modified before saving them
7447 if (!strEqual(string, default_string))
7450 // do not save if explicitly told or if unmodified default settings
7451 if ((save_type == SAVE_CONF_NEVER) ||
7452 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7456 num_bytes += putFile16BitBE(file, element);
7458 num_bytes += putFile8Bit(file, conf_type);
7459 num_bytes += putFile16BitBE(file, string_length);
7461 for (i = 0; i < string_length; i++)
7462 num_bytes += putFile8Bit(file, string[i]);
7464 else if (data_type == TYPE_ELEMENT_LIST)
7466 int *element_array = (int *)(entry->value);
7467 int num_elements = *(int *)(entry->num_entities);
7470 // check if any settings have been modified before saving them
7471 for (i = 0; i < num_elements; i++)
7472 if (element_array[i] != default_value)
7475 // do not save if explicitly told or if unmodified default settings
7476 if ((save_type == SAVE_CONF_NEVER) ||
7477 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7481 num_bytes += putFile16BitBE(file, element);
7483 num_bytes += putFile8Bit(file, conf_type);
7484 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7486 for (i = 0; i < num_elements; i++)
7487 num_bytes += putFile16BitBE(file, element_array[i]);
7489 else if (data_type == TYPE_CONTENT_LIST)
7491 struct Content *content = (struct Content *)(entry->value);
7492 int num_contents = *(int *)(entry->num_entities);
7495 // check if any settings have been modified before saving them
7496 for (i = 0; i < num_contents; i++)
7497 for (y = 0; y < 3; y++)
7498 for (x = 0; x < 3; x++)
7499 if (content[i].e[x][y] != default_value)
7502 // do not save if explicitly told or if unmodified default settings
7503 if ((save_type == SAVE_CONF_NEVER) ||
7504 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7508 num_bytes += putFile16BitBE(file, element);
7510 num_bytes += putFile8Bit(file, conf_type);
7511 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7513 for (i = 0; i < num_contents; i++)
7514 for (y = 0; y < 3; y++)
7515 for (x = 0; x < 3; x++)
7516 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7522 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7527 li = *level; // copy level data into temporary buffer
7529 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7530 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7535 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7540 li = *level; // copy level data into temporary buffer
7542 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7543 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7548 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7550 int envelope_nr = element - EL_ENVELOPE_1;
7554 chunk_size += putFile16BitBE(file, element);
7556 // copy envelope data into temporary buffer
7557 xx_envelope = level->envelope[envelope_nr];
7559 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7560 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7565 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7567 struct ElementInfo *ei = &element_info[element];
7571 chunk_size += putFile16BitBE(file, element);
7573 xx_ei = *ei; // copy element data into temporary buffer
7575 // set default description string for this specific element
7576 strcpy(xx_default_description, getDefaultElementDescription(ei));
7578 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7579 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7581 for (i = 0; i < ei->num_change_pages; i++)
7583 struct ElementChangeInfo *change = &ei->change_page[i];
7585 xx_current_change_page = i;
7587 xx_change = *change; // copy change data into temporary buffer
7590 setEventBitsFromEventFlags(change);
7592 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7593 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7600 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7602 struct ElementInfo *ei = &element_info[element];
7603 struct ElementGroupInfo *group = ei->group;
7607 chunk_size += putFile16BitBE(file, element);
7609 xx_ei = *ei; // copy element data into temporary buffer
7610 xx_group = *group; // copy group data into temporary buffer
7612 // set default description string for this specific element
7613 strcpy(xx_default_description, getDefaultElementDescription(ei));
7615 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7616 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7621 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7623 struct ElementInfo *ei = &element_info[element];
7627 chunk_size += putFile16BitBE(file, element);
7629 xx_ei = *ei; // copy element data into temporary buffer
7631 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7632 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7637 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7638 boolean save_as_template)
7644 if (!(file = fopen(filename, MODE_WRITE)))
7646 Warn("cannot save level file '%s'", filename);
7651 level->file_version = FILE_VERSION_ACTUAL;
7652 level->game_version = GAME_VERSION_ACTUAL;
7654 level->creation_date = getCurrentDate();
7656 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7657 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7659 chunk_size = SaveLevel_VERS(NULL, level);
7660 putFileChunkBE(file, "VERS", chunk_size);
7661 SaveLevel_VERS(file, level);
7663 chunk_size = SaveLevel_DATE(NULL, level);
7664 putFileChunkBE(file, "DATE", chunk_size);
7665 SaveLevel_DATE(file, level);
7667 chunk_size = SaveLevel_NAME(NULL, level);
7668 putFileChunkBE(file, "NAME", chunk_size);
7669 SaveLevel_NAME(file, level);
7671 chunk_size = SaveLevel_AUTH(NULL, level);
7672 putFileChunkBE(file, "AUTH", chunk_size);
7673 SaveLevel_AUTH(file, level);
7675 chunk_size = SaveLevel_INFO(NULL, level);
7676 putFileChunkBE(file, "INFO", chunk_size);
7677 SaveLevel_INFO(file, level);
7679 chunk_size = SaveLevel_BODY(NULL, level);
7680 putFileChunkBE(file, "BODY", chunk_size);
7681 SaveLevel_BODY(file, level);
7683 chunk_size = SaveLevel_ELEM(NULL, level);
7684 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7686 putFileChunkBE(file, "ELEM", chunk_size);
7687 SaveLevel_ELEM(file, level);
7690 for (i = 0; i < NUM_ENVELOPES; i++)
7692 int element = EL_ENVELOPE_1 + i;
7694 chunk_size = SaveLevel_NOTE(NULL, level, element);
7695 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7697 putFileChunkBE(file, "NOTE", chunk_size);
7698 SaveLevel_NOTE(file, level, element);
7702 // if not using template level, check for non-default custom/group elements
7703 if (!level->use_custom_template || save_as_template)
7705 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7707 int element = EL_CUSTOM_START + i;
7709 chunk_size = SaveLevel_CUSX(NULL, level, element);
7710 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7712 putFileChunkBE(file, "CUSX", chunk_size);
7713 SaveLevel_CUSX(file, level, element);
7717 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7719 int element = EL_GROUP_START + i;
7721 chunk_size = SaveLevel_GRPX(NULL, level, element);
7722 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7724 putFileChunkBE(file, "GRPX", chunk_size);
7725 SaveLevel_GRPX(file, level, element);
7729 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7731 int element = GET_EMPTY_ELEMENT(i);
7733 chunk_size = SaveLevel_EMPX(NULL, level, element);
7734 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7736 putFileChunkBE(file, "EMPX", chunk_size);
7737 SaveLevel_EMPX(file, level, element);
7744 SetFilePermissions(filename, PERMS_PRIVATE);
7747 void SaveLevel(int nr)
7749 char *filename = getDefaultLevelFilename(nr);
7751 SaveLevelFromFilename(&level, filename, FALSE);
7754 void SaveLevelTemplate(void)
7756 char *filename = getLocalLevelTemplateFilename();
7758 SaveLevelFromFilename(&level, filename, TRUE);
7761 boolean SaveLevelChecked(int nr)
7763 char *filename = getDefaultLevelFilename(nr);
7764 boolean new_level = !fileExists(filename);
7765 boolean level_saved = FALSE;
7767 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7772 Request("Level saved!", REQ_CONFIRM);
7780 void DumpLevel(struct LevelInfo *level)
7782 if (level->no_level_file || level->no_valid_file)
7784 Warn("cannot dump -- no valid level file found");
7790 Print("Level xxx (file version %08d, game version %08d)\n",
7791 level->file_version, level->game_version);
7794 Print("Level author: '%s'\n", level->author);
7795 Print("Level title: '%s'\n", level->name);
7797 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7799 Print("Level time: %d seconds\n", level->time);
7800 Print("Gems needed: %d\n", level->gems_needed);
7802 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7803 Print("Time for wheel: %d seconds\n", level->time_wheel);
7804 Print("Time for light: %d seconds\n", level->time_light);
7805 Print("Time for timegate: %d seconds\n", level->time_timegate);
7807 Print("Amoeba speed: %d\n", level->amoeba_speed);
7810 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7811 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7812 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7813 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7814 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7815 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7821 for (i = 0; i < NUM_ENVELOPES; i++)
7823 char *text = level->envelope[i].text;
7824 int text_len = strlen(text);
7825 boolean has_text = FALSE;
7827 for (j = 0; j < text_len; j++)
7828 if (text[j] != ' ' && text[j] != '\n')
7834 Print("Envelope %d:\n'%s'\n", i + 1, text);
7842 void DumpLevels(void)
7844 static LevelDirTree *dumplevel_leveldir = NULL;
7846 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7847 global.dumplevel_leveldir);
7849 if (dumplevel_leveldir == NULL)
7850 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7852 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7853 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7854 Fail("no such level number: %d", global.dumplevel_level_nr);
7856 leveldir_current = dumplevel_leveldir;
7858 LoadLevel(global.dumplevel_level_nr);
7865 // ============================================================================
7866 // tape file functions
7867 // ============================================================================
7869 static void setTapeInfoToDefaults(void)
7873 // always start with reliable default values (empty tape)
7876 // default values (also for pre-1.2 tapes) with only the first player
7877 tape.player_participates[0] = TRUE;
7878 for (i = 1; i < MAX_PLAYERS; i++)
7879 tape.player_participates[i] = FALSE;
7881 // at least one (default: the first) player participates in every tape
7882 tape.num_participating_players = 1;
7884 tape.property_bits = TAPE_PROPERTY_NONE;
7886 tape.level_nr = level_nr;
7888 tape.changed = FALSE;
7889 tape.solved = FALSE;
7891 tape.recording = FALSE;
7892 tape.playing = FALSE;
7893 tape.pausing = FALSE;
7895 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7896 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7898 tape.no_info_chunk = TRUE;
7899 tape.no_valid_file = FALSE;
7902 static int getTapePosSize(struct TapeInfo *tape)
7904 int tape_pos_size = 0;
7906 if (tape->use_key_actions)
7907 tape_pos_size += tape->num_participating_players;
7909 if (tape->use_mouse_actions)
7910 tape_pos_size += 3; // x and y position and mouse button mask
7912 tape_pos_size += 1; // tape action delay value
7914 return tape_pos_size;
7917 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7919 tape->use_key_actions = FALSE;
7920 tape->use_mouse_actions = FALSE;
7922 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7923 tape->use_key_actions = TRUE;
7925 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7926 tape->use_mouse_actions = TRUE;
7929 static int getTapeActionValue(struct TapeInfo *tape)
7931 return (tape->use_key_actions &&
7932 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7933 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7934 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7935 TAPE_ACTIONS_DEFAULT);
7938 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7940 tape->file_version = getFileVersion(file);
7941 tape->game_version = getFileVersion(file);
7946 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7950 tape->random_seed = getFile32BitBE(file);
7951 tape->date = getFile32BitBE(file);
7952 tape->length = getFile32BitBE(file);
7954 // read header fields that are new since version 1.2
7955 if (tape->file_version >= FILE_VERSION_1_2)
7957 byte store_participating_players = getFile8Bit(file);
7960 // since version 1.2, tapes store which players participate in the tape
7961 tape->num_participating_players = 0;
7962 for (i = 0; i < MAX_PLAYERS; i++)
7964 tape->player_participates[i] = FALSE;
7966 if (store_participating_players & (1 << i))
7968 tape->player_participates[i] = TRUE;
7969 tape->num_participating_players++;
7973 setTapeActionFlags(tape, getFile8Bit(file));
7975 tape->property_bits = getFile8Bit(file);
7976 tape->solved = getFile8Bit(file);
7978 engine_version = getFileVersion(file);
7979 if (engine_version > 0)
7980 tape->engine_version = engine_version;
7982 tape->engine_version = tape->game_version;
7988 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7990 tape->scr_fieldx = getFile8Bit(file);
7991 tape->scr_fieldy = getFile8Bit(file);
7996 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7998 char *level_identifier = NULL;
7999 int level_identifier_size;
8002 tape->no_info_chunk = FALSE;
8004 level_identifier_size = getFile16BitBE(file);
8006 level_identifier = checked_malloc(level_identifier_size);
8008 for (i = 0; i < level_identifier_size; i++)
8009 level_identifier[i] = getFile8Bit(file);
8011 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8012 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8014 checked_free(level_identifier);
8016 tape->level_nr = getFile16BitBE(file);
8018 chunk_size = 2 + level_identifier_size + 2;
8023 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8026 int tape_pos_size = getTapePosSize(tape);
8027 int chunk_size_expected = tape_pos_size * tape->length;
8029 if (chunk_size_expected != chunk_size)
8031 ReadUnusedBytesFromFile(file, chunk_size);
8032 return chunk_size_expected;
8035 for (i = 0; i < tape->length; i++)
8037 if (i >= MAX_TAPE_LEN)
8039 Warn("tape truncated -- size exceeds maximum tape size %d",
8042 // tape too large; read and ignore remaining tape data from this chunk
8043 for (;i < tape->length; i++)
8044 ReadUnusedBytesFromFile(file, tape_pos_size);
8049 if (tape->use_key_actions)
8051 for (j = 0; j < MAX_PLAYERS; j++)
8053 tape->pos[i].action[j] = MV_NONE;
8055 if (tape->player_participates[j])
8056 tape->pos[i].action[j] = getFile8Bit(file);
8060 if (tape->use_mouse_actions)
8062 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8063 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8064 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8067 tape->pos[i].delay = getFile8Bit(file);
8069 if (tape->file_version == FILE_VERSION_1_0)
8071 // eliminate possible diagonal moves in old tapes
8072 // this is only for backward compatibility
8074 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8075 byte action = tape->pos[i].action[0];
8076 int k, num_moves = 0;
8078 for (k = 0; k<4; k++)
8080 if (action & joy_dir[k])
8082 tape->pos[i + num_moves].action[0] = joy_dir[k];
8084 tape->pos[i + num_moves].delay = 0;
8093 tape->length += num_moves;
8096 else if (tape->file_version < FILE_VERSION_2_0)
8098 // convert pre-2.0 tapes to new tape format
8100 if (tape->pos[i].delay > 1)
8103 tape->pos[i + 1] = tape->pos[i];
8104 tape->pos[i + 1].delay = 1;
8107 for (j = 0; j < MAX_PLAYERS; j++)
8108 tape->pos[i].action[j] = MV_NONE;
8109 tape->pos[i].delay--;
8116 if (checkEndOfFile(file))
8120 if (i != tape->length)
8121 chunk_size = tape_pos_size * i;
8126 static void LoadTape_SokobanSolution(char *filename)
8129 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8131 if (!(file = openFile(filename, MODE_READ)))
8133 tape.no_valid_file = TRUE;
8138 while (!checkEndOfFile(file))
8140 unsigned char c = getByteFromFile(file);
8142 if (checkEndOfFile(file))
8149 tape.pos[tape.length].action[0] = MV_UP;
8150 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8156 tape.pos[tape.length].action[0] = MV_DOWN;
8157 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8163 tape.pos[tape.length].action[0] = MV_LEFT;
8164 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8170 tape.pos[tape.length].action[0] = MV_RIGHT;
8171 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8179 // ignore white-space characters
8183 tape.no_valid_file = TRUE;
8185 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8193 if (tape.no_valid_file)
8196 tape.length_frames = GetTapeLengthFrames();
8197 tape.length_seconds = GetTapeLengthSeconds();
8200 void LoadTapeFromFilename(char *filename)
8202 char cookie[MAX_LINE_LEN];
8203 char chunk_name[CHUNK_ID_LEN + 1];
8207 // always start with reliable default values
8208 setTapeInfoToDefaults();
8210 if (strSuffix(filename, ".sln"))
8212 LoadTape_SokobanSolution(filename);
8217 if (!(file = openFile(filename, MODE_READ)))
8219 tape.no_valid_file = TRUE;
8224 getFileChunkBE(file, chunk_name, NULL);
8225 if (strEqual(chunk_name, "RND1"))
8227 getFile32BitBE(file); // not used
8229 getFileChunkBE(file, chunk_name, NULL);
8230 if (!strEqual(chunk_name, "TAPE"))
8232 tape.no_valid_file = TRUE;
8234 Warn("unknown format of tape file '%s'", filename);
8241 else // check for pre-2.0 file format with cookie string
8243 strcpy(cookie, chunk_name);
8244 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8246 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8247 cookie[strlen(cookie) - 1] = '\0';
8249 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8251 tape.no_valid_file = TRUE;
8253 Warn("unknown format of tape file '%s'", filename);
8260 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8262 tape.no_valid_file = TRUE;
8264 Warn("unsupported version of tape file '%s'", filename);
8271 // pre-2.0 tape files have no game version, so use file version here
8272 tape.game_version = tape.file_version;
8275 if (tape.file_version < FILE_VERSION_1_2)
8277 // tape files from versions before 1.2.0 without chunk structure
8278 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8279 LoadTape_BODY(file, 2 * tape.length, &tape);
8287 int (*loader)(File *, int, struct TapeInfo *);
8291 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8292 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8293 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8294 { "INFO", -1, LoadTape_INFO },
8295 { "BODY", -1, LoadTape_BODY },
8299 while (getFileChunkBE(file, chunk_name, &chunk_size))
8303 while (chunk_info[i].name != NULL &&
8304 !strEqual(chunk_name, chunk_info[i].name))
8307 if (chunk_info[i].name == NULL)
8309 Warn("unknown chunk '%s' in tape file '%s'",
8310 chunk_name, filename);
8312 ReadUnusedBytesFromFile(file, chunk_size);
8314 else if (chunk_info[i].size != -1 &&
8315 chunk_info[i].size != chunk_size)
8317 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8318 chunk_size, chunk_name, filename);
8320 ReadUnusedBytesFromFile(file, chunk_size);
8324 // call function to load this tape chunk
8325 int chunk_size_expected =
8326 (chunk_info[i].loader)(file, chunk_size, &tape);
8328 // the size of some chunks cannot be checked before reading other
8329 // chunks first (like "HEAD" and "BODY") that contain some header
8330 // information, so check them here
8331 if (chunk_size_expected != chunk_size)
8333 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8334 chunk_size, chunk_name, filename);
8342 tape.length_frames = GetTapeLengthFrames();
8343 tape.length_seconds = GetTapeLengthSeconds();
8346 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8348 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8350 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8351 tape.engine_version);
8355 void LoadTape(int nr)
8357 char *filename = getTapeFilename(nr);
8359 LoadTapeFromFilename(filename);
8362 void LoadSolutionTape(int nr)
8364 char *filename = getSolutionTapeFilename(nr);
8366 LoadTapeFromFilename(filename);
8368 if (TAPE_IS_EMPTY(tape) &&
8369 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8370 level.native_sp_level->demo.is_available)
8371 CopyNativeTape_SP_to_RND(&level);
8374 void LoadScoreTape(char *score_tape_basename, int nr)
8376 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8378 LoadTapeFromFilename(filename);
8381 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8383 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8385 LoadTapeFromFilename(filename);
8388 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8390 // chunk required for team mode tapes with non-default screen size
8391 return (tape->num_participating_players > 1 &&
8392 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8393 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8396 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8398 putFileVersion(file, tape->file_version);
8399 putFileVersion(file, tape->game_version);
8402 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8405 byte store_participating_players = 0;
8407 // set bits for participating players for compact storage
8408 for (i = 0; i < MAX_PLAYERS; i++)
8409 if (tape->player_participates[i])
8410 store_participating_players |= (1 << i);
8412 putFile32BitBE(file, tape->random_seed);
8413 putFile32BitBE(file, tape->date);
8414 putFile32BitBE(file, tape->length);
8416 putFile8Bit(file, store_participating_players);
8418 putFile8Bit(file, getTapeActionValue(tape));
8420 putFile8Bit(file, tape->property_bits);
8421 putFile8Bit(file, tape->solved);
8423 putFileVersion(file, tape->engine_version);
8426 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8428 putFile8Bit(file, tape->scr_fieldx);
8429 putFile8Bit(file, tape->scr_fieldy);
8432 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8434 int level_identifier_size = strlen(tape->level_identifier) + 1;
8437 putFile16BitBE(file, level_identifier_size);
8439 for (i = 0; i < level_identifier_size; i++)
8440 putFile8Bit(file, tape->level_identifier[i]);
8442 putFile16BitBE(file, tape->level_nr);
8445 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8449 for (i = 0; i < tape->length; i++)
8451 if (tape->use_key_actions)
8453 for (j = 0; j < MAX_PLAYERS; j++)
8454 if (tape->player_participates[j])
8455 putFile8Bit(file, tape->pos[i].action[j]);
8458 if (tape->use_mouse_actions)
8460 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8461 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8462 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8465 putFile8Bit(file, tape->pos[i].delay);
8469 void SaveTapeToFilename(char *filename)
8473 int info_chunk_size;
8474 int body_chunk_size;
8476 if (!(file = fopen(filename, MODE_WRITE)))
8478 Warn("cannot save level recording file '%s'", filename);
8483 tape_pos_size = getTapePosSize(&tape);
8485 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8486 body_chunk_size = tape_pos_size * tape.length;
8488 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8489 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8491 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8492 SaveTape_VERS(file, &tape);
8494 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8495 SaveTape_HEAD(file, &tape);
8497 if (checkSaveTape_SCRN(&tape))
8499 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8500 SaveTape_SCRN(file, &tape);
8503 putFileChunkBE(file, "INFO", info_chunk_size);
8504 SaveTape_INFO(file, &tape);
8506 putFileChunkBE(file, "BODY", body_chunk_size);
8507 SaveTape_BODY(file, &tape);
8511 SetFilePermissions(filename, PERMS_PRIVATE);
8514 static void SaveTapeExt(char *filename)
8518 tape.file_version = FILE_VERSION_ACTUAL;
8519 tape.game_version = GAME_VERSION_ACTUAL;
8521 tape.num_participating_players = 0;
8523 // count number of participating players
8524 for (i = 0; i < MAX_PLAYERS; i++)
8525 if (tape.player_participates[i])
8526 tape.num_participating_players++;
8528 SaveTapeToFilename(filename);
8530 tape.changed = FALSE;
8533 void SaveTape(int nr)
8535 char *filename = getTapeFilename(nr);
8537 InitTapeDirectory(leveldir_current->subdir);
8539 SaveTapeExt(filename);
8542 void SaveScoreTape(int nr)
8544 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8546 // used instead of "leveldir_current->subdir" (for network games)
8547 InitScoreTapeDirectory(levelset.identifier, nr);
8549 SaveTapeExt(filename);
8552 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8553 unsigned int req_state_added)
8555 char *filename = getTapeFilename(nr);
8556 boolean new_tape = !fileExists(filename);
8557 boolean tape_saved = FALSE;
8559 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8564 Request(msg_saved, REQ_CONFIRM | req_state_added);
8572 boolean SaveTapeChecked(int nr)
8574 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8577 boolean SaveTapeChecked_LevelSolved(int nr)
8579 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8580 "Level solved! Tape saved!", REQ_STAY_OPEN);
8583 void DumpTape(struct TapeInfo *tape)
8585 int tape_frame_counter;
8588 if (tape->no_valid_file)
8590 Warn("cannot dump -- no valid tape file found");
8597 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8598 tape->level_nr, tape->file_version, tape->game_version);
8599 Print(" (effective engine version %08d)\n",
8600 tape->engine_version);
8601 Print("Level series identifier: '%s'\n", tape->level_identifier);
8603 Print("Solution tape: %s\n",
8604 tape->solved ? "yes" :
8605 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8607 Print("Special tape properties: ");
8608 if (tape->property_bits == TAPE_PROPERTY_NONE)
8610 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8611 Print("[em_random_bug]");
8612 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8613 Print("[game_speed]");
8614 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8616 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8617 Print("[single_step]");
8618 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8619 Print("[snapshot]");
8620 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8621 Print("[replayed]");
8622 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8623 Print("[tas_keys]");
8624 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8625 Print("[small_graphics]");
8628 int year2 = tape->date / 10000;
8629 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8630 int month_index_raw = (tape->date / 100) % 100;
8631 int month_index = month_index_raw % 12; // prevent invalid index
8632 int month = month_index + 1;
8633 int day = tape->date % 100;
8635 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8639 tape_frame_counter = 0;
8641 for (i = 0; i < tape->length; i++)
8643 if (i >= MAX_TAPE_LEN)
8648 for (j = 0; j < MAX_PLAYERS; j++)
8650 if (tape->player_participates[j])
8652 int action = tape->pos[i].action[j];
8654 Print("%d:%02x ", j, action);
8655 Print("[%c%c%c%c|%c%c] - ",
8656 (action & JOY_LEFT ? '<' : ' '),
8657 (action & JOY_RIGHT ? '>' : ' '),
8658 (action & JOY_UP ? '^' : ' '),
8659 (action & JOY_DOWN ? 'v' : ' '),
8660 (action & JOY_BUTTON_1 ? '1' : ' '),
8661 (action & JOY_BUTTON_2 ? '2' : ' '));
8665 Print("(%03d) ", tape->pos[i].delay);
8666 Print("[%05d]\n", tape_frame_counter);
8668 tape_frame_counter += tape->pos[i].delay;
8674 void DumpTapes(void)
8676 static LevelDirTree *dumptape_leveldir = NULL;
8678 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8679 global.dumptape_leveldir);
8681 if (dumptape_leveldir == NULL)
8682 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8684 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8685 global.dumptape_level_nr > dumptape_leveldir->last_level)
8686 Fail("no such level number: %d", global.dumptape_level_nr);
8688 leveldir_current = dumptape_leveldir;
8690 if (options.mytapes)
8691 LoadTape(global.dumptape_level_nr);
8693 LoadSolutionTape(global.dumptape_level_nr);
8701 // ============================================================================
8702 // score file functions
8703 // ============================================================================
8705 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8709 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8711 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8712 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8713 scores->entry[i].score = 0;
8714 scores->entry[i].time = 0;
8716 scores->entry[i].id = -1;
8717 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8718 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8719 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8720 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8721 strcpy(scores->entry[i].country_code, "??");
8724 scores->num_entries = 0;
8725 scores->last_added = -1;
8726 scores->last_added_local = -1;
8728 scores->updated = FALSE;
8729 scores->uploaded = FALSE;
8730 scores->tape_downloaded = FALSE;
8731 scores->force_last_added = FALSE;
8733 // The following values are intentionally not reset here:
8737 // - continue_playing
8738 // - continue_on_return
8741 static void setScoreInfoToDefaults(void)
8743 setScoreInfoToDefaultsExt(&scores);
8746 static void setServerScoreInfoToDefaults(void)
8748 setScoreInfoToDefaultsExt(&server_scores);
8751 static void LoadScore_OLD(int nr)
8754 char *filename = getScoreFilename(nr);
8755 char cookie[MAX_LINE_LEN];
8756 char line[MAX_LINE_LEN];
8760 if (!(file = fopen(filename, MODE_READ)))
8763 // check file identifier
8764 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8766 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8767 cookie[strlen(cookie) - 1] = '\0';
8769 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8771 Warn("unknown format of score file '%s'", filename);
8778 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8780 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8781 Warn("fscanf() failed; %s", strerror(errno));
8783 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8786 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8787 line[strlen(line) - 1] = '\0';
8789 for (line_ptr = line; *line_ptr; line_ptr++)
8791 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8793 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8794 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8803 static void ConvertScore_OLD(void)
8805 // only convert score to time for levels that rate playing time over score
8806 if (!level.rate_time_over_score)
8809 // convert old score to playing time for score-less levels (like Supaplex)
8810 int time_final_max = 999;
8813 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8815 int score = scores.entry[i].score;
8817 if (score > 0 && score < time_final_max)
8818 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8822 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8824 scores->file_version = getFileVersion(file);
8825 scores->game_version = getFileVersion(file);
8830 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8832 char *level_identifier = NULL;
8833 int level_identifier_size;
8836 level_identifier_size = getFile16BitBE(file);
8838 level_identifier = checked_malloc(level_identifier_size);
8840 for (i = 0; i < level_identifier_size; i++)
8841 level_identifier[i] = getFile8Bit(file);
8843 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8844 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8846 checked_free(level_identifier);
8848 scores->level_nr = getFile16BitBE(file);
8849 scores->num_entries = getFile16BitBE(file);
8851 chunk_size = 2 + level_identifier_size + 2 + 2;
8856 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8860 for (i = 0; i < scores->num_entries; i++)
8862 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8863 scores->entry[i].name[j] = getFile8Bit(file);
8865 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8868 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8873 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8877 for (i = 0; i < scores->num_entries; i++)
8878 scores->entry[i].score = getFile16BitBE(file);
8880 chunk_size = scores->num_entries * 2;
8885 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8889 for (i = 0; i < scores->num_entries; i++)
8890 scores->entry[i].score = getFile32BitBE(file);
8892 chunk_size = scores->num_entries * 4;
8897 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8901 for (i = 0; i < scores->num_entries; i++)
8902 scores->entry[i].time = getFile32BitBE(file);
8904 chunk_size = scores->num_entries * 4;
8909 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8913 for (i = 0; i < scores->num_entries; i++)
8915 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8916 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8918 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8921 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8926 void LoadScore(int nr)
8928 char *filename = getScoreFilename(nr);
8929 char cookie[MAX_LINE_LEN];
8930 char chunk_name[CHUNK_ID_LEN + 1];
8932 boolean old_score_file_format = FALSE;
8935 // always start with reliable default values
8936 setScoreInfoToDefaults();
8938 if (!(file = openFile(filename, MODE_READ)))
8941 getFileChunkBE(file, chunk_name, NULL);
8942 if (strEqual(chunk_name, "RND1"))
8944 getFile32BitBE(file); // not used
8946 getFileChunkBE(file, chunk_name, NULL);
8947 if (!strEqual(chunk_name, "SCOR"))
8949 Warn("unknown format of score file '%s'", filename);
8956 else // check for old file format with cookie string
8958 strcpy(cookie, chunk_name);
8959 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8961 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8962 cookie[strlen(cookie) - 1] = '\0';
8964 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8966 Warn("unknown format of score file '%s'", filename);
8973 old_score_file_format = TRUE;
8976 if (old_score_file_format)
8978 // score files from versions before 4.2.4.0 without chunk structure
8981 // convert score to time, if possible (mainly for Supaplex levels)
8990 int (*loader)(File *, int, struct ScoreInfo *);
8994 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8995 { "INFO", -1, LoadScore_INFO },
8996 { "NAME", -1, LoadScore_NAME },
8997 { "SCOR", -1, LoadScore_SCOR },
8998 { "SC4R", -1, LoadScore_SC4R },
8999 { "TIME", -1, LoadScore_TIME },
9000 { "TAPE", -1, LoadScore_TAPE },
9005 while (getFileChunkBE(file, chunk_name, &chunk_size))
9009 while (chunk_info[i].name != NULL &&
9010 !strEqual(chunk_name, chunk_info[i].name))
9013 if (chunk_info[i].name == NULL)
9015 Warn("unknown chunk '%s' in score file '%s'",
9016 chunk_name, filename);
9018 ReadUnusedBytesFromFile(file, chunk_size);
9020 else if (chunk_info[i].size != -1 &&
9021 chunk_info[i].size != chunk_size)
9023 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9024 chunk_size, chunk_name, filename);
9026 ReadUnusedBytesFromFile(file, chunk_size);
9030 // call function to load this score chunk
9031 int chunk_size_expected =
9032 (chunk_info[i].loader)(file, chunk_size, &scores);
9034 // the size of some chunks cannot be checked before reading other
9035 // chunks first (like "HEAD" and "BODY") that contain some header
9036 // information, so check them here
9037 if (chunk_size_expected != chunk_size)
9039 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9040 chunk_size, chunk_name, filename);
9049 #if ENABLE_HISTORIC_CHUNKS
9050 void SaveScore_OLD(int nr)
9053 char *filename = getScoreFilename(nr);
9056 // used instead of "leveldir_current->subdir" (for network games)
9057 InitScoreDirectory(levelset.identifier);
9059 if (!(file = fopen(filename, MODE_WRITE)))
9061 Warn("cannot save score for level %d", nr);
9066 fprintf(file, "%s\n\n", SCORE_COOKIE);
9068 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9069 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9073 SetFilePermissions(filename, PERMS_PRIVATE);
9077 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9079 putFileVersion(file, scores->file_version);
9080 putFileVersion(file, scores->game_version);
9083 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9085 int level_identifier_size = strlen(scores->level_identifier) + 1;
9088 putFile16BitBE(file, level_identifier_size);
9090 for (i = 0; i < level_identifier_size; i++)
9091 putFile8Bit(file, scores->level_identifier[i]);
9093 putFile16BitBE(file, scores->level_nr);
9094 putFile16BitBE(file, scores->num_entries);
9097 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9101 for (i = 0; i < scores->num_entries; i++)
9103 int name_size = strlen(scores->entry[i].name);
9105 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9106 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9110 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9114 for (i = 0; i < scores->num_entries; i++)
9115 putFile16BitBE(file, scores->entry[i].score);
9118 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9122 for (i = 0; i < scores->num_entries; i++)
9123 putFile32BitBE(file, scores->entry[i].score);
9126 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9130 for (i = 0; i < scores->num_entries; i++)
9131 putFile32BitBE(file, scores->entry[i].time);
9134 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9138 for (i = 0; i < scores->num_entries; i++)
9140 int size = strlen(scores->entry[i].tape_basename);
9142 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9143 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9147 static void SaveScoreToFilename(char *filename)
9150 int info_chunk_size;
9151 int name_chunk_size;
9152 int scor_chunk_size;
9153 int sc4r_chunk_size;
9154 int time_chunk_size;
9155 int tape_chunk_size;
9156 boolean has_large_score_values;
9159 if (!(file = fopen(filename, MODE_WRITE)))
9161 Warn("cannot save score file '%s'", filename);
9166 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9167 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9168 scor_chunk_size = scores.num_entries * 2;
9169 sc4r_chunk_size = scores.num_entries * 4;
9170 time_chunk_size = scores.num_entries * 4;
9171 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9173 has_large_score_values = FALSE;
9174 for (i = 0; i < scores.num_entries; i++)
9175 if (scores.entry[i].score > 0xffff)
9176 has_large_score_values = TRUE;
9178 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9179 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9181 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9182 SaveScore_VERS(file, &scores);
9184 putFileChunkBE(file, "INFO", info_chunk_size);
9185 SaveScore_INFO(file, &scores);
9187 putFileChunkBE(file, "NAME", name_chunk_size);
9188 SaveScore_NAME(file, &scores);
9190 if (has_large_score_values)
9192 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9193 SaveScore_SC4R(file, &scores);
9197 putFileChunkBE(file, "SCOR", scor_chunk_size);
9198 SaveScore_SCOR(file, &scores);
9201 putFileChunkBE(file, "TIME", time_chunk_size);
9202 SaveScore_TIME(file, &scores);
9204 putFileChunkBE(file, "TAPE", tape_chunk_size);
9205 SaveScore_TAPE(file, &scores);
9209 SetFilePermissions(filename, PERMS_PRIVATE);
9212 void SaveScore(int nr)
9214 char *filename = getScoreFilename(nr);
9217 // used instead of "leveldir_current->subdir" (for network games)
9218 InitScoreDirectory(levelset.identifier);
9220 scores.file_version = FILE_VERSION_ACTUAL;
9221 scores.game_version = GAME_VERSION_ACTUAL;
9223 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9224 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9225 scores.level_nr = level_nr;
9227 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9228 if (scores.entry[i].score == 0 &&
9229 scores.entry[i].time == 0 &&
9230 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9233 scores.num_entries = i;
9235 if (scores.num_entries == 0)
9238 SaveScoreToFilename(filename);
9241 static void LoadServerScoreFromCache(int nr)
9243 struct ScoreEntry score_entry;
9252 { &score_entry.score, FALSE, 0 },
9253 { &score_entry.time, FALSE, 0 },
9254 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9255 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9256 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9257 { &score_entry.id, FALSE, 0 },
9258 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9259 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9260 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9261 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9265 char *filename = getScoreCacheFilename(nr);
9266 SetupFileHash *score_hash = loadSetupFileHash(filename);
9269 server_scores.num_entries = 0;
9271 if (score_hash == NULL)
9274 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9276 score_entry = server_scores.entry[i];
9278 for (j = 0; score_mapping[j].value != NULL; j++)
9282 sprintf(token, "%02d.%d", i, j);
9284 char *value = getHashEntry(score_hash, token);
9289 if (score_mapping[j].is_string)
9291 char *score_value = (char *)score_mapping[j].value;
9292 int value_size = score_mapping[j].string_size;
9294 strncpy(score_value, value, value_size);
9295 score_value[value_size] = '\0';
9299 int *score_value = (int *)score_mapping[j].value;
9301 *score_value = atoi(value);
9304 server_scores.num_entries = i + 1;
9307 server_scores.entry[i] = score_entry;
9310 freeSetupFileHash(score_hash);
9313 void LoadServerScore(int nr, boolean download_score)
9315 if (!setup.use_api_server)
9318 // always start with reliable default values
9319 setServerScoreInfoToDefaults();
9321 // 1st step: load server scores from cache file (which may not exist)
9322 // (this should prevent reading it while the thread is writing to it)
9323 LoadServerScoreFromCache(nr);
9325 if (download_score && runtime.use_api_server)
9327 // 2nd step: download server scores from score server to cache file
9328 // (as thread, as it might time out if the server is not reachable)
9329 ApiGetScoreAsThread(nr);
9333 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9335 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9337 // if score tape not uploaded, ask for uploading missing tapes later
9338 if (!setup.has_remaining_tapes)
9339 setup.ask_for_remaining_tapes = TRUE;
9341 setup.provide_uploading_tapes = TRUE;
9342 setup.has_remaining_tapes = TRUE;
9344 SaveSetup_ServerSetup();
9347 void SaveServerScore(int nr, boolean tape_saved)
9349 if (!runtime.use_api_server)
9351 PrepareScoreTapesForUpload(leveldir_current->subdir);
9356 ApiAddScoreAsThread(nr, tape_saved, NULL);
9359 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9360 char *score_tape_filename)
9362 if (!runtime.use_api_server)
9365 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9368 void LoadLocalAndServerScore(int nr, boolean download_score)
9370 int last_added_local = scores.last_added_local;
9371 boolean force_last_added = scores.force_last_added;
9373 // needed if only showing server scores
9374 setScoreInfoToDefaults();
9376 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9379 // restore last added local score entry (before merging server scores)
9380 scores.last_added = scores.last_added_local = last_added_local;
9382 if (setup.use_api_server &&
9383 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9385 // load server scores from cache file and trigger update from server
9386 LoadServerScore(nr, download_score);
9388 // merge local scores with scores from server
9392 if (force_last_added)
9393 scores.force_last_added = force_last_added;
9397 // ============================================================================
9398 // setup file functions
9399 // ============================================================================
9401 #define TOKEN_STR_PLAYER_PREFIX "player_"
9404 static struct TokenInfo global_setup_tokens[] =
9408 &setup.player_name, "player_name"
9412 &setup.multiple_users, "multiple_users"
9416 &setup.sound, "sound"
9420 &setup.sound_loops, "repeating_sound_loops"
9424 &setup.sound_music, "background_music"
9428 &setup.sound_simple, "simple_sound_effects"
9432 &setup.toons, "toons"
9436 &setup.scroll_delay, "scroll_delay"
9440 &setup.forced_scroll_delay, "forced_scroll_delay"
9444 &setup.scroll_delay_value, "scroll_delay_value"
9448 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9452 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9456 &setup.fade_screens, "fade_screens"
9460 &setup.autorecord, "automatic_tape_recording"
9464 &setup.auto_pause_on_start, "auto_pause_on_start"
9468 &setup.show_titlescreen, "show_titlescreen"
9472 &setup.quick_doors, "quick_doors"
9476 &setup.team_mode, "team_mode"
9480 &setup.handicap, "handicap"
9484 &setup.skip_levels, "skip_levels"
9488 &setup.increment_levels, "increment_levels"
9492 &setup.auto_play_next_level, "auto_play_next_level"
9496 &setup.count_score_after_game, "count_score_after_game"
9500 &setup.show_scores_after_game, "show_scores_after_game"
9504 &setup.time_limit, "time_limit"
9508 &setup.fullscreen, "fullscreen"
9512 &setup.window_scaling_percent, "window_scaling_percent"
9516 &setup.window_scaling_quality, "window_scaling_quality"
9520 &setup.screen_rendering_mode, "screen_rendering_mode"
9524 &setup.vsync_mode, "vsync_mode"
9528 &setup.ask_on_escape, "ask_on_escape"
9532 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9536 &setup.ask_on_game_over, "ask_on_game_over"
9540 &setup.ask_on_quit_game, "ask_on_quit_game"
9544 &setup.ask_on_quit_program, "ask_on_quit_program"
9548 &setup.quick_switch, "quick_player_switch"
9552 &setup.input_on_focus, "input_on_focus"
9556 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9560 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9564 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9568 &setup.game_speed_extended, "game_speed_extended"
9572 &setup.game_frame_delay, "game_frame_delay"
9576 &setup.sp_show_border_elements, "sp_show_border_elements"
9580 &setup.small_game_graphics, "small_game_graphics"
9584 &setup.show_load_save_buttons, "show_load_save_buttons"
9588 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9592 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9596 &setup.graphics_set, "graphics_set"
9600 &setup.sounds_set, "sounds_set"
9604 &setup.music_set, "music_set"
9608 &setup.override_level_graphics, "override_level_graphics"
9612 &setup.override_level_sounds, "override_level_sounds"
9616 &setup.override_level_music, "override_level_music"
9620 &setup.volume_simple, "volume_simple"
9624 &setup.volume_loops, "volume_loops"
9628 &setup.volume_music, "volume_music"
9632 &setup.network_mode, "network_mode"
9636 &setup.network_player_nr, "network_player"
9640 &setup.network_server_hostname, "network_server_hostname"
9644 &setup.touch.control_type, "touch.control_type"
9648 &setup.touch.move_distance, "touch.move_distance"
9652 &setup.touch.drop_distance, "touch.drop_distance"
9656 &setup.touch.transparency, "touch.transparency"
9660 &setup.touch.draw_outlined, "touch.draw_outlined"
9664 &setup.touch.draw_pressed, "touch.draw_pressed"
9668 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9672 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9676 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9680 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9684 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9688 static struct TokenInfo auto_setup_tokens[] =
9692 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9696 static struct TokenInfo server_setup_tokens[] =
9700 &setup.player_uuid, "player_uuid"
9704 &setup.player_version, "player_version"
9708 &setup.use_api_server, TEST_PREFIX "use_api_server"
9712 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9716 &setup.api_server_password, TEST_PREFIX "api_server_password"
9720 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9724 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9728 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9732 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9736 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9740 static struct TokenInfo editor_setup_tokens[] =
9744 &setup.editor.el_classic, "editor.el_classic"
9748 &setup.editor.el_custom, "editor.el_custom"
9752 &setup.editor.el_user_defined, "editor.el_user_defined"
9756 &setup.editor.el_dynamic, "editor.el_dynamic"
9760 &setup.editor.el_headlines, "editor.el_headlines"
9764 &setup.editor.show_element_token, "editor.show_element_token"
9768 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9772 static struct TokenInfo editor_cascade_setup_tokens[] =
9776 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9780 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9784 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9788 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9792 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9796 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9800 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9804 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9808 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9812 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9816 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9820 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9824 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9828 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9832 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9836 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9840 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9844 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9848 static struct TokenInfo shortcut_setup_tokens[] =
9852 &setup.shortcut.save_game, "shortcut.save_game"
9856 &setup.shortcut.load_game, "shortcut.load_game"
9860 &setup.shortcut.restart_game, "shortcut.restart_game"
9864 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9868 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9872 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9876 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9880 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9884 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9888 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9892 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9896 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9900 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9904 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9908 &setup.shortcut.tape_record, "shortcut.tape_record"
9912 &setup.shortcut.tape_play, "shortcut.tape_play"
9916 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9920 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9924 &setup.shortcut.sound_music, "shortcut.sound_music"
9928 &setup.shortcut.snap_left, "shortcut.snap_left"
9932 &setup.shortcut.snap_right, "shortcut.snap_right"
9936 &setup.shortcut.snap_up, "shortcut.snap_up"
9940 &setup.shortcut.snap_down, "shortcut.snap_down"
9944 static struct SetupInputInfo setup_input;
9945 static struct TokenInfo player_setup_tokens[] =
9949 &setup_input.use_joystick, ".use_joystick"
9953 &setup_input.joy.device_name, ".joy.device_name"
9957 &setup_input.joy.xleft, ".joy.xleft"
9961 &setup_input.joy.xmiddle, ".joy.xmiddle"
9965 &setup_input.joy.xright, ".joy.xright"
9969 &setup_input.joy.yupper, ".joy.yupper"
9973 &setup_input.joy.ymiddle, ".joy.ymiddle"
9977 &setup_input.joy.ylower, ".joy.ylower"
9981 &setup_input.joy.snap, ".joy.snap_field"
9985 &setup_input.joy.drop, ".joy.place_bomb"
9989 &setup_input.key.left, ".key.move_left"
9993 &setup_input.key.right, ".key.move_right"
9997 &setup_input.key.up, ".key.move_up"
10001 &setup_input.key.down, ".key.move_down"
10005 &setup_input.key.snap, ".key.snap_field"
10009 &setup_input.key.drop, ".key.place_bomb"
10013 static struct TokenInfo system_setup_tokens[] =
10017 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10021 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10025 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10029 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10033 static struct TokenInfo internal_setup_tokens[] =
10037 &setup.internal.program_title, "program_title"
10041 &setup.internal.program_version, "program_version"
10045 &setup.internal.program_author, "program_author"
10049 &setup.internal.program_email, "program_email"
10053 &setup.internal.program_website, "program_website"
10057 &setup.internal.program_copyright, "program_copyright"
10061 &setup.internal.program_company, "program_company"
10065 &setup.internal.program_icon_file, "program_icon_file"
10069 &setup.internal.default_graphics_set, "default_graphics_set"
10073 &setup.internal.default_sounds_set, "default_sounds_set"
10077 &setup.internal.default_music_set, "default_music_set"
10081 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10085 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10089 &setup.internal.fallback_music_file, "fallback_music_file"
10093 &setup.internal.default_level_series, "default_level_series"
10097 &setup.internal.default_window_width, "default_window_width"
10101 &setup.internal.default_window_height, "default_window_height"
10105 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10109 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10113 &setup.internal.create_user_levelset, "create_user_levelset"
10117 &setup.internal.menu_game, "menu_game"
10121 &setup.internal.menu_editor, "menu_editor"
10125 &setup.internal.menu_graphics, "menu_graphics"
10129 &setup.internal.menu_sound, "menu_sound"
10133 &setup.internal.menu_artwork, "menu_artwork"
10137 &setup.internal.menu_input, "menu_input"
10141 &setup.internal.menu_touch, "menu_touch"
10145 &setup.internal.menu_shortcuts, "menu_shortcuts"
10149 &setup.internal.menu_exit, "menu_exit"
10153 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10157 &setup.internal.info_title, "info_title"
10161 &setup.internal.info_elements, "info_elements"
10165 &setup.internal.info_music, "info_music"
10169 &setup.internal.info_credits, "info_credits"
10173 &setup.internal.info_program, "info_program"
10177 &setup.internal.info_version, "info_version"
10181 &setup.internal.info_levelset, "info_levelset"
10185 &setup.internal.info_exit, "info_exit"
10189 static struct TokenInfo debug_setup_tokens[] =
10193 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10197 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10201 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10205 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10209 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10213 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10217 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10221 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10225 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10229 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10233 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10237 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10241 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10245 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10249 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10253 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10257 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10261 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10265 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10269 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10273 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10276 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10280 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10284 &setup.debug.xsn_mode, "debug.xsn_mode"
10288 &setup.debug.xsn_percent, "debug.xsn_percent"
10292 static struct TokenInfo options_setup_tokens[] =
10296 &setup.options.verbose, "options.verbose"
10300 static void setSetupInfoToDefaults(struct SetupInfo *si)
10304 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10306 si->multiple_users = TRUE;
10309 si->sound_loops = TRUE;
10310 si->sound_music = TRUE;
10311 si->sound_simple = TRUE;
10313 si->scroll_delay = TRUE;
10314 si->forced_scroll_delay = FALSE;
10315 si->scroll_delay_value = STD_SCROLL_DELAY;
10316 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10317 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10318 si->fade_screens = TRUE;
10319 si->autorecord = TRUE;
10320 si->auto_pause_on_start = FALSE;
10321 si->show_titlescreen = TRUE;
10322 si->quick_doors = FALSE;
10323 si->team_mode = FALSE;
10324 si->handicap = TRUE;
10325 si->skip_levels = TRUE;
10326 si->increment_levels = TRUE;
10327 si->auto_play_next_level = TRUE;
10328 si->count_score_after_game = TRUE;
10329 si->show_scores_after_game = TRUE;
10330 si->time_limit = TRUE;
10331 si->fullscreen = FALSE;
10332 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10333 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10334 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10335 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10336 si->ask_on_escape = TRUE;
10337 si->ask_on_escape_editor = TRUE;
10338 si->ask_on_game_over = TRUE;
10339 si->ask_on_quit_game = TRUE;
10340 si->ask_on_quit_program = TRUE;
10341 si->quick_switch = FALSE;
10342 si->input_on_focus = FALSE;
10343 si->prefer_aga_graphics = TRUE;
10344 si->prefer_lowpass_sounds = FALSE;
10345 si->prefer_extra_panel_items = TRUE;
10346 si->game_speed_extended = FALSE;
10347 si->game_frame_delay = GAME_FRAME_DELAY;
10348 si->sp_show_border_elements = FALSE;
10349 si->small_game_graphics = FALSE;
10350 si->show_load_save_buttons = FALSE;
10351 si->show_undo_redo_buttons = FALSE;
10352 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10354 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10355 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10356 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10358 si->override_level_graphics = FALSE;
10359 si->override_level_sounds = FALSE;
10360 si->override_level_music = FALSE;
10362 si->volume_simple = 100; // percent
10363 si->volume_loops = 100; // percent
10364 si->volume_music = 100; // percent
10366 si->network_mode = FALSE;
10367 si->network_player_nr = 0; // first player
10368 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10370 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10371 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10372 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10373 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10374 si->touch.draw_outlined = TRUE;
10375 si->touch.draw_pressed = TRUE;
10377 for (i = 0; i < 2; i++)
10379 char *default_grid_button[6][2] =
10385 { "111222", " vv " },
10386 { "111222", " vv " }
10388 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10389 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10390 int min_xsize = MIN(6, grid_xsize);
10391 int min_ysize = MIN(6, grid_ysize);
10392 int startx = grid_xsize - min_xsize;
10393 int starty = grid_ysize - min_ysize;
10396 // virtual buttons grid can only be set to defaults if video is initialized
10397 // (this will be repeated if virtual buttons are not loaded from setup file)
10398 if (video.initialized)
10400 si->touch.grid_xsize[i] = grid_xsize;
10401 si->touch.grid_ysize[i] = grid_ysize;
10405 si->touch.grid_xsize[i] = -1;
10406 si->touch.grid_ysize[i] = -1;
10409 for (x = 0; x < MAX_GRID_XSIZE; x++)
10410 for (y = 0; y < MAX_GRID_YSIZE; y++)
10411 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10413 for (x = 0; x < min_xsize; x++)
10414 for (y = 0; y < min_ysize; y++)
10415 si->touch.grid_button[i][x][starty + y] =
10416 default_grid_button[y][0][x];
10418 for (x = 0; x < min_xsize; x++)
10419 for (y = 0; y < min_ysize; y++)
10420 si->touch.grid_button[i][startx + x][starty + y] =
10421 default_grid_button[y][1][x];
10424 si->touch.grid_initialized = video.initialized;
10426 si->touch.overlay_buttons = FALSE;
10428 si->editor.el_boulderdash = TRUE;
10429 si->editor.el_emerald_mine = TRUE;
10430 si->editor.el_emerald_mine_club = TRUE;
10431 si->editor.el_more = TRUE;
10432 si->editor.el_sokoban = TRUE;
10433 si->editor.el_supaplex = TRUE;
10434 si->editor.el_diamond_caves = TRUE;
10435 si->editor.el_dx_boulderdash = TRUE;
10437 si->editor.el_mirror_magic = TRUE;
10438 si->editor.el_deflektor = TRUE;
10440 si->editor.el_chars = TRUE;
10441 si->editor.el_steel_chars = TRUE;
10443 si->editor.el_classic = TRUE;
10444 si->editor.el_custom = TRUE;
10446 si->editor.el_user_defined = FALSE;
10447 si->editor.el_dynamic = TRUE;
10449 si->editor.el_headlines = TRUE;
10451 si->editor.show_element_token = FALSE;
10453 si->editor.show_read_only_warning = TRUE;
10455 si->editor.use_template_for_new_levels = TRUE;
10457 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10458 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10459 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10460 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10461 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10463 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10464 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10465 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10466 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10467 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10469 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10470 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10471 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10472 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10473 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10474 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10476 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10477 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10478 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10480 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10481 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10482 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10483 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10485 for (i = 0; i < MAX_PLAYERS; i++)
10487 si->input[i].use_joystick = FALSE;
10488 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10489 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10490 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10491 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10492 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10493 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10494 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10495 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10496 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10497 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10498 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10499 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10500 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10501 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10502 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10505 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10506 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10507 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10508 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10510 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10511 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10512 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10513 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10514 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10515 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10516 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10518 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10520 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10521 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10522 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10524 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10525 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10526 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10528 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10529 si->internal.choose_from_top_leveldir = FALSE;
10530 si->internal.show_scaling_in_title = TRUE;
10531 si->internal.create_user_levelset = TRUE;
10533 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10534 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10536 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10537 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10538 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10539 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10540 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10541 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10542 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10543 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10544 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10545 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10547 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10548 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10549 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10550 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10551 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10552 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10553 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10554 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10555 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10556 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10558 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10559 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10561 si->debug.show_frames_per_second = FALSE;
10563 si->debug.xsn_mode = AUTO;
10564 si->debug.xsn_percent = 0;
10566 si->options.verbose = FALSE;
10568 #if defined(PLATFORM_ANDROID)
10569 si->fullscreen = TRUE;
10570 si->touch.overlay_buttons = TRUE;
10573 setHideSetupEntry(&setup.debug.xsn_mode);
10576 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10578 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10581 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10583 si->player_uuid = NULL; // (will be set later)
10584 si->player_version = 1; // (will be set later)
10586 si->use_api_server = TRUE;
10587 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10588 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10589 si->ask_for_uploading_tapes = TRUE;
10590 si->ask_for_remaining_tapes = FALSE;
10591 si->provide_uploading_tapes = TRUE;
10592 si->ask_for_using_api_server = TRUE;
10593 si->has_remaining_tapes = FALSE;
10596 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10598 si->editor_cascade.el_bd = TRUE;
10599 si->editor_cascade.el_em = TRUE;
10600 si->editor_cascade.el_emc = TRUE;
10601 si->editor_cascade.el_rnd = TRUE;
10602 si->editor_cascade.el_sb = TRUE;
10603 si->editor_cascade.el_sp = TRUE;
10604 si->editor_cascade.el_dc = TRUE;
10605 si->editor_cascade.el_dx = TRUE;
10607 si->editor_cascade.el_mm = TRUE;
10608 si->editor_cascade.el_df = TRUE;
10610 si->editor_cascade.el_chars = FALSE;
10611 si->editor_cascade.el_steel_chars = FALSE;
10612 si->editor_cascade.el_ce = FALSE;
10613 si->editor_cascade.el_ge = FALSE;
10614 si->editor_cascade.el_es = FALSE;
10615 si->editor_cascade.el_ref = FALSE;
10616 si->editor_cascade.el_user = FALSE;
10617 si->editor_cascade.el_dynamic = FALSE;
10620 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10622 static char *getHideSetupToken(void *setup_value)
10624 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10626 if (setup_value != NULL)
10627 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10629 return hide_setup_token;
10632 void setHideSetupEntry(void *setup_value)
10634 char *hide_setup_token = getHideSetupToken(setup_value);
10636 if (hide_setup_hash == NULL)
10637 hide_setup_hash = newSetupFileHash();
10639 if (setup_value != NULL)
10640 setHashEntry(hide_setup_hash, hide_setup_token, "");
10643 void removeHideSetupEntry(void *setup_value)
10645 char *hide_setup_token = getHideSetupToken(setup_value);
10647 if (setup_value != NULL)
10648 removeHashEntry(hide_setup_hash, hide_setup_token);
10651 boolean hideSetupEntry(void *setup_value)
10653 char *hide_setup_token = getHideSetupToken(setup_value);
10655 return (setup_value != NULL &&
10656 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10659 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10660 struct TokenInfo *token_info,
10661 int token_nr, char *token_text)
10663 char *token_hide_text = getStringCat2(token_text, ".hide");
10664 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10666 // set the value of this setup option in the setup option structure
10667 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10669 // check if this setup option should be hidden in the setup menu
10670 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10671 setHideSetupEntry(token_info[token_nr].value);
10673 free(token_hide_text);
10676 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10677 struct TokenInfo *token_info,
10680 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10681 token_info[token_nr].text);
10684 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10688 if (!setup_file_hash)
10691 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10692 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10694 setup.touch.grid_initialized = TRUE;
10695 for (i = 0; i < 2; i++)
10697 int grid_xsize = setup.touch.grid_xsize[i];
10698 int grid_ysize = setup.touch.grid_ysize[i];
10701 // if virtual buttons are not loaded from setup file, repeat initializing
10702 // virtual buttons grid with default values later when video is initialized
10703 if (grid_xsize == -1 ||
10706 setup.touch.grid_initialized = FALSE;
10711 for (y = 0; y < grid_ysize; y++)
10713 char token_string[MAX_LINE_LEN];
10715 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10717 char *value_string = getHashEntry(setup_file_hash, token_string);
10719 if (value_string == NULL)
10722 for (x = 0; x < grid_xsize; x++)
10724 char c = value_string[x];
10726 setup.touch.grid_button[i][x][y] =
10727 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10732 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10733 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10735 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10736 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10738 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10742 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10744 setup_input = setup.input[pnr];
10745 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10747 char full_token[100];
10749 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10750 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10753 setup.input[pnr] = setup_input;
10756 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10757 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10759 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10760 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10762 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10763 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10765 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10766 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10768 setHideRelatedSetupEntries();
10771 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10775 if (!setup_file_hash)
10778 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10779 setSetupInfo(auto_setup_tokens, i,
10780 getHashEntry(setup_file_hash,
10781 auto_setup_tokens[i].text));
10784 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10788 if (!setup_file_hash)
10791 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10792 setSetupInfo(server_setup_tokens, i,
10793 getHashEntry(setup_file_hash,
10794 server_setup_tokens[i].text));
10797 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10801 if (!setup_file_hash)
10804 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10805 setSetupInfo(editor_cascade_setup_tokens, i,
10806 getHashEntry(setup_file_hash,
10807 editor_cascade_setup_tokens[i].text));
10810 void LoadUserNames(void)
10812 int last_user_nr = user.nr;
10815 if (global.user_names != NULL)
10817 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10818 checked_free(global.user_names[i]);
10820 checked_free(global.user_names);
10823 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10825 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10829 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10831 if (setup_file_hash)
10833 char *player_name = getHashEntry(setup_file_hash, "player_name");
10835 global.user_names[i] = getFixedUserName(player_name);
10837 freeSetupFileHash(setup_file_hash);
10840 if (global.user_names[i] == NULL)
10841 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10844 user.nr = last_user_nr;
10847 void LoadSetupFromFilename(char *filename)
10849 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10851 if (setup_file_hash)
10853 decodeSetupFileHash_Default(setup_file_hash);
10855 freeSetupFileHash(setup_file_hash);
10859 Debug("setup", "using default setup values");
10863 static void LoadSetup_SpecialPostProcessing(void)
10865 char *player_name_new;
10867 // needed to work around problems with fixed length strings
10868 player_name_new = getFixedUserName(setup.player_name);
10869 free(setup.player_name);
10870 setup.player_name = player_name_new;
10872 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10873 if (setup.scroll_delay == FALSE)
10875 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10876 setup.scroll_delay = TRUE; // now always "on"
10879 // make sure that scroll delay value stays inside valid range
10880 setup.scroll_delay_value =
10881 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10884 void LoadSetup_Default(void)
10888 // always start with reliable default values
10889 setSetupInfoToDefaults(&setup);
10891 // try to load setup values from default setup file
10892 filename = getDefaultSetupFilename();
10894 if (fileExists(filename))
10895 LoadSetupFromFilename(filename);
10897 // try to load setup values from platform setup file
10898 filename = getPlatformSetupFilename();
10900 if (fileExists(filename))
10901 LoadSetupFromFilename(filename);
10903 // try to load setup values from user setup file
10904 filename = getSetupFilename();
10906 LoadSetupFromFilename(filename);
10908 LoadSetup_SpecialPostProcessing();
10911 void LoadSetup_AutoSetup(void)
10913 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10914 SetupFileHash *setup_file_hash = NULL;
10916 // always start with reliable default values
10917 setSetupInfoToDefaults_AutoSetup(&setup);
10919 setup_file_hash = loadSetupFileHash(filename);
10921 if (setup_file_hash)
10923 decodeSetupFileHash_AutoSetup(setup_file_hash);
10925 freeSetupFileHash(setup_file_hash);
10931 void LoadSetup_ServerSetup(void)
10933 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10934 SetupFileHash *setup_file_hash = NULL;
10936 // always start with reliable default values
10937 setSetupInfoToDefaults_ServerSetup(&setup);
10939 setup_file_hash = loadSetupFileHash(filename);
10941 if (setup_file_hash)
10943 decodeSetupFileHash_ServerSetup(setup_file_hash);
10945 freeSetupFileHash(setup_file_hash);
10950 if (setup.player_uuid == NULL)
10952 // player UUID does not yet exist in setup file
10953 setup.player_uuid = getStringCopy(getUUID());
10954 setup.player_version = 2;
10956 SaveSetup_ServerSetup();
10960 void LoadSetup_EditorCascade(void)
10962 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10963 SetupFileHash *setup_file_hash = NULL;
10965 // always start with reliable default values
10966 setSetupInfoToDefaults_EditorCascade(&setup);
10968 setup_file_hash = loadSetupFileHash(filename);
10970 if (setup_file_hash)
10972 decodeSetupFileHash_EditorCascade(setup_file_hash);
10974 freeSetupFileHash(setup_file_hash);
10980 void LoadSetup(void)
10982 LoadSetup_Default();
10983 LoadSetup_AutoSetup();
10984 LoadSetup_ServerSetup();
10985 LoadSetup_EditorCascade();
10988 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10989 char *mapping_line)
10991 char mapping_guid[MAX_LINE_LEN];
10992 char *mapping_start, *mapping_end;
10994 // get GUID from game controller mapping line: copy complete line
10995 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10996 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10998 // get GUID from game controller mapping line: cut after GUID part
10999 mapping_start = strchr(mapping_guid, ',');
11000 if (mapping_start != NULL)
11001 *mapping_start = '\0';
11003 // cut newline from game controller mapping line
11004 mapping_end = strchr(mapping_line, '\n');
11005 if (mapping_end != NULL)
11006 *mapping_end = '\0';
11008 // add mapping entry to game controller mappings hash
11009 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11012 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11017 if (!(file = fopen(filename, MODE_READ)))
11019 Warn("cannot read game controller mappings file '%s'", filename);
11024 while (!feof(file))
11026 char line[MAX_LINE_LEN];
11028 if (!fgets(line, MAX_LINE_LEN, file))
11031 addGameControllerMappingToHash(mappings_hash, line);
11037 void SaveSetup_Default(void)
11039 char *filename = getSetupFilename();
11043 InitUserDataDirectory();
11045 if (!(file = fopen(filename, MODE_WRITE)))
11047 Warn("cannot write setup file '%s'", filename);
11052 fprintFileHeader(file, SETUP_FILENAME);
11054 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11056 // just to make things nicer :)
11057 if (global_setup_tokens[i].value == &setup.multiple_users ||
11058 global_setup_tokens[i].value == &setup.sound ||
11059 global_setup_tokens[i].value == &setup.graphics_set ||
11060 global_setup_tokens[i].value == &setup.volume_simple ||
11061 global_setup_tokens[i].value == &setup.network_mode ||
11062 global_setup_tokens[i].value == &setup.touch.control_type ||
11063 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11064 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11065 fprintf(file, "\n");
11067 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11070 for (i = 0; i < 2; i++)
11072 int grid_xsize = setup.touch.grid_xsize[i];
11073 int grid_ysize = setup.touch.grid_ysize[i];
11076 fprintf(file, "\n");
11078 for (y = 0; y < grid_ysize; y++)
11080 char token_string[MAX_LINE_LEN];
11081 char value_string[MAX_LINE_LEN];
11083 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11085 for (x = 0; x < grid_xsize; x++)
11087 char c = setup.touch.grid_button[i][x][y];
11089 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11092 value_string[grid_xsize] = '\0';
11094 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11098 fprintf(file, "\n");
11099 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11100 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11102 fprintf(file, "\n");
11103 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11104 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11106 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11110 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11111 fprintf(file, "\n");
11113 setup_input = setup.input[pnr];
11114 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11115 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11118 fprintf(file, "\n");
11119 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11120 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11122 // (internal setup values not saved to user setup file)
11124 fprintf(file, "\n");
11125 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11126 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11127 setup.debug.xsn_mode != AUTO)
11128 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11130 fprintf(file, "\n");
11131 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11132 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11136 SetFilePermissions(filename, PERMS_PRIVATE);
11139 void SaveSetup_AutoSetup(void)
11141 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11145 InitUserDataDirectory();
11147 if (!(file = fopen(filename, MODE_WRITE)))
11149 Warn("cannot write auto setup file '%s'", filename);
11156 fprintFileHeader(file, AUTOSETUP_FILENAME);
11158 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11159 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11163 SetFilePermissions(filename, PERMS_PRIVATE);
11168 void SaveSetup_ServerSetup(void)
11170 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11174 InitUserDataDirectory();
11176 if (!(file = fopen(filename, MODE_WRITE)))
11178 Warn("cannot write server setup file '%s'", filename);
11185 fprintFileHeader(file, SERVERSETUP_FILENAME);
11187 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11189 // just to make things nicer :)
11190 if (server_setup_tokens[i].value == &setup.use_api_server)
11191 fprintf(file, "\n");
11193 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11198 SetFilePermissions(filename, PERMS_PRIVATE);
11203 void SaveSetup_EditorCascade(void)
11205 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11209 InitUserDataDirectory();
11211 if (!(file = fopen(filename, MODE_WRITE)))
11213 Warn("cannot write editor cascade state file '%s'", filename);
11220 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11222 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11223 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11227 SetFilePermissions(filename, PERMS_PRIVATE);
11232 void SaveSetup(void)
11234 SaveSetup_Default();
11235 SaveSetup_AutoSetup();
11236 SaveSetup_ServerSetup();
11237 SaveSetup_EditorCascade();
11240 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11245 if (!(file = fopen(filename, MODE_WRITE)))
11247 Warn("cannot write game controller mappings file '%s'", filename);
11252 BEGIN_HASH_ITERATION(mappings_hash, itr)
11254 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11256 END_HASH_ITERATION(mappings_hash, itr)
11261 void SaveSetup_AddGameControllerMapping(char *mapping)
11263 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11264 SetupFileHash *mappings_hash = newSetupFileHash();
11266 InitUserDataDirectory();
11268 // load existing personal game controller mappings
11269 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11271 // add new mapping to personal game controller mappings
11272 addGameControllerMappingToHash(mappings_hash, mapping);
11274 // save updated personal game controller mappings
11275 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11277 freeSetupFileHash(mappings_hash);
11281 void LoadCustomElementDescriptions(void)
11283 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11284 SetupFileHash *setup_file_hash;
11287 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11289 if (element_info[i].custom_description != NULL)
11291 free(element_info[i].custom_description);
11292 element_info[i].custom_description = NULL;
11296 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11299 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11301 char *token = getStringCat2(element_info[i].token_name, ".name");
11302 char *value = getHashEntry(setup_file_hash, token);
11305 element_info[i].custom_description = getStringCopy(value);
11310 freeSetupFileHash(setup_file_hash);
11313 static int getElementFromToken(char *token)
11315 char *value = getHashEntry(element_token_hash, token);
11318 return atoi(value);
11320 Warn("unknown element token '%s'", token);
11322 return EL_UNDEFINED;
11325 void FreeGlobalAnimEventInfo(void)
11327 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11329 if (gaei->event_list == NULL)
11334 for (i = 0; i < gaei->num_event_lists; i++)
11336 checked_free(gaei->event_list[i]->event_value);
11337 checked_free(gaei->event_list[i]);
11340 checked_free(gaei->event_list);
11342 gaei->event_list = NULL;
11343 gaei->num_event_lists = 0;
11346 static int AddGlobalAnimEventList(void)
11348 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11349 int list_pos = gaei->num_event_lists++;
11351 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11352 sizeof(struct GlobalAnimEventListInfo *));
11354 gaei->event_list[list_pos] =
11355 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11357 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11359 gaeli->event_value = NULL;
11360 gaeli->num_event_values = 0;
11365 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11367 // do not add empty global animation events
11368 if (event_value == ANIM_EVENT_NONE)
11371 // if list position is undefined, create new list
11372 if (list_pos == ANIM_EVENT_UNDEFINED)
11373 list_pos = AddGlobalAnimEventList();
11375 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11376 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11377 int value_pos = gaeli->num_event_values++;
11379 gaeli->event_value = checked_realloc(gaeli->event_value,
11380 gaeli->num_event_values * sizeof(int *));
11382 gaeli->event_value[value_pos] = event_value;
11387 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11389 if (list_pos == ANIM_EVENT_UNDEFINED)
11390 return ANIM_EVENT_NONE;
11392 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11393 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11395 return gaeli->event_value[value_pos];
11398 int GetGlobalAnimEventValueCount(int list_pos)
11400 if (list_pos == ANIM_EVENT_UNDEFINED)
11403 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11404 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11406 return gaeli->num_event_values;
11409 // This function checks if a string <s> of the format "string1, string2, ..."
11410 // exactly contains a string <s_contained>.
11412 static boolean string_has_parameter(char *s, char *s_contained)
11416 if (s == NULL || s_contained == NULL)
11419 if (strlen(s_contained) > strlen(s))
11422 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11424 char next_char = s[strlen(s_contained)];
11426 // check if next character is delimiter or whitespace
11427 return (next_char == ',' || next_char == '\0' ||
11428 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11431 // check if string contains another parameter string after a comma
11432 substring = strchr(s, ',');
11433 if (substring == NULL) // string does not contain a comma
11436 // advance string pointer to next character after the comma
11439 // skip potential whitespaces after the comma
11440 while (*substring == ' ' || *substring == '\t')
11443 return string_has_parameter(substring, s_contained);
11446 static int get_anim_parameter_value(char *s)
11448 int event_value[] =
11456 char *pattern_1[] =
11464 char *pattern_2 = ".part_";
11465 char *matching_char = NULL;
11467 int pattern_1_len = 0;
11468 int result = ANIM_EVENT_NONE;
11471 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11473 matching_char = strstr(s_ptr, pattern_1[i]);
11474 pattern_1_len = strlen(pattern_1[i]);
11475 result = event_value[i];
11477 if (matching_char != NULL)
11481 if (matching_char == NULL)
11482 return ANIM_EVENT_NONE;
11484 s_ptr = matching_char + pattern_1_len;
11486 // check for main animation number ("anim_X" or "anim_XX")
11487 if (*s_ptr >= '0' && *s_ptr <= '9')
11489 int gic_anim_nr = (*s_ptr++ - '0');
11491 if (*s_ptr >= '0' && *s_ptr <= '9')
11492 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11494 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11495 return ANIM_EVENT_NONE;
11497 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11501 // invalid main animation number specified
11503 return ANIM_EVENT_NONE;
11506 // check for animation part number ("part_X" or "part_XX") (optional)
11507 if (strPrefix(s_ptr, pattern_2))
11509 s_ptr += strlen(pattern_2);
11511 if (*s_ptr >= '0' && *s_ptr <= '9')
11513 int gic_part_nr = (*s_ptr++ - '0');
11515 if (*s_ptr >= '0' && *s_ptr <= '9')
11516 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11518 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11519 return ANIM_EVENT_NONE;
11521 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11525 // invalid animation part number specified
11527 return ANIM_EVENT_NONE;
11531 // discard result if next character is neither delimiter nor whitespace
11532 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11533 *s_ptr == ' ' || *s_ptr == '\t'))
11534 return ANIM_EVENT_NONE;
11539 static int get_anim_parameter_values(char *s)
11541 int list_pos = ANIM_EVENT_UNDEFINED;
11542 int event_value = ANIM_EVENT_DEFAULT;
11544 if (string_has_parameter(s, "any"))
11545 event_value |= ANIM_EVENT_ANY;
11547 if (string_has_parameter(s, "click:self") ||
11548 string_has_parameter(s, "click") ||
11549 string_has_parameter(s, "self"))
11550 event_value |= ANIM_EVENT_SELF;
11552 if (string_has_parameter(s, "unclick:any"))
11553 event_value |= ANIM_EVENT_UNCLICK_ANY;
11555 // if animation event found, add it to global animation event list
11556 if (event_value != ANIM_EVENT_NONE)
11557 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11561 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11562 event_value = get_anim_parameter_value(s);
11564 // if animation event found, add it to global animation event list
11565 if (event_value != ANIM_EVENT_NONE)
11566 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11568 // continue with next part of the string, starting with next comma
11569 s = strchr(s + 1, ',');
11575 static int get_anim_action_parameter_value(char *token)
11577 // check most common default case first to massively speed things up
11578 if (strEqual(token, ARG_UNDEFINED))
11579 return ANIM_EVENT_ACTION_NONE;
11581 int result = getImageIDFromToken(token);
11585 char *gfx_token = getStringCat2("gfx.", token);
11587 result = getImageIDFromToken(gfx_token);
11589 checked_free(gfx_token);
11594 Key key = getKeyFromX11KeyName(token);
11596 if (key != KSYM_UNDEFINED)
11597 result = -(int)key;
11604 result = get_hash_from_key(token); // unsigned int => int
11605 result = ABS(result); // may be negative now
11606 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11608 setHashEntry(anim_url_hash, int2str(result, 0), token);
11613 result = ANIM_EVENT_ACTION_NONE;
11618 int get_parameter_value(char *value_raw, char *suffix, int type)
11620 char *value = getStringToLower(value_raw);
11621 int result = 0; // probably a save default value
11623 if (strEqual(suffix, ".direction"))
11625 result = (strEqual(value, "left") ? MV_LEFT :
11626 strEqual(value, "right") ? MV_RIGHT :
11627 strEqual(value, "up") ? MV_UP :
11628 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11630 else if (strEqual(suffix, ".position"))
11632 result = (strEqual(value, "left") ? POS_LEFT :
11633 strEqual(value, "right") ? POS_RIGHT :
11634 strEqual(value, "top") ? POS_TOP :
11635 strEqual(value, "upper") ? POS_UPPER :
11636 strEqual(value, "middle") ? POS_MIDDLE :
11637 strEqual(value, "lower") ? POS_LOWER :
11638 strEqual(value, "bottom") ? POS_BOTTOM :
11639 strEqual(value, "any") ? POS_ANY :
11640 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11642 else if (strEqual(suffix, ".align"))
11644 result = (strEqual(value, "left") ? ALIGN_LEFT :
11645 strEqual(value, "right") ? ALIGN_RIGHT :
11646 strEqual(value, "center") ? ALIGN_CENTER :
11647 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11649 else if (strEqual(suffix, ".valign"))
11651 result = (strEqual(value, "top") ? VALIGN_TOP :
11652 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11653 strEqual(value, "middle") ? VALIGN_MIDDLE :
11654 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11656 else if (strEqual(suffix, ".anim_mode"))
11658 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11659 string_has_parameter(value, "loop") ? ANIM_LOOP :
11660 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11661 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11662 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11663 string_has_parameter(value, "random") ? ANIM_RANDOM :
11664 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11665 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11666 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11667 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11668 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11669 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11670 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11671 string_has_parameter(value, "all") ? ANIM_ALL :
11672 string_has_parameter(value, "tiled") ? ANIM_TILED :
11675 if (string_has_parameter(value, "once"))
11676 result |= ANIM_ONCE;
11678 if (string_has_parameter(value, "reverse"))
11679 result |= ANIM_REVERSE;
11681 if (string_has_parameter(value, "opaque_player"))
11682 result |= ANIM_OPAQUE_PLAYER;
11684 if (string_has_parameter(value, "static_panel"))
11685 result |= ANIM_STATIC_PANEL;
11687 else if (strEqual(suffix, ".init_event") ||
11688 strEqual(suffix, ".anim_event"))
11690 result = get_anim_parameter_values(value);
11692 else if (strEqual(suffix, ".init_delay_action") ||
11693 strEqual(suffix, ".anim_delay_action") ||
11694 strEqual(suffix, ".post_delay_action") ||
11695 strEqual(suffix, ".init_event_action") ||
11696 strEqual(suffix, ".anim_event_action"))
11698 result = get_anim_action_parameter_value(value_raw);
11700 else if (strEqual(suffix, ".class"))
11702 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11703 get_hash_from_key(value));
11705 else if (strEqual(suffix, ".style"))
11707 result = STYLE_DEFAULT;
11709 if (string_has_parameter(value, "accurate_borders"))
11710 result |= STYLE_ACCURATE_BORDERS;
11712 if (string_has_parameter(value, "inner_corners"))
11713 result |= STYLE_INNER_CORNERS;
11715 if (string_has_parameter(value, "reverse"))
11716 result |= STYLE_REVERSE;
11718 if (string_has_parameter(value, "leftmost_position"))
11719 result |= STYLE_LEFTMOST_POSITION;
11721 if (string_has_parameter(value, "block_clicks"))
11722 result |= STYLE_BLOCK;
11724 if (string_has_parameter(value, "passthrough_clicks"))
11725 result |= STYLE_PASSTHROUGH;
11727 if (string_has_parameter(value, "multiple_actions"))
11728 result |= STYLE_MULTIPLE_ACTIONS;
11730 else if (strEqual(suffix, ".fade_mode"))
11732 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11733 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11734 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11735 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11736 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11737 FADE_MODE_DEFAULT);
11739 else if (strEqual(suffix, ".auto_delay_unit"))
11741 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11742 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11743 AUTO_DELAY_UNIT_DEFAULT);
11745 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11747 result = gfx.get_font_from_token_function(value);
11749 else // generic parameter of type integer or boolean
11751 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11752 type == TYPE_INTEGER ? get_integer_from_string(value) :
11753 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11754 ARG_UNDEFINED_VALUE);
11762 static int get_token_parameter_value(char *token, char *value_raw)
11766 if (token == NULL || value_raw == NULL)
11767 return ARG_UNDEFINED_VALUE;
11769 suffix = strrchr(token, '.');
11770 if (suffix == NULL)
11773 if (strEqual(suffix, ".element"))
11774 return getElementFromToken(value_raw);
11776 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11777 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11780 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11781 boolean ignore_defaults)
11785 for (i = 0; image_config_vars[i].token != NULL; i++)
11787 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11789 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11790 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11794 *image_config_vars[i].value =
11795 get_token_parameter_value(image_config_vars[i].token, value);
11799 void InitMenuDesignSettings_Static(void)
11801 // always start with reliable default values from static default config
11802 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11805 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11809 // the following initializes hierarchical values from static configuration
11811 // special case: initialize "ARG_DEFAULT" values in static default config
11812 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11813 titlescreen_initial_first_default.fade_mode =
11814 title_initial_first_default.fade_mode;
11815 titlescreen_initial_first_default.fade_delay =
11816 title_initial_first_default.fade_delay;
11817 titlescreen_initial_first_default.post_delay =
11818 title_initial_first_default.post_delay;
11819 titlescreen_initial_first_default.auto_delay =
11820 title_initial_first_default.auto_delay;
11821 titlescreen_initial_first_default.auto_delay_unit =
11822 title_initial_first_default.auto_delay_unit;
11823 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11824 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11825 titlescreen_first_default.post_delay = title_first_default.post_delay;
11826 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11827 titlescreen_first_default.auto_delay_unit =
11828 title_first_default.auto_delay_unit;
11829 titlemessage_initial_first_default.fade_mode =
11830 title_initial_first_default.fade_mode;
11831 titlemessage_initial_first_default.fade_delay =
11832 title_initial_first_default.fade_delay;
11833 titlemessage_initial_first_default.post_delay =
11834 title_initial_first_default.post_delay;
11835 titlemessage_initial_first_default.auto_delay =
11836 title_initial_first_default.auto_delay;
11837 titlemessage_initial_first_default.auto_delay_unit =
11838 title_initial_first_default.auto_delay_unit;
11839 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11840 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11841 titlemessage_first_default.post_delay = title_first_default.post_delay;
11842 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11843 titlemessage_first_default.auto_delay_unit =
11844 title_first_default.auto_delay_unit;
11846 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11847 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11848 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11849 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11850 titlescreen_initial_default.auto_delay_unit =
11851 title_initial_default.auto_delay_unit;
11852 titlescreen_default.fade_mode = title_default.fade_mode;
11853 titlescreen_default.fade_delay = title_default.fade_delay;
11854 titlescreen_default.post_delay = title_default.post_delay;
11855 titlescreen_default.auto_delay = title_default.auto_delay;
11856 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11857 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11858 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11859 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11860 titlemessage_initial_default.auto_delay_unit =
11861 title_initial_default.auto_delay_unit;
11862 titlemessage_default.fade_mode = title_default.fade_mode;
11863 titlemessage_default.fade_delay = title_default.fade_delay;
11864 titlemessage_default.post_delay = title_default.post_delay;
11865 titlemessage_default.auto_delay = title_default.auto_delay;
11866 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11868 // special case: initialize "ARG_DEFAULT" values in static default config
11869 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11870 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11872 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11873 titlescreen_first[i] = titlescreen_first_default;
11874 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11875 titlemessage_first[i] = titlemessage_first_default;
11877 titlescreen_initial[i] = titlescreen_initial_default;
11878 titlescreen[i] = titlescreen_default;
11879 titlemessage_initial[i] = titlemessage_initial_default;
11880 titlemessage[i] = titlemessage_default;
11883 // special case: initialize "ARG_DEFAULT" values in static default config
11884 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11885 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11887 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11890 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11891 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11892 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11895 // special case: initialize "ARG_DEFAULT" values in static default config
11896 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11897 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11899 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11900 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11901 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11903 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11906 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11910 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11914 struct XY *dst, *src;
11916 game_buttons_xy[] =
11918 { &game.button.save, &game.button.stop },
11919 { &game.button.pause2, &game.button.pause },
11920 { &game.button.load, &game.button.play },
11921 { &game.button.undo, &game.button.stop },
11922 { &game.button.redo, &game.button.play },
11928 // special case: initialize later added SETUP list size from LEVELS value
11929 if (menu.list_size[GAME_MODE_SETUP] == -1)
11930 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11932 // set default position for snapshot buttons to stop/pause/play buttons
11933 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11934 if ((*game_buttons_xy[i].dst).x == -1 &&
11935 (*game_buttons_xy[i].dst).y == -1)
11936 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11938 // --------------------------------------------------------------------------
11939 // dynamic viewports (including playfield margins, borders and alignments)
11940 // --------------------------------------------------------------------------
11942 // dynamic viewports currently only supported for landscape mode
11943 int display_width = MAX(video.display_width, video.display_height);
11944 int display_height = MIN(video.display_width, video.display_height);
11946 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11948 struct RectWithBorder *vp_window = &viewport.window[i];
11949 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11950 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11951 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11952 boolean dynamic_window_width = (vp_window->min_width != -1);
11953 boolean dynamic_window_height = (vp_window->min_height != -1);
11954 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11955 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11957 // adjust window size if min/max width/height is specified
11959 if (vp_window->min_width != -1)
11961 int window_width = display_width;
11963 // when using static window height, use aspect ratio of display
11964 if (vp_window->min_height == -1)
11965 window_width = vp_window->height * display_width / display_height;
11967 vp_window->width = MAX(vp_window->min_width, window_width);
11970 if (vp_window->min_height != -1)
11972 int window_height = display_height;
11974 // when using static window width, use aspect ratio of display
11975 if (vp_window->min_width == -1)
11976 window_height = vp_window->width * display_height / display_width;
11978 vp_window->height = MAX(vp_window->min_height, window_height);
11981 if (vp_window->max_width != -1)
11982 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11984 if (vp_window->max_height != -1)
11985 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11987 int playfield_width = vp_window->width;
11988 int playfield_height = vp_window->height;
11990 // adjust playfield size and position according to specified margins
11992 playfield_width -= vp_playfield->margin_left;
11993 playfield_width -= vp_playfield->margin_right;
11995 playfield_height -= vp_playfield->margin_top;
11996 playfield_height -= vp_playfield->margin_bottom;
11998 // adjust playfield size if min/max width/height is specified
12000 if (vp_playfield->min_width != -1)
12001 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12003 if (vp_playfield->min_height != -1)
12004 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12006 if (vp_playfield->max_width != -1)
12007 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12009 if (vp_playfield->max_height != -1)
12010 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12012 // adjust playfield position according to specified alignment
12014 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12015 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12016 else if (vp_playfield->align == ALIGN_CENTER)
12017 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12018 else if (vp_playfield->align == ALIGN_RIGHT)
12019 vp_playfield->x += playfield_width - vp_playfield->width;
12021 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12022 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12023 else if (vp_playfield->valign == VALIGN_MIDDLE)
12024 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12025 else if (vp_playfield->valign == VALIGN_BOTTOM)
12026 vp_playfield->y += playfield_height - vp_playfield->height;
12028 vp_playfield->x += vp_playfield->margin_left;
12029 vp_playfield->y += vp_playfield->margin_top;
12031 // adjust individual playfield borders if only default border is specified
12033 if (vp_playfield->border_left == -1)
12034 vp_playfield->border_left = vp_playfield->border_size;
12035 if (vp_playfield->border_right == -1)
12036 vp_playfield->border_right = vp_playfield->border_size;
12037 if (vp_playfield->border_top == -1)
12038 vp_playfield->border_top = vp_playfield->border_size;
12039 if (vp_playfield->border_bottom == -1)
12040 vp_playfield->border_bottom = vp_playfield->border_size;
12042 // set dynamic playfield borders if borders are specified as undefined
12043 // (but only if window size was dynamic and playfield size was static)
12045 if (dynamic_window_width && !dynamic_playfield_width)
12047 if (vp_playfield->border_left == -1)
12049 vp_playfield->border_left = (vp_playfield->x -
12050 vp_playfield->margin_left);
12051 vp_playfield->x -= vp_playfield->border_left;
12052 vp_playfield->width += vp_playfield->border_left;
12055 if (vp_playfield->border_right == -1)
12057 vp_playfield->border_right = (vp_window->width -
12059 vp_playfield->width -
12060 vp_playfield->margin_right);
12061 vp_playfield->width += vp_playfield->border_right;
12065 if (dynamic_window_height && !dynamic_playfield_height)
12067 if (vp_playfield->border_top == -1)
12069 vp_playfield->border_top = (vp_playfield->y -
12070 vp_playfield->margin_top);
12071 vp_playfield->y -= vp_playfield->border_top;
12072 vp_playfield->height += vp_playfield->border_top;
12075 if (vp_playfield->border_bottom == -1)
12077 vp_playfield->border_bottom = (vp_window->height -
12079 vp_playfield->height -
12080 vp_playfield->margin_bottom);
12081 vp_playfield->height += vp_playfield->border_bottom;
12085 // adjust playfield size to be a multiple of a defined alignment tile size
12087 int align_size = vp_playfield->align_size;
12088 int playfield_xtiles = vp_playfield->width / align_size;
12089 int playfield_ytiles = vp_playfield->height / align_size;
12090 int playfield_width_corrected = playfield_xtiles * align_size;
12091 int playfield_height_corrected = playfield_ytiles * align_size;
12092 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12093 i == GFX_SPECIAL_ARG_EDITOR);
12095 if (is_playfield_mode &&
12096 dynamic_playfield_width &&
12097 vp_playfield->width != playfield_width_corrected)
12099 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12101 vp_playfield->width = playfield_width_corrected;
12103 if (vp_playfield->align == ALIGN_LEFT)
12105 vp_playfield->border_left += playfield_xdiff;
12107 else if (vp_playfield->align == ALIGN_RIGHT)
12109 vp_playfield->border_right += playfield_xdiff;
12111 else if (vp_playfield->align == ALIGN_CENTER)
12113 int border_left_diff = playfield_xdiff / 2;
12114 int border_right_diff = playfield_xdiff - border_left_diff;
12116 vp_playfield->border_left += border_left_diff;
12117 vp_playfield->border_right += border_right_diff;
12121 if (is_playfield_mode &&
12122 dynamic_playfield_height &&
12123 vp_playfield->height != playfield_height_corrected)
12125 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12127 vp_playfield->height = playfield_height_corrected;
12129 if (vp_playfield->valign == VALIGN_TOP)
12131 vp_playfield->border_top += playfield_ydiff;
12133 else if (vp_playfield->align == VALIGN_BOTTOM)
12135 vp_playfield->border_right += playfield_ydiff;
12137 else if (vp_playfield->align == VALIGN_MIDDLE)
12139 int border_top_diff = playfield_ydiff / 2;
12140 int border_bottom_diff = playfield_ydiff - border_top_diff;
12142 vp_playfield->border_top += border_top_diff;
12143 vp_playfield->border_bottom += border_bottom_diff;
12147 // adjust door positions according to specified alignment
12149 for (j = 0; j < 2; j++)
12151 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12153 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12154 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12155 else if (vp_door->align == ALIGN_CENTER)
12156 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12157 else if (vp_door->align == ALIGN_RIGHT)
12158 vp_door->x += vp_window->width - vp_door->width;
12160 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12161 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12162 else if (vp_door->valign == VALIGN_MIDDLE)
12163 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12164 else if (vp_door->valign == VALIGN_BOTTOM)
12165 vp_door->y += vp_window->height - vp_door->height;
12170 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12174 struct XYTileSize *dst, *src;
12177 editor_buttons_xy[] =
12180 &editor.button.element_left, &editor.palette.element_left,
12181 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12184 &editor.button.element_middle, &editor.palette.element_middle,
12185 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12188 &editor.button.element_right, &editor.palette.element_right,
12189 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12196 // set default position for element buttons to element graphics
12197 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12199 if ((*editor_buttons_xy[i].dst).x == -1 &&
12200 (*editor_buttons_xy[i].dst).y == -1)
12202 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12204 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12206 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12210 // adjust editor palette rows and columns if specified to be dynamic
12212 if (editor.palette.cols == -1)
12214 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12215 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12216 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12218 editor.palette.cols = (vp_width - sc_width) / bt_width;
12220 if (editor.palette.x == -1)
12222 int palette_width = editor.palette.cols * bt_width + sc_width;
12224 editor.palette.x = (vp_width - palette_width) / 2;
12228 if (editor.palette.rows == -1)
12230 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12231 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12232 int tx_height = getFontHeight(FONT_TEXT_2);
12234 editor.palette.rows = (vp_height - tx_height) / bt_height;
12236 if (editor.palette.y == -1)
12238 int palette_height = editor.palette.rows * bt_height + tx_height;
12240 editor.palette.y = (vp_height - palette_height) / 2;
12245 static void LoadMenuDesignSettingsFromFilename(char *filename)
12247 static struct TitleFadingInfo tfi;
12248 static struct TitleMessageInfo tmi;
12249 static struct TokenInfo title_tokens[] =
12251 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12252 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12253 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12254 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12255 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12259 static struct TokenInfo titlemessage_tokens[] =
12261 { TYPE_INTEGER, &tmi.x, ".x" },
12262 { TYPE_INTEGER, &tmi.y, ".y" },
12263 { TYPE_INTEGER, &tmi.width, ".width" },
12264 { TYPE_INTEGER, &tmi.height, ".height" },
12265 { TYPE_INTEGER, &tmi.chars, ".chars" },
12266 { TYPE_INTEGER, &tmi.lines, ".lines" },
12267 { TYPE_INTEGER, &tmi.align, ".align" },
12268 { TYPE_INTEGER, &tmi.valign, ".valign" },
12269 { TYPE_INTEGER, &tmi.font, ".font" },
12270 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12271 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12272 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12273 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12274 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12275 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12276 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12277 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12278 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12284 struct TitleFadingInfo *info;
12289 // initialize first titles from "enter screen" definitions, if defined
12290 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12291 { &title_first_default, "menu.enter_screen.TITLE" },
12293 // initialize title screens from "next screen" definitions, if defined
12294 { &title_initial_default, "menu.next_screen.TITLE" },
12295 { &title_default, "menu.next_screen.TITLE" },
12301 struct TitleMessageInfo *array;
12304 titlemessage_arrays[] =
12306 // initialize first titles from "enter screen" definitions, if defined
12307 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12308 { titlescreen_first, "menu.enter_screen.TITLE" },
12309 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12310 { titlemessage_first, "menu.enter_screen.TITLE" },
12312 // initialize titles from "next screen" definitions, if defined
12313 { titlescreen_initial, "menu.next_screen.TITLE" },
12314 { titlescreen, "menu.next_screen.TITLE" },
12315 { titlemessage_initial, "menu.next_screen.TITLE" },
12316 { titlemessage, "menu.next_screen.TITLE" },
12318 // overwrite titles with title definitions, if defined
12319 { titlescreen_initial_first, "[title_initial]" },
12320 { titlescreen_first, "[title]" },
12321 { titlemessage_initial_first, "[title_initial]" },
12322 { titlemessage_first, "[title]" },
12324 { titlescreen_initial, "[title_initial]" },
12325 { titlescreen, "[title]" },
12326 { titlemessage_initial, "[title_initial]" },
12327 { titlemessage, "[title]" },
12329 // overwrite titles with title screen/message definitions, if defined
12330 { titlescreen_initial_first, "[titlescreen_initial]" },
12331 { titlescreen_first, "[titlescreen]" },
12332 { titlemessage_initial_first, "[titlemessage_initial]" },
12333 { titlemessage_first, "[titlemessage]" },
12335 { titlescreen_initial, "[titlescreen_initial]" },
12336 { titlescreen, "[titlescreen]" },
12337 { titlemessage_initial, "[titlemessage_initial]" },
12338 { titlemessage, "[titlemessage]" },
12342 SetupFileHash *setup_file_hash;
12345 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12348 // the following initializes hierarchical values from dynamic configuration
12350 // special case: initialize with default values that may be overwritten
12351 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12352 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12354 struct TokenIntPtrInfo menu_config[] =
12356 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12357 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12358 { "menu.list_size", &menu.list_size[i] }
12361 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12363 char *token = menu_config[j].token;
12364 char *value = getHashEntry(setup_file_hash, token);
12367 *menu_config[j].value = get_integer_from_string(value);
12371 // special case: initialize with default values that may be overwritten
12372 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12373 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12375 struct TokenIntPtrInfo menu_config[] =
12377 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12378 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12379 { "menu.list_size.INFO", &menu.list_size_info[i] }
12382 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12384 char *token = menu_config[j].token;
12385 char *value = getHashEntry(setup_file_hash, token);
12388 *menu_config[j].value = get_integer_from_string(value);
12392 // special case: initialize with default values that may be overwritten
12393 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12394 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12396 struct TokenIntPtrInfo menu_config[] =
12398 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12399 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12402 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12404 char *token = menu_config[j].token;
12405 char *value = getHashEntry(setup_file_hash, token);
12408 *menu_config[j].value = get_integer_from_string(value);
12412 // special case: initialize with default values that may be overwritten
12413 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12414 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12416 struct TokenIntPtrInfo menu_config[] =
12418 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12419 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12420 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12421 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12422 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12423 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12424 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12425 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12426 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12429 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12431 char *token = menu_config[j].token;
12432 char *value = getHashEntry(setup_file_hash, token);
12435 *menu_config[j].value = get_integer_from_string(value);
12439 // special case: initialize with default values that may be overwritten
12440 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12441 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12443 struct TokenIntPtrInfo menu_config[] =
12445 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12446 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12447 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12448 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12449 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12450 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12451 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12452 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12453 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12456 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12458 char *token = menu_config[j].token;
12459 char *value = getHashEntry(setup_file_hash, token);
12462 *menu_config[j].value = get_token_parameter_value(token, value);
12466 // special case: initialize with default values that may be overwritten
12467 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12468 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12472 char *token_prefix;
12473 struct RectWithBorder *struct_ptr;
12477 { "viewport.window", &viewport.window[i] },
12478 { "viewport.playfield", &viewport.playfield[i] },
12479 { "viewport.door_1", &viewport.door_1[i] },
12480 { "viewport.door_2", &viewport.door_2[i] }
12483 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12485 struct TokenIntPtrInfo vp_config[] =
12487 { ".x", &vp_struct[j].struct_ptr->x },
12488 { ".y", &vp_struct[j].struct_ptr->y },
12489 { ".width", &vp_struct[j].struct_ptr->width },
12490 { ".height", &vp_struct[j].struct_ptr->height },
12491 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12492 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12493 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12494 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12495 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12496 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12497 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12498 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12499 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12500 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12501 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12502 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12503 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12504 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12505 { ".align", &vp_struct[j].struct_ptr->align },
12506 { ".valign", &vp_struct[j].struct_ptr->valign }
12509 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12511 char *token = getStringCat2(vp_struct[j].token_prefix,
12512 vp_config[k].token);
12513 char *value = getHashEntry(setup_file_hash, token);
12516 *vp_config[k].value = get_token_parameter_value(token, value);
12523 // special case: initialize with default values that may be overwritten
12524 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12525 for (i = 0; title_info[i].info != NULL; i++)
12527 struct TitleFadingInfo *info = title_info[i].info;
12528 char *base_token = title_info[i].text;
12530 for (j = 0; title_tokens[j].type != -1; j++)
12532 char *token = getStringCat2(base_token, title_tokens[j].text);
12533 char *value = getHashEntry(setup_file_hash, token);
12537 int parameter_value = get_token_parameter_value(token, value);
12541 *(int *)title_tokens[j].value = (int)parameter_value;
12550 // special case: initialize with default values that may be overwritten
12551 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12552 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12554 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12555 char *base_token = titlemessage_arrays[i].text;
12557 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12559 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12560 char *value = getHashEntry(setup_file_hash, token);
12564 int parameter_value = get_token_parameter_value(token, value);
12566 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12570 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12571 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12573 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12583 // special case: check if network and preview player positions are redefined,
12584 // to compare this later against the main menu level preview being redefined
12585 struct TokenIntPtrInfo menu_config_players[] =
12587 { "main.network_players.x", &menu.main.network_players.redefined },
12588 { "main.network_players.y", &menu.main.network_players.redefined },
12589 { "main.preview_players.x", &menu.main.preview_players.redefined },
12590 { "main.preview_players.y", &menu.main.preview_players.redefined },
12591 { "preview.x", &preview.redefined },
12592 { "preview.y", &preview.redefined }
12595 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12596 *menu_config_players[i].value = FALSE;
12598 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12599 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12600 *menu_config_players[i].value = TRUE;
12602 // read (and overwrite with) values that may be specified in config file
12603 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12605 freeSetupFileHash(setup_file_hash);
12608 void LoadMenuDesignSettings(void)
12610 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12612 InitMenuDesignSettings_Static();
12613 InitMenuDesignSettings_SpecialPreProcessing();
12615 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12617 // first look for special settings configured in level series config
12618 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12620 if (fileExists(filename_base))
12621 LoadMenuDesignSettingsFromFilename(filename_base);
12624 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12626 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12627 LoadMenuDesignSettingsFromFilename(filename_local);
12629 InitMenuDesignSettings_SpecialPostProcessing();
12632 void LoadMenuDesignSettings_AfterGraphics(void)
12634 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12637 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12639 char *filename = getEditorSetupFilename();
12640 SetupFileList *setup_file_list, *list;
12641 SetupFileHash *element_hash;
12642 int num_unknown_tokens = 0;
12645 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12648 element_hash = newSetupFileHash();
12650 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12651 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12653 // determined size may be larger than needed (due to unknown elements)
12655 for (list = setup_file_list; list != NULL; list = list->next)
12658 // add space for up to 3 more elements for padding that may be needed
12659 *num_elements += 3;
12661 // free memory for old list of elements, if needed
12662 checked_free(*elements);
12664 // allocate memory for new list of elements
12665 *elements = checked_malloc(*num_elements * sizeof(int));
12668 for (list = setup_file_list; list != NULL; list = list->next)
12670 char *value = getHashEntry(element_hash, list->token);
12672 if (value == NULL) // try to find obsolete token mapping
12674 char *mapped_token = get_mapped_token(list->token);
12676 if (mapped_token != NULL)
12678 value = getHashEntry(element_hash, mapped_token);
12680 free(mapped_token);
12686 (*elements)[(*num_elements)++] = atoi(value);
12690 if (num_unknown_tokens == 0)
12693 Warn("unknown token(s) found in config file:");
12694 Warn("- config file: '%s'", filename);
12696 num_unknown_tokens++;
12699 Warn("- token: '%s'", list->token);
12703 if (num_unknown_tokens > 0)
12706 while (*num_elements % 4) // pad with empty elements, if needed
12707 (*elements)[(*num_elements)++] = EL_EMPTY;
12709 freeSetupFileList(setup_file_list);
12710 freeSetupFileHash(element_hash);
12713 for (i = 0; i < *num_elements; i++)
12714 Debug("editor", "element '%s' [%d]\n",
12715 element_info[(*elements)[i]].token_name, (*elements)[i]);
12719 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12722 SetupFileHash *setup_file_hash = NULL;
12723 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12724 char *filename_music, *filename_prefix, *filename_info;
12730 token_to_value_ptr[] =
12732 { "title_header", &tmp_music_file_info.title_header },
12733 { "artist_header", &tmp_music_file_info.artist_header },
12734 { "album_header", &tmp_music_file_info.album_header },
12735 { "year_header", &tmp_music_file_info.year_header },
12737 { "title", &tmp_music_file_info.title },
12738 { "artist", &tmp_music_file_info.artist },
12739 { "album", &tmp_music_file_info.album },
12740 { "year", &tmp_music_file_info.year },
12746 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12747 getCustomMusicFilename(basename));
12749 if (filename_music == NULL)
12752 // ---------- try to replace file extension ----------
12754 filename_prefix = getStringCopy(filename_music);
12755 if (strrchr(filename_prefix, '.') != NULL)
12756 *strrchr(filename_prefix, '.') = '\0';
12757 filename_info = getStringCat2(filename_prefix, ".txt");
12759 if (fileExists(filename_info))
12760 setup_file_hash = loadSetupFileHash(filename_info);
12762 free(filename_prefix);
12763 free(filename_info);
12765 if (setup_file_hash == NULL)
12767 // ---------- try to add file extension ----------
12769 filename_prefix = getStringCopy(filename_music);
12770 filename_info = getStringCat2(filename_prefix, ".txt");
12772 if (fileExists(filename_info))
12773 setup_file_hash = loadSetupFileHash(filename_info);
12775 free(filename_prefix);
12776 free(filename_info);
12779 if (setup_file_hash == NULL)
12782 // ---------- music file info found ----------
12784 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12786 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12788 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12790 *token_to_value_ptr[i].value_ptr =
12791 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12794 tmp_music_file_info.basename = getStringCopy(basename);
12795 tmp_music_file_info.music = music;
12796 tmp_music_file_info.is_sound = is_sound;
12798 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12799 *new_music_file_info = tmp_music_file_info;
12801 return new_music_file_info;
12804 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12806 return get_music_file_info_ext(basename, music, FALSE);
12809 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12811 return get_music_file_info_ext(basename, sound, TRUE);
12814 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12815 char *basename, boolean is_sound)
12817 for (; list != NULL; list = list->next)
12818 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12824 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12826 return music_info_listed_ext(list, basename, FALSE);
12829 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12831 return music_info_listed_ext(list, basename, TRUE);
12834 void LoadMusicInfo(void)
12836 char *music_directory = getCustomMusicDirectory();
12837 int num_music = getMusicListSize();
12838 int num_music_noconf = 0;
12839 int num_sounds = getSoundListSize();
12841 DirectoryEntry *dir_entry;
12842 struct FileInfo *music, *sound;
12843 struct MusicFileInfo *next, **new;
12846 while (music_file_info != NULL)
12848 next = music_file_info->next;
12850 checked_free(music_file_info->basename);
12852 checked_free(music_file_info->title_header);
12853 checked_free(music_file_info->artist_header);
12854 checked_free(music_file_info->album_header);
12855 checked_free(music_file_info->year_header);
12857 checked_free(music_file_info->title);
12858 checked_free(music_file_info->artist);
12859 checked_free(music_file_info->album);
12860 checked_free(music_file_info->year);
12862 free(music_file_info);
12864 music_file_info = next;
12867 new = &music_file_info;
12869 for (i = 0; i < num_music; i++)
12871 music = getMusicListEntry(i);
12873 if (music->filename == NULL)
12876 if (strEqual(music->filename, UNDEFINED_FILENAME))
12879 // a configured file may be not recognized as music
12880 if (!FileIsMusic(music->filename))
12883 if (!music_info_listed(music_file_info, music->filename))
12885 *new = get_music_file_info(music->filename, i);
12888 new = &(*new)->next;
12892 if ((dir = openDirectory(music_directory)) == NULL)
12894 Warn("cannot read music directory '%s'", music_directory);
12899 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12901 char *basename = dir_entry->basename;
12902 boolean music_already_used = FALSE;
12905 // skip all music files that are configured in music config file
12906 for (i = 0; i < num_music; i++)
12908 music = getMusicListEntry(i);
12910 if (music->filename == NULL)
12913 if (strEqual(basename, music->filename))
12915 music_already_used = TRUE;
12920 if (music_already_used)
12923 if (!FileIsMusic(dir_entry->filename))
12926 if (!music_info_listed(music_file_info, basename))
12928 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12931 new = &(*new)->next;
12934 num_music_noconf++;
12937 closeDirectory(dir);
12939 for (i = 0; i < num_sounds; i++)
12941 sound = getSoundListEntry(i);
12943 if (sound->filename == NULL)
12946 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12949 // a configured file may be not recognized as sound
12950 if (!FileIsSound(sound->filename))
12953 if (!sound_info_listed(music_file_info, sound->filename))
12955 *new = get_sound_file_info(sound->filename, i);
12957 new = &(*new)->next;
12961 // add pointers to previous list nodes
12963 struct MusicFileInfo *node = music_file_info;
12965 while (node != NULL)
12968 node->next->prev = node;
12974 static void add_helpanim_entry(int element, int action, int direction,
12975 int delay, int *num_list_entries)
12977 struct HelpAnimInfo *new_list_entry;
12978 (*num_list_entries)++;
12981 checked_realloc(helpanim_info,
12982 *num_list_entries * sizeof(struct HelpAnimInfo));
12983 new_list_entry = &helpanim_info[*num_list_entries - 1];
12985 new_list_entry->element = element;
12986 new_list_entry->action = action;
12987 new_list_entry->direction = direction;
12988 new_list_entry->delay = delay;
12991 static void print_unknown_token(char *filename, char *token, int token_nr)
12996 Warn("unknown token(s) found in config file:");
12997 Warn("- config file: '%s'", filename);
13000 Warn("- token: '%s'", token);
13003 static void print_unknown_token_end(int token_nr)
13009 void LoadHelpAnimInfo(void)
13011 char *filename = getHelpAnimFilename();
13012 SetupFileList *setup_file_list = NULL, *list;
13013 SetupFileHash *element_hash, *action_hash, *direction_hash;
13014 int num_list_entries = 0;
13015 int num_unknown_tokens = 0;
13018 if (fileExists(filename))
13019 setup_file_list = loadSetupFileList(filename);
13021 if (setup_file_list == NULL)
13023 // use reliable default values from static configuration
13024 SetupFileList *insert_ptr;
13026 insert_ptr = setup_file_list =
13027 newSetupFileList(helpanim_config[0].token,
13028 helpanim_config[0].value);
13030 for (i = 1; helpanim_config[i].token; i++)
13031 insert_ptr = addListEntry(insert_ptr,
13032 helpanim_config[i].token,
13033 helpanim_config[i].value);
13036 element_hash = newSetupFileHash();
13037 action_hash = newSetupFileHash();
13038 direction_hash = newSetupFileHash();
13040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13041 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13043 for (i = 0; i < NUM_ACTIONS; i++)
13044 setHashEntry(action_hash, element_action_info[i].suffix,
13045 i_to_a(element_action_info[i].value));
13047 // do not store direction index (bit) here, but direction value!
13048 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13049 setHashEntry(direction_hash, element_direction_info[i].suffix,
13050 i_to_a(1 << element_direction_info[i].value));
13052 for (list = setup_file_list; list != NULL; list = list->next)
13054 char *element_token, *action_token, *direction_token;
13055 char *element_value, *action_value, *direction_value;
13056 int delay = atoi(list->value);
13058 if (strEqual(list->token, "end"))
13060 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13065 /* first try to break element into element/action/direction parts;
13066 if this does not work, also accept combined "element[.act][.dir]"
13067 elements (like "dynamite.active"), which are unique elements */
13069 if (strchr(list->token, '.') == NULL) // token contains no '.'
13071 element_value = getHashEntry(element_hash, list->token);
13072 if (element_value != NULL) // element found
13073 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13074 &num_list_entries);
13077 // no further suffixes found -- this is not an element
13078 print_unknown_token(filename, list->token, num_unknown_tokens++);
13084 // token has format "<prefix>.<something>"
13086 action_token = strchr(list->token, '.'); // suffix may be action ...
13087 direction_token = action_token; // ... or direction
13089 element_token = getStringCopy(list->token);
13090 *strchr(element_token, '.') = '\0';
13092 element_value = getHashEntry(element_hash, element_token);
13094 if (element_value == NULL) // this is no element
13096 element_value = getHashEntry(element_hash, list->token);
13097 if (element_value != NULL) // combined element found
13098 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13099 &num_list_entries);
13101 print_unknown_token(filename, list->token, num_unknown_tokens++);
13103 free(element_token);
13108 action_value = getHashEntry(action_hash, action_token);
13110 if (action_value != NULL) // action found
13112 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13113 &num_list_entries);
13115 free(element_token);
13120 direction_value = getHashEntry(direction_hash, direction_token);
13122 if (direction_value != NULL) // direction found
13124 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13125 &num_list_entries);
13127 free(element_token);
13132 if (strchr(action_token + 1, '.') == NULL)
13134 // no further suffixes found -- this is not an action nor direction
13136 element_value = getHashEntry(element_hash, list->token);
13137 if (element_value != NULL) // combined element found
13138 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13139 &num_list_entries);
13141 print_unknown_token(filename, list->token, num_unknown_tokens++);
13143 free(element_token);
13148 // token has format "<prefix>.<suffix>.<something>"
13150 direction_token = strchr(action_token + 1, '.');
13152 action_token = getStringCopy(action_token);
13153 *strchr(action_token + 1, '.') = '\0';
13155 action_value = getHashEntry(action_hash, action_token);
13157 if (action_value == NULL) // this is no action
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);
13167 free(action_token);
13172 direction_value = getHashEntry(direction_hash, direction_token);
13174 if (direction_value != NULL) // direction found
13176 add_helpanim_entry(atoi(element_value), atoi(action_value),
13177 atoi(direction_value), delay, &num_list_entries);
13179 free(element_token);
13180 free(action_token);
13185 // this is no direction
13187 element_value = getHashEntry(element_hash, list->token);
13188 if (element_value != NULL) // combined element found
13189 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13190 &num_list_entries);
13192 print_unknown_token(filename, list->token, num_unknown_tokens++);
13194 free(element_token);
13195 free(action_token);
13198 print_unknown_token_end(num_unknown_tokens);
13200 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13201 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13203 freeSetupFileList(setup_file_list);
13204 freeSetupFileHash(element_hash);
13205 freeSetupFileHash(action_hash);
13206 freeSetupFileHash(direction_hash);
13209 for (i = 0; i < num_list_entries; i++)
13210 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13211 EL_NAME(helpanim_info[i].element),
13212 helpanim_info[i].element,
13213 helpanim_info[i].action,
13214 helpanim_info[i].direction,
13215 helpanim_info[i].delay);
13219 void LoadHelpTextInfo(void)
13221 char *filename = getHelpTextFilename();
13224 if (helptext_info != NULL)
13226 freeSetupFileHash(helptext_info);
13227 helptext_info = NULL;
13230 if (fileExists(filename))
13231 helptext_info = loadSetupFileHash(filename);
13233 if (helptext_info == NULL)
13235 // use reliable default values from static configuration
13236 helptext_info = newSetupFileHash();
13238 for (i = 0; helptext_config[i].token; i++)
13239 setHashEntry(helptext_info,
13240 helptext_config[i].token,
13241 helptext_config[i].value);
13245 BEGIN_HASH_ITERATION(helptext_info, itr)
13247 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13248 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13250 END_HASH_ITERATION(hash, itr)
13255 // ----------------------------------------------------------------------------
13257 // ----------------------------------------------------------------------------
13259 #define MAX_NUM_CONVERT_LEVELS 1000
13261 void ConvertLevels(void)
13263 static LevelDirTree *convert_leveldir = NULL;
13264 static int convert_level_nr = -1;
13265 static int num_levels_handled = 0;
13266 static int num_levels_converted = 0;
13267 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13270 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13271 global.convert_leveldir);
13273 if (convert_leveldir == NULL)
13274 Fail("no such level identifier: '%s'", global.convert_leveldir);
13276 leveldir_current = convert_leveldir;
13278 if (global.convert_level_nr != -1)
13280 convert_leveldir->first_level = global.convert_level_nr;
13281 convert_leveldir->last_level = global.convert_level_nr;
13284 convert_level_nr = convert_leveldir->first_level;
13286 PrintLine("=", 79);
13287 Print("Converting levels\n");
13288 PrintLine("-", 79);
13289 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13290 Print("Level series name: '%s'\n", convert_leveldir->name);
13291 Print("Level series author: '%s'\n", convert_leveldir->author);
13292 Print("Number of levels: %d\n", convert_leveldir->levels);
13293 PrintLine("=", 79);
13296 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13297 levels_failed[i] = FALSE;
13299 while (convert_level_nr <= convert_leveldir->last_level)
13301 char *level_filename;
13304 level_nr = convert_level_nr++;
13306 Print("Level %03d: ", level_nr);
13308 LoadLevel(level_nr);
13309 if (level.no_level_file || level.no_valid_file)
13311 Print("(no level)\n");
13315 Print("converting level ... ");
13318 // special case: conversion of some EMC levels as requested by ACME
13319 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13322 level_filename = getDefaultLevelFilename(level_nr);
13323 new_level = !fileExists(level_filename);
13327 SaveLevel(level_nr);
13329 num_levels_converted++;
13331 Print("converted.\n");
13335 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13336 levels_failed[level_nr] = TRUE;
13338 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13341 num_levels_handled++;
13345 PrintLine("=", 79);
13346 Print("Number of levels handled: %d\n", num_levels_handled);
13347 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13348 (num_levels_handled ?
13349 num_levels_converted * 100 / num_levels_handled : 0));
13350 PrintLine("-", 79);
13351 Print("Summary (for automatic parsing by scripts):\n");
13352 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13353 convert_leveldir->identifier, num_levels_converted,
13354 num_levels_handled,
13355 (num_levels_handled ?
13356 num_levels_converted * 100 / num_levels_handled : 0));
13358 if (num_levels_handled != num_levels_converted)
13360 Print(", FAILED:");
13361 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13362 if (levels_failed[i])
13367 PrintLine("=", 79);
13369 CloseAllAndExit(0);
13373 // ----------------------------------------------------------------------------
13374 // create and save images for use in level sketches (raw BMP format)
13375 // ----------------------------------------------------------------------------
13377 void CreateLevelSketchImages(void)
13383 InitElementPropertiesGfxElement();
13385 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13386 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13388 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13390 int element = getMappedElement(i);
13391 char basename1[16];
13392 char basename2[16];
13396 sprintf(basename1, "%04d.bmp", i);
13397 sprintf(basename2, "%04ds.bmp", i);
13399 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13400 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13402 DrawSizedElement(0, 0, element, TILESIZE);
13403 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13405 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13406 Fail("cannot save level sketch image file '%s'", filename1);
13408 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13409 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13411 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13412 Fail("cannot save level sketch image file '%s'", filename2);
13417 // create corresponding SQL statements (for normal and small images)
13420 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13421 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13424 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13425 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13427 // optional: create content for forum level sketch demonstration post
13429 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13432 FreeBitmap(bitmap1);
13433 FreeBitmap(bitmap2);
13436 fprintf(stderr, "\n");
13438 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13440 CloseAllAndExit(0);
13444 // ----------------------------------------------------------------------------
13445 // create and save images for element collecting animations (raw BMP format)
13446 // ----------------------------------------------------------------------------
13448 static boolean createCollectImage(int element)
13450 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13453 void CreateCollectElementImages(void)
13457 int anim_frames = num_steps - 1;
13458 int tile_size = TILESIZE;
13459 int anim_width = tile_size * anim_frames;
13460 int anim_height = tile_size;
13461 int num_collect_images = 0;
13462 int pos_collect_images = 0;
13464 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13465 if (createCollectImage(i))
13466 num_collect_images++;
13468 Info("Creating %d element collecting animation images ...",
13469 num_collect_images);
13471 int dst_width = anim_width * 2;
13472 int dst_height = anim_height * num_collect_images / 2;
13473 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13474 char *basename_bmp = "RocksCollect.bmp";
13475 char *basename_png = "RocksCollect.png";
13476 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13477 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13478 int len_filename_bmp = strlen(filename_bmp);
13479 int len_filename_png = strlen(filename_png);
13480 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13481 char cmd_convert[max_command_len];
13483 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13487 // force using RGBA surface for destination bitmap
13488 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13489 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13491 dst_bitmap->surface =
13492 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13494 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13496 if (!createCollectImage(i))
13499 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13500 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13501 int graphic = el2img(i);
13502 char *token_name = element_info[i].token_name;
13503 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13504 Bitmap *src_bitmap;
13507 Info("- creating collecting image for '%s' ...", token_name);
13509 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13511 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13512 tile_size, tile_size, 0, 0);
13514 // force using RGBA surface for temporary bitmap (using transparent black)
13515 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13516 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13518 tmp_bitmap->surface =
13519 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13521 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13523 for (j = 0; j < anim_frames; j++)
13525 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13526 int frame_size = frame_size_final * num_steps;
13527 int offset = (tile_size - frame_size_final) / 2;
13528 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13530 while (frame_size > frame_size_final)
13534 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13536 FreeBitmap(frame_bitmap);
13538 frame_bitmap = half_bitmap;
13541 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13542 frame_size_final, frame_size_final,
13543 dst_x + j * tile_size + offset, dst_y + offset);
13545 FreeBitmap(frame_bitmap);
13548 tmp_bitmap->surface_masked = NULL;
13550 FreeBitmap(tmp_bitmap);
13552 pos_collect_images++;
13555 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13556 Fail("cannot save element collecting image file '%s'", filename_bmp);
13558 FreeBitmap(dst_bitmap);
13560 Info("Converting image file from BMP to PNG ...");
13562 if (system(cmd_convert) != 0)
13563 Fail("converting image file failed");
13565 unlink(filename_bmp);
13569 CloseAllAndExit(0);
13573 // ----------------------------------------------------------------------------
13574 // create and save images for custom and group elements (raw BMP format)
13575 // ----------------------------------------------------------------------------
13577 void CreateCustomElementImages(char *directory)
13579 char *src_basename = "RocksCE-template.ilbm";
13580 char *dst_basename = "RocksCE.bmp";
13581 char *src_filename = getPath2(directory, src_basename);
13582 char *dst_filename = getPath2(directory, dst_basename);
13583 Bitmap *src_bitmap;
13585 int yoffset_ce = 0;
13586 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13589 InitVideoDefaults();
13591 ReCreateBitmap(&backbuffer, video.width, video.height);
13593 src_bitmap = LoadImage(src_filename);
13595 bitmap = CreateBitmap(TILEX * 16 * 2,
13596 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13599 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13606 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13607 TILEX * x, TILEY * y + yoffset_ce);
13609 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13611 TILEX * x + TILEX * 16,
13612 TILEY * y + yoffset_ce);
13614 for (j = 2; j >= 0; j--)
13618 BlitBitmap(src_bitmap, bitmap,
13619 TILEX + c * 7, 0, 6, 10,
13620 TILEX * x + 6 + j * 7,
13621 TILEY * y + 11 + yoffset_ce);
13623 BlitBitmap(src_bitmap, bitmap,
13624 TILEX + c * 8, TILEY, 6, 10,
13625 TILEX * 16 + TILEX * x + 6 + j * 8,
13626 TILEY * y + 10 + yoffset_ce);
13632 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13639 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13640 TILEX * x, TILEY * y + yoffset_ge);
13642 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13644 TILEX * x + TILEX * 16,
13645 TILEY * y + yoffset_ge);
13647 for (j = 1; j >= 0; j--)
13651 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13652 TILEX * x + 6 + j * 10,
13653 TILEY * y + 11 + yoffset_ge);
13655 BlitBitmap(src_bitmap, bitmap,
13656 TILEX + c * 8, TILEY + 12, 6, 10,
13657 TILEX * 16 + TILEX * x + 10 + j * 8,
13658 TILEY * y + 10 + yoffset_ge);
13664 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13665 Fail("cannot save CE graphics file '%s'", dst_filename);
13667 FreeBitmap(bitmap);
13669 CloseAllAndExit(0);