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
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
311 &li.bd_cave_random_seed_c64, 0
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
605 // (some values for BD style amoeba following below)
608 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
609 &li.bd_diagonal_movements, FALSE
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
614 &li.bd_topmost_player_active, TRUE
618 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
619 &li.bd_pushing_prob, 25
623 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
624 &li.bd_pushing_prob_with_sweet, 100
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
629 &li.bd_push_mega_rock_with_sweet, FALSE
633 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
634 &li.bd_snap_element, EL_EMPTY
639 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
640 &li.bd_sand_looks_like, EL_BD_SAND
645 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
646 &li.bd_rock_turns_to_on_falling, EL_BD_ROCK_FALLING
650 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
651 &li.bd_rock_turns_to_on_impact, EL_BD_ROCK
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_DIAMOND_EXTRA], 20
661 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
662 &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
666 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
667 &li.bd_diamond_turns_to_on_impact, EL_BD_DIAMOND
672 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
673 &li.bd_firefly_explodes_to, EL_BD_EXPLODING_1
678 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
679 &li.bd_firefly_2_explodes_to, EL_BD_EXPLODING_1
684 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
685 &li.bd_butterfly_explodes_to, EL_BD_DIAMOND_GROWING_1
689 EL_BD_BUTTERFLY_2, -1,
690 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
691 &li.bd_butterfly_2_explodes_to, EL_BD_DIAMOND_GROWING_1
696 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
697 &li.bd_stonefly_explodes_to, EL_BD_ROCK_GROWING_1
702 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
703 &li.bd_dragonfly_explodes_to, EL_BD_EXPLODING_1
707 EL_BD_MAGIC_WALL, -1,
708 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
709 &li.bd_magic_wall_wait_hatching, FALSE
712 EL_BD_MAGIC_WALL, -1,
713 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
714 &li.bd_magic_wall_stops_amoeba, TRUE
717 EL_BD_MAGIC_WALL, -1,
718 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
719 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
722 EL_BD_MAGIC_WALL, -1,
723 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
724 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
727 EL_BD_MAGIC_WALL, -1,
728 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
729 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
732 EL_BD_MAGIC_WALL, -1,
733 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
734 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
737 EL_BD_MAGIC_WALL, -1,
738 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
739 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
742 EL_BD_MAGIC_WALL, -1,
743 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
744 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
747 EL_BD_MAGIC_WALL, -1,
748 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
749 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
754 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
755 &li.bd_clock_extra_time, 30
759 EL_BD_VOODOO_DOLL, -1,
760 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
761 &li.bd_voodoo_collects_diamonds, FALSE
764 EL_BD_VOODOO_DOLL, -1,
765 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
766 &li.bd_voodoo_hurt_kills_player, FALSE
769 EL_BD_VOODOO_DOLL, -1,
770 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
771 &li.bd_voodoo_dies_by_rock, FALSE
774 EL_BD_VOODOO_DOLL, -1,
775 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
776 &li.bd_voodoo_vanish_by_explosion, TRUE
779 EL_BD_VOODOO_DOLL, -1,
780 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
781 &li.bd_voodoo_penalty_time, 30
786 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
787 &li.bd_slime_is_predictable, TRUE
791 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
792 &li.bd_slime_permeability_rate, 100
796 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
797 &li.bd_slime_permeability_bits_c64, 0
801 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
802 &li.bd_slime_random_seed_c64, -1
806 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
807 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
811 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
812 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
816 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
817 &li.bd_slime_eats_element_2, EL_BD_ROCK
821 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
822 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
826 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
827 &li.bd_slime_eats_element_3, EL_BD_NUT
831 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
832 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
837 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
838 &li.bd_acid_eats_element, EL_BD_SAND
842 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
843 &li.bd_acid_spread_rate, 3
847 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
848 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
853 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
854 &li.bd_biter_move_delay, 0
858 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
859 &li.bd_biter_eats_element, EL_BD_DIAMOND
864 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
865 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
869 EL_BD_EXPANDABLE_WALL_ANY, -1,
870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
871 &li.bd_change_expanding_wall, FALSE
874 EL_BD_EXPANDABLE_WALL_ANY, -1,
875 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
876 &li.bd_expanding_wall_looks_like, EL_BD_WALL
880 EL_BD_REPLICATOR, -1,
881 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
882 &li.bd_replicators_active, TRUE
885 EL_BD_REPLICATOR, -1,
886 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
887 &li.bd_replicator_create_delay, 4
891 EL_BD_CONVEYOR_LEFT, -1,
892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
893 &li.bd_conveyor_belts_active, TRUE
896 EL_BD_CONVEYOR_LEFT, -1,
897 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
898 &li.bd_conveyor_belts_changed, FALSE
903 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
904 &li.bd_water_cannot_flow_down, FALSE
909 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
910 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
914 EL_BD_PNEUMATIC_HAMMER, -1,
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.bd_hammer_walls_break_delay, 5
919 EL_BD_PNEUMATIC_HAMMER, -1,
920 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
921 &li.bd_hammer_walls_reappear, FALSE
924 EL_BD_PNEUMATIC_HAMMER, -1,
925 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
926 &li.bd_hammer_walls_reappear_delay, 100
931 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
932 &li.bd_num_skeletons_needed_for_pot, 5
936 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
937 &li.bd_skeleton_worth_num_diamonds, 0
941 EL_BD_CREATURE_SWITCH, -1,
942 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
943 &li.bd_creatures_start_backwards, FALSE
946 EL_BD_CREATURE_SWITCH, -1,
947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
948 &li.bd_creatures_turn_on_hatching, FALSE
951 EL_BD_CREATURE_SWITCH, -1,
952 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
953 &li.bd_creatures_auto_turn_delay, 0
957 EL_BD_GRAVITY_SWITCH, -1,
958 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
959 &li.bd_gravity_direction, GD_MV_DOWN
962 EL_BD_GRAVITY_SWITCH, -1,
963 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
964 &li.bd_gravity_switch_active, FALSE
967 EL_BD_GRAVITY_SWITCH, -1,
968 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
969 &li.bd_gravity_switch_delay, 10
972 EL_BD_GRAVITY_SWITCH, -1,
973 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
974 &li.bd_gravity_affects_all, TRUE
977 // (the following values are related to various game elements)
981 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
982 &li.score[SC_EMERALD], 10
987 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
988 &li.score[SC_DIAMOND], 10
993 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
994 &li.score[SC_BUG], 10
999 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1000 &li.score[SC_SPACESHIP], 10
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1006 &li.score[SC_PACMAN], 10
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1012 &li.score[SC_NUT], 10
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1018 &li.score[SC_DYNAMITE], 10
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1024 &li.score[SC_KEY], 10
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1030 &li.score[SC_PEARL], 10
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1036 &li.score[SC_CRYSTAL], 10
1039 // (amoeba values used by R'n'D game engine only)
1042 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1043 &li.amoeba_content, EL_DIAMOND
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1048 &li.amoeba_speed, 10
1052 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1053 &li.grow_into_diggable, TRUE
1055 // (amoeba values used by BD game engine only)
1058 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1059 &li.bd_amoeba_wait_for_hatching, FALSE
1063 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1064 &li.bd_amoeba_start_immediately, TRUE
1068 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1069 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1073 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1074 &li.bd_amoeba_threshold_too_big, 200
1078 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1079 &li.bd_amoeba_slow_growth_time, 200
1083 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1084 &li.bd_amoeba_slow_growth_rate, 3
1088 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1089 &li.bd_amoeba_fast_growth_rate, 25
1093 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1094 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1098 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1099 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1105 &li.bd_amoeba_2_threshold_too_big, 200
1109 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1110 &li.bd_amoeba_2_slow_growth_time, 200
1114 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1115 &li.bd_amoeba_2_slow_growth_rate, 3
1119 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1120 &li.bd_amoeba_2_fast_growth_rate, 25
1124 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1125 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1129 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1130 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1134 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1135 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1139 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1140 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1145 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1146 &li.yamyam_content, EL_ROCK, NULL,
1147 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1151 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1152 &li.score[SC_YAMYAM], 10
1157 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1158 &li.score[SC_ROBOT], 10
1162 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1168 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1174 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1175 &li.time_magic_wall, 10
1179 EL_GAME_OF_LIFE, -1,
1180 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1181 &li.game_of_life[0], 2
1184 EL_GAME_OF_LIFE, -1,
1185 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1186 &li.game_of_life[1], 3
1189 EL_GAME_OF_LIFE, -1,
1190 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1191 &li.game_of_life[2], 3
1194 EL_GAME_OF_LIFE, -1,
1195 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1196 &li.game_of_life[3], 3
1199 EL_GAME_OF_LIFE, -1,
1200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1201 &li.use_life_bugs, FALSE
1206 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1211 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1216 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1221 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1226 EL_TIMEGATE_SWITCH, -1,
1227 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1228 &li.time_timegate, 10
1232 EL_LIGHT_SWITCH_ACTIVE, -1,
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1238 EL_SHIELD_NORMAL, -1,
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1240 &li.shield_normal_time, 10
1243 EL_SHIELD_NORMAL, -1,
1244 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1245 &li.score[SC_SHIELD], 10
1249 EL_SHIELD_DEADLY, -1,
1250 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1251 &li.shield_deadly_time, 10
1254 EL_SHIELD_DEADLY, -1,
1255 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1256 &li.score[SC_SHIELD], 10
1261 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1266 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1267 &li.extra_time_score, 10
1271 EL_TIME_ORB_FULL, -1,
1272 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1273 &li.time_orb_time, 10
1276 EL_TIME_ORB_FULL, -1,
1277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1278 &li.use_time_orb_bug, FALSE
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1284 &li.use_spring_bug, FALSE
1289 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1290 &li.android_move_time, 10
1294 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1295 &li.android_clone_time, 10
1298 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1299 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1300 &li.android_clone_element[0], EL_EMPTY, NULL,
1301 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1305 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1306 &li.android_clone_element[0], EL_EMPTY, NULL,
1307 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1312 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1313 &li.lenses_score, 10
1317 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1322 EL_EMC_MAGNIFIER, -1,
1323 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1324 &li.magnify_score, 10
1327 EL_EMC_MAGNIFIER, -1,
1328 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1329 &li.magnify_time, 10
1333 EL_EMC_MAGIC_BALL, -1,
1334 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1338 EL_EMC_MAGIC_BALL, -1,
1339 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1340 &li.ball_random, FALSE
1343 EL_EMC_MAGIC_BALL, -1,
1344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1345 &li.ball_active_initial, FALSE
1348 EL_EMC_MAGIC_BALL, -1,
1349 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1350 &li.ball_content, EL_EMPTY, NULL,
1351 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1355 EL_SOKOBAN_FIELD_EMPTY, -1,
1356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1357 &li.sb_fields_needed, TRUE
1361 EL_SOKOBAN_OBJECT, -1,
1362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1363 &li.sb_objects_needed, TRUE
1368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1369 &li.mm_laser_red, FALSE
1373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1374 &li.mm_laser_green, FALSE
1378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1379 &li.mm_laser_blue, TRUE
1384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1385 &li.df_laser_red, TRUE
1389 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1390 &li.df_laser_green, TRUE
1394 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1395 &li.df_laser_blue, FALSE
1399 EL_MM_FUSE_ACTIVE, -1,
1400 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1401 &li.mm_time_fuse, 25
1405 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1406 &li.mm_time_bomb, 75
1410 EL_MM_GRAY_BALL, -1,
1411 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1412 &li.mm_time_ball, 75
1415 EL_MM_GRAY_BALL, -1,
1416 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1417 &li.mm_ball_choice_mode, ANIM_RANDOM
1420 EL_MM_GRAY_BALL, -1,
1421 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1422 &li.mm_ball_content, EL_EMPTY, NULL,
1423 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1426 EL_MM_GRAY_BALL, -1,
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1428 &li.rotate_mm_ball_content, TRUE
1431 EL_MM_GRAY_BALL, -1,
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1433 &li.explode_mm_ball, FALSE
1437 EL_MM_STEEL_BLOCK, -1,
1438 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1439 &li.mm_time_block, 75
1442 EL_MM_LIGHTBALL, -1,
1443 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1444 &li.score[SC_ELEM_BONUS], 10
1454 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1458 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1459 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1463 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1464 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1470 &xx_envelope.autowrap, FALSE
1474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1475 &xx_envelope.centered, FALSE
1480 TYPE_STRING, CONF_VALUE_BYTES(1),
1481 &xx_envelope.text, -1, NULL,
1482 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1483 &xx_default_string_empty[0]
1493 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1497 TYPE_STRING, CONF_VALUE_BYTES(1),
1498 &xx_ei.description[0], -1,
1499 &yy_ei.description[0],
1500 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1501 &xx_default_description[0]
1506 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1507 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1508 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1510 #if ENABLE_RESERVED_CODE
1511 // (reserved for later use)
1514 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1515 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1516 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1523 &xx_ei.use_gfx_element, FALSE,
1524 &yy_ei.use_gfx_element
1528 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1529 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1530 &yy_ei.gfx_element_initial
1535 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1536 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1537 &yy_ei.access_direction
1542 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1543 &xx_ei.collect_score_initial, 10,
1544 &yy_ei.collect_score_initial
1548 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1549 &xx_ei.collect_count_initial, 1,
1550 &yy_ei.collect_count_initial
1555 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1556 &xx_ei.ce_value_fixed_initial, 0,
1557 &yy_ei.ce_value_fixed_initial
1561 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1562 &xx_ei.ce_value_random_initial, 0,
1563 &yy_ei.ce_value_random_initial
1567 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1568 &xx_ei.use_last_ce_value, FALSE,
1569 &yy_ei.use_last_ce_value
1574 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1575 &xx_ei.push_delay_fixed, 8,
1576 &yy_ei.push_delay_fixed
1580 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1581 &xx_ei.push_delay_random, 8,
1582 &yy_ei.push_delay_random
1586 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1587 &xx_ei.drop_delay_fixed, 0,
1588 &yy_ei.drop_delay_fixed
1592 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1593 &xx_ei.drop_delay_random, 0,
1594 &yy_ei.drop_delay_random
1598 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1599 &xx_ei.move_delay_fixed, 0,
1600 &yy_ei.move_delay_fixed
1604 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1605 &xx_ei.move_delay_random, 0,
1606 &yy_ei.move_delay_random
1610 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1611 &xx_ei.step_delay_fixed, 0,
1612 &yy_ei.step_delay_fixed
1616 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1617 &xx_ei.step_delay_random, 0,
1618 &yy_ei.step_delay_random
1623 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1624 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1629 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1630 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1631 &yy_ei.move_direction_initial
1635 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1636 &xx_ei.move_stepsize, TILEX / 8,
1637 &yy_ei.move_stepsize
1642 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1643 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1644 &yy_ei.move_enter_element
1648 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1649 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1650 &yy_ei.move_leave_element
1654 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1655 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1656 &yy_ei.move_leave_type
1661 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1662 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1663 &yy_ei.slippery_type
1668 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1669 &xx_ei.explosion_type, EXPLODES_3X3,
1670 &yy_ei.explosion_type
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1675 &xx_ei.explosion_delay, 16,
1676 &yy_ei.explosion_delay
1680 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1681 &xx_ei.ignition_delay, 8,
1682 &yy_ei.ignition_delay
1687 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1688 &xx_ei.content, EL_EMPTY_SPACE,
1690 &xx_num_contents, 1, 1
1693 // ---------- "num_change_pages" must be the last entry ---------------------
1696 -1, SAVE_CONF_ALWAYS,
1697 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1698 &xx_ei.num_change_pages, 1,
1699 &yy_ei.num_change_pages
1710 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1712 // ---------- "current_change_page" must be the first entry -----------------
1715 -1, SAVE_CONF_ALWAYS,
1716 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1717 &xx_current_change_page, -1
1720 // ---------- (the remaining entries can be in any order) -------------------
1724 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1725 &xx_change.can_change, FALSE
1730 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1731 &xx_event_bits[0], 0
1735 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1736 &xx_event_bits[1], 0
1741 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1742 &xx_change.trigger_player, CH_PLAYER_ANY
1746 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1747 &xx_change.trigger_side, CH_SIDE_ANY
1751 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1752 &xx_change.trigger_page, CH_PAGE_ANY
1757 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1758 &xx_change.target_element, EL_EMPTY_SPACE
1763 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1764 &xx_change.delay_fixed, 0
1768 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1769 &xx_change.delay_random, 0
1773 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1774 &xx_change.delay_frames, FRAMES_PER_SECOND
1779 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1780 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1785 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1786 &xx_change.explode, FALSE
1790 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1791 &xx_change.use_target_content, FALSE
1795 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1796 &xx_change.only_if_complete, FALSE
1800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1801 &xx_change.use_random_replace, FALSE
1805 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1806 &xx_change.random_percentage, 100
1810 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1811 &xx_change.replace_when, CP_WHEN_EMPTY
1816 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1817 &xx_change.has_action, FALSE
1821 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1822 &xx_change.action_type, CA_NO_ACTION
1826 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1827 &xx_change.action_mode, CA_MODE_UNDEFINED
1831 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1832 &xx_change.action_arg, CA_ARG_UNDEFINED
1837 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1838 &xx_change.action_element, EL_EMPTY_SPACE
1843 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1844 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1845 &xx_num_contents, 1, 1
1855 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1859 TYPE_STRING, CONF_VALUE_BYTES(1),
1860 &xx_ei.description[0], -1, NULL,
1861 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1862 &xx_default_description[0]
1867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1868 &xx_ei.use_gfx_element, FALSE
1872 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1873 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1878 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1879 &xx_group.choice_mode, ANIM_RANDOM
1884 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1885 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1886 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1896 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1900 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1901 &xx_ei.use_gfx_element, FALSE
1905 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1906 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1916 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1920 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1921 &li.block_snap_field, TRUE
1925 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1926 &li.continuous_snapping, TRUE
1930 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1931 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1936 &li.use_start_element[0], FALSE
1940 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1941 &li.start_element[0], EL_PLAYER_1
1945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1946 &li.use_artwork_element[0], FALSE
1950 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1951 &li.artwork_element[0], EL_PLAYER_1
1955 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1956 &li.use_explosion_element[0], FALSE
1960 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1961 &li.explosion_element[0], EL_PLAYER_1
1976 filetype_id_list[] =
1978 { LEVEL_FILE_TYPE_RND, "RND" },
1979 { LEVEL_FILE_TYPE_BD, "BD" },
1980 { LEVEL_FILE_TYPE_EM, "EM" },
1981 { LEVEL_FILE_TYPE_SP, "SP" },
1982 { LEVEL_FILE_TYPE_DX, "DX" },
1983 { LEVEL_FILE_TYPE_SB, "SB" },
1984 { LEVEL_FILE_TYPE_DC, "DC" },
1985 { LEVEL_FILE_TYPE_MM, "MM" },
1986 { LEVEL_FILE_TYPE_MM, "DF" },
1991 // ============================================================================
1992 // level file functions
1993 // ============================================================================
1995 static boolean check_special_flags(char *flag)
1997 if (strEqual(options.special_flags, flag) ||
1998 strEqual(leveldir_current->special_flags, flag))
2004 static struct DateInfo getCurrentDate(void)
2006 time_t epoch_seconds = time(NULL);
2007 struct tm *now = localtime(&epoch_seconds);
2008 struct DateInfo date;
2010 date.year = now->tm_year + 1900;
2011 date.month = now->tm_mon + 1;
2012 date.day = now->tm_mday;
2014 date.src = DATE_SRC_CLOCK;
2019 static void resetEventFlags(struct ElementChangeInfo *change)
2023 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2024 change->has_event[i] = FALSE;
2027 static void resetEventBits(void)
2031 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2032 xx_event_bits[i] = 0;
2035 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2039 /* important: only change event flag if corresponding event bit is set
2040 (this is because all xx_event_bits[] values are loaded separately,
2041 and all xx_event_bits[] values are set back to zero before loading
2042 another value xx_event_bits[x] (each value representing 32 flags)) */
2044 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2045 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2046 change->has_event[i] = TRUE;
2049 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2053 /* in contrast to the above function setEventFlagsFromEventBits(), it
2054 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2055 depending on the corresponding change->has_event[i] values here, as
2056 all xx_event_bits[] values are reset in resetEventBits() before */
2058 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2059 if (change->has_event[i])
2060 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2063 static char *getDefaultElementDescription(struct ElementInfo *ei)
2065 static char description[MAX_ELEMENT_NAME_LEN + 1];
2066 char *default_description = (ei->custom_description != NULL ?
2067 ei->custom_description :
2068 ei->editor_description);
2071 // always start with reliable default values
2072 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2073 description[i] = '\0';
2075 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2076 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2078 return &description[0];
2081 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2083 char *default_description = getDefaultElementDescription(ei);
2086 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2087 ei->description[i] = default_description[i];
2090 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2094 for (i = 0; conf[i].data_type != -1; i++)
2096 int default_value = conf[i].default_value;
2097 int data_type = conf[i].data_type;
2098 int conf_type = conf[i].conf_type;
2099 int byte_mask = conf_type & CONF_MASK_BYTES;
2101 if (byte_mask == CONF_MASK_MULTI_BYTES)
2103 int default_num_entities = conf[i].default_num_entities;
2104 int max_num_entities = conf[i].max_num_entities;
2106 *(int *)(conf[i].num_entities) = default_num_entities;
2108 if (data_type == TYPE_STRING)
2110 char *default_string = conf[i].default_string;
2111 char *string = (char *)(conf[i].value);
2113 strncpy(string, default_string, max_num_entities);
2115 else if (data_type == TYPE_ELEMENT_LIST)
2117 int *element_array = (int *)(conf[i].value);
2120 for (j = 0; j < max_num_entities; j++)
2121 element_array[j] = default_value;
2123 else if (data_type == TYPE_CONTENT_LIST)
2125 struct Content *content = (struct Content *)(conf[i].value);
2128 for (c = 0; c < max_num_entities; c++)
2129 for (y = 0; y < 3; y++)
2130 for (x = 0; x < 3; x++)
2131 content[c].e[x][y] = default_value;
2134 else // constant size configuration data (1, 2 or 4 bytes)
2136 if (data_type == TYPE_BOOLEAN)
2137 *(boolean *)(conf[i].value) = default_value;
2139 *(int *) (conf[i].value) = default_value;
2144 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2148 for (i = 0; conf[i].data_type != -1; i++)
2150 int data_type = conf[i].data_type;
2151 int conf_type = conf[i].conf_type;
2152 int byte_mask = conf_type & CONF_MASK_BYTES;
2154 if (byte_mask == CONF_MASK_MULTI_BYTES)
2156 int max_num_entities = conf[i].max_num_entities;
2158 if (data_type == TYPE_STRING)
2160 char *string = (char *)(conf[i].value);
2161 char *string_copy = (char *)(conf[i].value_copy);
2163 strncpy(string_copy, string, max_num_entities);
2165 else if (data_type == TYPE_ELEMENT_LIST)
2167 int *element_array = (int *)(conf[i].value);
2168 int *element_array_copy = (int *)(conf[i].value_copy);
2171 for (j = 0; j < max_num_entities; j++)
2172 element_array_copy[j] = element_array[j];
2174 else if (data_type == TYPE_CONTENT_LIST)
2176 struct Content *content = (struct Content *)(conf[i].value);
2177 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2180 for (c = 0; c < max_num_entities; c++)
2181 for (y = 0; y < 3; y++)
2182 for (x = 0; x < 3; x++)
2183 content_copy[c].e[x][y] = content[c].e[x][y];
2186 else // constant size configuration data (1, 2 or 4 bytes)
2188 if (data_type == TYPE_BOOLEAN)
2189 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2191 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2196 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2200 xx_ei = *ei_from; // copy element data into temporary buffer
2201 yy_ei = *ei_to; // copy element data into temporary buffer
2203 copyConfigFromConfigList(chunk_config_CUSX_base);
2208 // ---------- reinitialize and copy change pages ----------
2210 ei_to->num_change_pages = ei_from->num_change_pages;
2211 ei_to->current_change_page = ei_from->current_change_page;
2213 setElementChangePages(ei_to, ei_to->num_change_pages);
2215 for (i = 0; i < ei_to->num_change_pages; i++)
2216 ei_to->change_page[i] = ei_from->change_page[i];
2218 // ---------- copy group element info ----------
2219 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2220 *ei_to->group = *ei_from->group;
2222 // mark this custom element as modified
2223 ei_to->modified_settings = TRUE;
2226 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2228 int change_page_size = sizeof(struct ElementChangeInfo);
2230 ei->num_change_pages = MAX(1, change_pages);
2233 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2235 if (ei->current_change_page >= ei->num_change_pages)
2236 ei->current_change_page = ei->num_change_pages - 1;
2238 ei->change = &ei->change_page[ei->current_change_page];
2241 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2243 xx_change = *change; // copy change data into temporary buffer
2245 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2247 *change = xx_change;
2249 resetEventFlags(change);
2251 change->direct_action = 0;
2252 change->other_action = 0;
2254 change->pre_change_function = NULL;
2255 change->change_function = NULL;
2256 change->post_change_function = NULL;
2259 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2263 li = *level; // copy level data into temporary buffer
2264 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2265 *level = li; // copy temporary buffer back to level data
2267 setLevelInfoToDefaults_BD();
2268 setLevelInfoToDefaults_EM();
2269 setLevelInfoToDefaults_SP();
2270 setLevelInfoToDefaults_MM();
2272 level->native_bd_level = &native_bd_level;
2273 level->native_em_level = &native_em_level;
2274 level->native_sp_level = &native_sp_level;
2275 level->native_mm_level = &native_mm_level;
2277 level->file_version = FILE_VERSION_ACTUAL;
2278 level->game_version = GAME_VERSION_ACTUAL;
2280 level->creation_date = getCurrentDate();
2282 level->encoding_16bit_field = TRUE;
2283 level->encoding_16bit_yamyam = TRUE;
2284 level->encoding_16bit_amoeba = TRUE;
2286 // clear level name and level author string buffers
2287 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2288 level->name[i] = '\0';
2289 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2290 level->author[i] = '\0';
2292 // set level name and level author to default values
2293 strcpy(level->name, NAMELESS_LEVEL_NAME);
2294 strcpy(level->author, ANONYMOUS_NAME);
2296 // set level playfield to playable default level with player and exit
2297 for (x = 0; x < MAX_LEV_FIELDX; x++)
2298 for (y = 0; y < MAX_LEV_FIELDY; y++)
2299 level->field[x][y] = EL_SAND;
2301 level->field[0][0] = EL_PLAYER_1;
2302 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2304 BorderElement = EL_STEELWALL;
2306 // detect custom elements when loading them
2307 level->file_has_custom_elements = FALSE;
2309 // set all bug compatibility flags to "false" => do not emulate this bug
2310 level->use_action_after_change_bug = FALSE;
2312 if (leveldir_current)
2314 // try to determine better author name than 'anonymous'
2315 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2317 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2318 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2322 switch (LEVELCLASS(leveldir_current))
2324 case LEVELCLASS_TUTORIAL:
2325 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2328 case LEVELCLASS_CONTRIB:
2329 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2330 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2333 case LEVELCLASS_PRIVATE:
2334 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2335 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2339 // keep default value
2346 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2348 static boolean clipboard_elements_initialized = FALSE;
2351 InitElementPropertiesStatic();
2353 li = *level; // copy level data into temporary buffer
2354 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2355 *level = li; // copy temporary buffer back to level data
2357 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2360 struct ElementInfo *ei = &element_info[element];
2362 if (element == EL_MM_GRAY_BALL)
2364 struct LevelInfo_MM *level_mm = level->native_mm_level;
2367 for (j = 0; j < level->num_mm_ball_contents; j++)
2368 level->mm_ball_content[j] =
2369 map_element_MM_to_RND(level_mm->ball_content[j]);
2372 // never initialize clipboard elements after the very first time
2373 // (to be able to use clipboard elements between several levels)
2374 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2377 if (IS_ENVELOPE(element))
2379 int envelope_nr = element - EL_ENVELOPE_1;
2381 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2383 level->envelope[envelope_nr] = xx_envelope;
2386 if (IS_CUSTOM_ELEMENT(element) ||
2387 IS_GROUP_ELEMENT(element) ||
2388 IS_INTERNAL_ELEMENT(element))
2390 xx_ei = *ei; // copy element data into temporary buffer
2392 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2397 setElementChangePages(ei, 1);
2398 setElementChangeInfoToDefaults(ei->change);
2400 if (IS_CUSTOM_ELEMENT(element) ||
2401 IS_GROUP_ELEMENT(element))
2403 setElementDescriptionToDefault(ei);
2405 ei->modified_settings = FALSE;
2408 if (IS_CUSTOM_ELEMENT(element) ||
2409 IS_INTERNAL_ELEMENT(element))
2411 // internal values used in level editor
2413 ei->access_type = 0;
2414 ei->access_layer = 0;
2415 ei->access_protected = 0;
2416 ei->walk_to_action = 0;
2417 ei->smash_targets = 0;
2420 ei->can_explode_by_fire = FALSE;
2421 ei->can_explode_smashed = FALSE;
2422 ei->can_explode_impact = FALSE;
2424 ei->current_change_page = 0;
2427 if (IS_GROUP_ELEMENT(element) ||
2428 IS_INTERNAL_ELEMENT(element))
2430 struct ElementGroupInfo *group;
2432 // initialize memory for list of elements in group
2433 if (ei->group == NULL)
2434 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2438 xx_group = *group; // copy group data into temporary buffer
2440 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2445 if (IS_EMPTY_ELEMENT(element) ||
2446 IS_INTERNAL_ELEMENT(element))
2448 xx_ei = *ei; // copy element data into temporary buffer
2450 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2456 clipboard_elements_initialized = TRUE;
2459 static void setLevelInfoToDefaults(struct LevelInfo *level,
2460 boolean level_info_only,
2461 boolean reset_file_status)
2463 setLevelInfoToDefaults_Level(level);
2465 if (!level_info_only)
2466 setLevelInfoToDefaults_Elements(level);
2468 if (reset_file_status)
2470 level->no_valid_file = FALSE;
2471 level->no_level_file = FALSE;
2474 level->changed = FALSE;
2477 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2479 level_file_info->nr = 0;
2480 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2481 level_file_info->packed = FALSE;
2483 setString(&level_file_info->basename, NULL);
2484 setString(&level_file_info->filename, NULL);
2487 int getMappedElement_SB(int, boolean);
2489 static void ActivateLevelTemplate(void)
2493 if (check_special_flags("load_xsb_to_ces"))
2495 // fill smaller playfields with padding "beyond border wall" elements
2496 if (level.fieldx < level_template.fieldx ||
2497 level.fieldy < level_template.fieldy)
2499 short field[level.fieldx][level.fieldy];
2500 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2501 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2502 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2503 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2505 // copy old playfield (which is smaller than the visible area)
2506 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2507 field[x][y] = level.field[x][y];
2509 // fill new, larger playfield with "beyond border wall" elements
2510 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2511 level.field[x][y] = getMappedElement_SB('_', TRUE);
2513 // copy the old playfield to the middle of the new playfield
2514 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2515 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2517 level.fieldx = new_fieldx;
2518 level.fieldy = new_fieldy;
2522 // Currently there is no special action needed to activate the template
2523 // data, because 'element_info' property settings overwrite the original
2524 // level data, while all other variables do not change.
2526 // Exception: 'from_level_template' elements in the original level playfield
2527 // are overwritten with the corresponding elements at the same position in
2528 // playfield from the level template.
2530 for (x = 0; x < level.fieldx; x++)
2531 for (y = 0; y < level.fieldy; y++)
2532 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2533 level.field[x][y] = level_template.field[x][y];
2535 if (check_special_flags("load_xsb_to_ces"))
2537 struct LevelInfo level_backup = level;
2539 // overwrite all individual level settings from template level settings
2540 level = level_template;
2542 // restore level file info
2543 level.file_info = level_backup.file_info;
2545 // restore playfield size
2546 level.fieldx = level_backup.fieldx;
2547 level.fieldy = level_backup.fieldy;
2549 // restore playfield content
2550 for (x = 0; x < level.fieldx; x++)
2551 for (y = 0; y < level.fieldy; y++)
2552 level.field[x][y] = level_backup.field[x][y];
2554 // restore name and author from individual level
2555 strcpy(level.name, level_backup.name);
2556 strcpy(level.author, level_backup.author);
2558 // restore flag "use_custom_template"
2559 level.use_custom_template = level_backup.use_custom_template;
2563 static boolean checkForPackageFromBasename_BD(char *basename)
2565 // check for native BD level file extensions
2566 if (!strSuffixLower(basename, ".bd") &&
2567 !strSuffixLower(basename, ".bdr") &&
2568 !strSuffixLower(basename, ".brc") &&
2569 !strSuffixLower(basename, ".gds"))
2572 // check for standard single-level BD files (like "001.bd")
2573 if (strSuffixLower(basename, ".bd") &&
2574 strlen(basename) == 6 &&
2575 basename[0] >= '0' && basename[0] <= '9' &&
2576 basename[1] >= '0' && basename[1] <= '9' &&
2577 basename[2] >= '0' && basename[2] <= '9')
2580 // this is a level package in native BD file format
2584 static char *getLevelFilenameFromBasename(char *basename)
2586 static char *filename = NULL;
2588 checked_free(filename);
2590 filename = getPath2(getCurrentLevelDir(), basename);
2595 static int getFileTypeFromBasename(char *basename)
2597 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2599 static char *filename = NULL;
2600 struct stat file_status;
2602 // ---------- try to determine file type from filename ----------
2604 // check for typical filename of a Supaplex level package file
2605 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2606 return LEVEL_FILE_TYPE_SP;
2608 // check for typical filename of a Diamond Caves II level package file
2609 if (strSuffixLower(basename, ".dc") ||
2610 strSuffixLower(basename, ".dc2"))
2611 return LEVEL_FILE_TYPE_DC;
2613 // check for typical filename of a Sokoban level package file
2614 if (strSuffixLower(basename, ".xsb") &&
2615 strchr(basename, '%') == NULL)
2616 return LEVEL_FILE_TYPE_SB;
2618 // check for typical filename of a Boulder Dash (GDash) level package file
2619 if (checkForPackageFromBasename_BD(basename))
2620 return LEVEL_FILE_TYPE_BD;
2622 // ---------- try to determine file type from filesize ----------
2624 checked_free(filename);
2625 filename = getPath2(getCurrentLevelDir(), basename);
2627 if (stat(filename, &file_status) == 0)
2629 // check for typical filesize of a Supaplex level package file
2630 if (file_status.st_size == 170496)
2631 return LEVEL_FILE_TYPE_SP;
2634 return LEVEL_FILE_TYPE_UNKNOWN;
2637 static int getFileTypeFromMagicBytes(char *filename, int type)
2641 if ((file = openFile(filename, MODE_READ)))
2643 char chunk_name[CHUNK_ID_LEN + 1];
2645 getFileChunkBE(file, chunk_name, NULL);
2647 if (strEqual(chunk_name, "MMII") ||
2648 strEqual(chunk_name, "MIRR"))
2649 type = LEVEL_FILE_TYPE_MM;
2657 static boolean checkForPackageFromBasename(char *basename)
2659 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2660 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2662 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2665 static char *getSingleLevelBasenameExt(int nr, char *extension)
2667 static char basename[MAX_FILENAME_LEN];
2670 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2672 sprintf(basename, "%03d.%s", nr, extension);
2677 static char *getSingleLevelBasename(int nr)
2679 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2682 static char *getPackedLevelBasename(int type)
2684 static char basename[MAX_FILENAME_LEN];
2685 char *directory = getCurrentLevelDir();
2687 DirectoryEntry *dir_entry;
2689 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2691 if ((dir = openDirectory(directory)) == NULL)
2693 Warn("cannot read current level directory '%s'", directory);
2698 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2700 char *entry_basename = dir_entry->basename;
2701 int entry_type = getFileTypeFromBasename(entry_basename);
2703 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2705 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2708 strcpy(basename, entry_basename);
2715 closeDirectory(dir);
2720 static char *getSingleLevelFilename(int nr)
2722 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2725 #if ENABLE_UNUSED_CODE
2726 static char *getPackedLevelFilename(int type)
2728 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2732 char *getDefaultLevelFilename(int nr)
2734 return getSingleLevelFilename(nr);
2737 #if ENABLE_UNUSED_CODE
2738 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2742 lfi->packed = FALSE;
2744 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2745 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2749 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2750 int type, char *format, ...)
2752 static char basename[MAX_FILENAME_LEN];
2755 va_start(ap, format);
2756 vsprintf(basename, format, ap);
2760 lfi->packed = FALSE;
2762 setString(&lfi->basename, basename);
2763 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2766 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2772 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2773 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2776 static int getFiletypeFromID(char *filetype_id)
2778 char *filetype_id_lower;
2779 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2782 if (filetype_id == NULL)
2783 return LEVEL_FILE_TYPE_UNKNOWN;
2785 filetype_id_lower = getStringToLower(filetype_id);
2787 for (i = 0; filetype_id_list[i].id != NULL; i++)
2789 char *id_lower = getStringToLower(filetype_id_list[i].id);
2791 if (strEqual(filetype_id_lower, id_lower))
2792 filetype = filetype_id_list[i].filetype;
2796 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2800 free(filetype_id_lower);
2805 char *getLocalLevelTemplateFilename(void)
2807 return getDefaultLevelFilename(-1);
2810 char *getGlobalLevelTemplateFilename(void)
2812 // global variable "leveldir_current" must be modified in the loop below
2813 LevelDirTree *leveldir_current_last = leveldir_current;
2814 char *filename = NULL;
2816 // check for template level in path from current to topmost tree node
2818 while (leveldir_current != NULL)
2820 filename = getDefaultLevelFilename(-1);
2822 if (fileExists(filename))
2825 leveldir_current = leveldir_current->node_parent;
2828 // restore global variable "leveldir_current" modified in above loop
2829 leveldir_current = leveldir_current_last;
2834 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2838 // special case: level number is negative => check for level template file
2841 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2842 getSingleLevelBasename(-1));
2844 // replace local level template filename with global template filename
2845 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2847 // no fallback if template file not existing
2851 // special case: check for file name/pattern specified in "levelinfo.conf"
2852 if (leveldir_current->level_filename != NULL)
2854 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2856 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2857 leveldir_current->level_filename, nr);
2859 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2861 if (fileExists(lfi->filename))
2864 else if (leveldir_current->level_filetype != NULL)
2866 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2868 // check for specified native level file with standard file name
2869 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2870 "%03d.%s", nr, LEVELFILE_EXTENSION);
2871 if (fileExists(lfi->filename))
2875 // check for native Rocks'n'Diamonds level file
2876 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2877 "%03d.%s", nr, LEVELFILE_EXTENSION);
2878 if (fileExists(lfi->filename))
2881 // check for native Boulder Dash level file
2882 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2883 if (fileExists(lfi->filename))
2886 // check for Emerald Mine level file (V1)
2887 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2888 'a' + (nr / 10) % 26, '0' + nr % 10);
2889 if (fileExists(lfi->filename))
2891 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2892 'A' + (nr / 10) % 26, '0' + nr % 10);
2893 if (fileExists(lfi->filename))
2896 // check for Emerald Mine level file (V2 to V5)
2897 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2898 if (fileExists(lfi->filename))
2901 // check for Emerald Mine level file (V6 / single mode)
2902 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2903 if (fileExists(lfi->filename))
2905 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2906 if (fileExists(lfi->filename))
2909 // check for Emerald Mine level file (V6 / teamwork mode)
2910 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2911 if (fileExists(lfi->filename))
2913 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2914 if (fileExists(lfi->filename))
2917 // check for various packed level file formats
2918 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2919 if (fileExists(lfi->filename))
2922 // no known level file found -- use default values (and fail later)
2923 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2924 "%03d.%s", nr, LEVELFILE_EXTENSION);
2927 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2929 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2930 lfi->type = getFileTypeFromBasename(lfi->basename);
2932 if (lfi->type == LEVEL_FILE_TYPE_RND)
2933 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2936 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2938 // always start with reliable default values
2939 setFileInfoToDefaults(level_file_info);
2941 level_file_info->nr = nr; // set requested level number
2943 determineLevelFileInfo_Filename(level_file_info);
2944 determineLevelFileInfo_Filetype(level_file_info);
2947 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2948 struct LevelFileInfo *lfi_to)
2950 lfi_to->nr = lfi_from->nr;
2951 lfi_to->type = lfi_from->type;
2952 lfi_to->packed = lfi_from->packed;
2954 setString(&lfi_to->basename, lfi_from->basename);
2955 setString(&lfi_to->filename, lfi_from->filename);
2958 // ----------------------------------------------------------------------------
2959 // functions for loading R'n'D level
2960 // ----------------------------------------------------------------------------
2962 int getMappedElement(int element)
2964 // remap some (historic, now obsolete) elements
2968 case EL_PLAYER_OBSOLETE:
2969 element = EL_PLAYER_1;
2972 case EL_KEY_OBSOLETE:
2976 case EL_EM_KEY_1_FILE_OBSOLETE:
2977 element = EL_EM_KEY_1;
2980 case EL_EM_KEY_2_FILE_OBSOLETE:
2981 element = EL_EM_KEY_2;
2984 case EL_EM_KEY_3_FILE_OBSOLETE:
2985 element = EL_EM_KEY_3;
2988 case EL_EM_KEY_4_FILE_OBSOLETE:
2989 element = EL_EM_KEY_4;
2992 case EL_ENVELOPE_OBSOLETE:
2993 element = EL_ENVELOPE_1;
3001 if (element >= NUM_FILE_ELEMENTS)
3003 Warn("invalid level element %d", element);
3005 element = EL_UNKNOWN;
3013 static int getMappedElementByVersion(int element, int game_version)
3015 // remap some elements due to certain game version
3017 if (game_version <= VERSION_IDENT(2,2,0,0))
3019 // map game font elements
3020 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3021 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3022 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3023 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3026 if (game_version < VERSION_IDENT(3,0,0,0))
3028 // map Supaplex gravity tube elements
3029 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3030 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3031 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3032 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3039 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3041 level->file_version = getFileVersion(file);
3042 level->game_version = getFileVersion(file);
3047 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3049 level->creation_date.year = getFile16BitBE(file);
3050 level->creation_date.month = getFile8Bit(file);
3051 level->creation_date.day = getFile8Bit(file);
3053 level->creation_date.src = DATE_SRC_LEVELFILE;
3058 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3060 int initial_player_stepsize;
3061 int initial_player_gravity;
3064 level->fieldx = getFile8Bit(file);
3065 level->fieldy = getFile8Bit(file);
3067 level->time = getFile16BitBE(file);
3068 level->gems_needed = getFile16BitBE(file);
3070 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3071 level->name[i] = getFile8Bit(file);
3072 level->name[MAX_LEVEL_NAME_LEN] = 0;
3074 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3075 level->score[i] = getFile8Bit(file);
3077 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3078 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3079 for (y = 0; y < 3; y++)
3080 for (x = 0; x < 3; x++)
3081 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3083 level->amoeba_speed = getFile8Bit(file);
3084 level->time_magic_wall = getFile8Bit(file);
3085 level->time_wheel = getFile8Bit(file);
3086 level->amoeba_content = getMappedElement(getFile8Bit(file));
3088 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3091 for (i = 0; i < MAX_PLAYERS; i++)
3092 level->initial_player_stepsize[i] = initial_player_stepsize;
3094 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3096 for (i = 0; i < MAX_PLAYERS; i++)
3097 level->initial_player_gravity[i] = initial_player_gravity;
3099 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3100 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3102 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3104 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3105 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3106 level->can_move_into_acid_bits = getFile32BitBE(file);
3107 level->dont_collide_with_bits = getFile8Bit(file);
3109 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3110 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3112 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3113 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3114 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3116 level->game_engine_type = getFile8Bit(file);
3118 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3123 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3127 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3128 level->name[i] = getFile8Bit(file);
3129 level->name[MAX_LEVEL_NAME_LEN] = 0;
3134 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3138 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3139 level->author[i] = getFile8Bit(file);
3140 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3145 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3148 int chunk_size_expected = level->fieldx * level->fieldy;
3150 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3151 stored with 16-bit encoding (and should be twice as big then).
3152 Even worse, playfield data was stored 16-bit when only yamyam content
3153 contained 16-bit elements and vice versa. */
3155 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3156 chunk_size_expected *= 2;
3158 if (chunk_size_expected != chunk_size)
3160 ReadUnusedBytesFromFile(file, chunk_size);
3161 return chunk_size_expected;
3164 for (y = 0; y < level->fieldy; y++)
3165 for (x = 0; x < level->fieldx; x++)
3166 level->field[x][y] =
3167 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3172 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3175 int header_size = 4;
3176 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3177 int chunk_size_expected = header_size + content_size;
3179 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3180 stored with 16-bit encoding (and should be twice as big then).
3181 Even worse, playfield data was stored 16-bit when only yamyam content
3182 contained 16-bit elements and vice versa. */
3184 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3185 chunk_size_expected += content_size;
3187 if (chunk_size_expected != chunk_size)
3189 ReadUnusedBytesFromFile(file, chunk_size);
3190 return chunk_size_expected;
3194 level->num_yamyam_contents = getFile8Bit(file);
3198 // correct invalid number of content fields -- should never happen
3199 if (level->num_yamyam_contents < 1 ||
3200 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3201 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3203 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3204 for (y = 0; y < 3; y++)
3205 for (x = 0; x < 3; x++)
3206 level->yamyam_content[i].e[x][y] =
3207 getMappedElement(level->encoding_16bit_field ?
3208 getFile16BitBE(file) : getFile8Bit(file));
3212 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3217 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3219 element = getMappedElement(getFile16BitBE(file));
3220 num_contents = getFile8Bit(file);
3222 getFile8Bit(file); // content x size (unused)
3223 getFile8Bit(file); // content y size (unused)
3225 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3227 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3228 for (y = 0; y < 3; y++)
3229 for (x = 0; x < 3; x++)
3230 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3232 // correct invalid number of content fields -- should never happen
3233 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3234 num_contents = STD_ELEMENT_CONTENTS;
3236 if (element == EL_YAMYAM)
3238 level->num_yamyam_contents = num_contents;
3240 for (i = 0; i < num_contents; i++)
3241 for (y = 0; y < 3; y++)
3242 for (x = 0; x < 3; x++)
3243 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3245 else if (element == EL_BD_AMOEBA)
3247 level->amoeba_content = content_array[0][0][0];
3251 Warn("cannot load content for element '%d'", element);
3257 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3263 int chunk_size_expected;
3265 element = getMappedElement(getFile16BitBE(file));
3266 if (!IS_ENVELOPE(element))
3267 element = EL_ENVELOPE_1;
3269 envelope_nr = element - EL_ENVELOPE_1;
3271 envelope_len = getFile16BitBE(file);
3273 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3274 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3276 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3278 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3279 if (chunk_size_expected != chunk_size)
3281 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3282 return chunk_size_expected;
3285 for (i = 0; i < envelope_len; i++)
3286 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3291 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3293 int num_changed_custom_elements = getFile16BitBE(file);
3294 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3297 if (chunk_size_expected != chunk_size)
3299 ReadUnusedBytesFromFile(file, chunk_size - 2);
3300 return chunk_size_expected;
3303 for (i = 0; i < num_changed_custom_elements; i++)
3305 int element = getMappedElement(getFile16BitBE(file));
3306 int properties = getFile32BitBE(file);
3308 if (IS_CUSTOM_ELEMENT(element))
3309 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3311 Warn("invalid custom element number %d", element);
3313 // older game versions that wrote level files with CUS1 chunks used
3314 // different default push delay values (not yet stored in level file)
3315 element_info[element].push_delay_fixed = 2;
3316 element_info[element].push_delay_random = 8;
3319 level->file_has_custom_elements = TRUE;
3324 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3326 int num_changed_custom_elements = getFile16BitBE(file);
3327 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3330 if (chunk_size_expected != chunk_size)
3332 ReadUnusedBytesFromFile(file, chunk_size - 2);
3333 return chunk_size_expected;
3336 for (i = 0; i < num_changed_custom_elements; i++)
3338 int element = getMappedElement(getFile16BitBE(file));
3339 int custom_target_element = getMappedElement(getFile16BitBE(file));
3341 if (IS_CUSTOM_ELEMENT(element))
3342 element_info[element].change->target_element = custom_target_element;
3344 Warn("invalid custom element number %d", element);
3347 level->file_has_custom_elements = TRUE;
3352 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3354 int num_changed_custom_elements = getFile16BitBE(file);
3355 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3358 if (chunk_size_expected != chunk_size)
3360 ReadUnusedBytesFromFile(file, chunk_size - 2);
3361 return chunk_size_expected;
3364 for (i = 0; i < num_changed_custom_elements; i++)
3366 int element = getMappedElement(getFile16BitBE(file));
3367 struct ElementInfo *ei = &element_info[element];
3368 unsigned int event_bits;
3370 if (!IS_CUSTOM_ELEMENT(element))
3372 Warn("invalid custom element number %d", element);
3374 element = EL_INTERNAL_DUMMY;
3377 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3378 ei->description[j] = getFile8Bit(file);
3379 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3381 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3383 // some free bytes for future properties and padding
3384 ReadUnusedBytesFromFile(file, 7);
3386 ei->use_gfx_element = getFile8Bit(file);
3387 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3389 ei->collect_score_initial = getFile8Bit(file);
3390 ei->collect_count_initial = getFile8Bit(file);
3392 ei->push_delay_fixed = getFile16BitBE(file);
3393 ei->push_delay_random = getFile16BitBE(file);
3394 ei->move_delay_fixed = getFile16BitBE(file);
3395 ei->move_delay_random = getFile16BitBE(file);
3397 ei->move_pattern = getFile16BitBE(file);
3398 ei->move_direction_initial = getFile8Bit(file);
3399 ei->move_stepsize = getFile8Bit(file);
3401 for (y = 0; y < 3; y++)
3402 for (x = 0; x < 3; x++)
3403 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3405 // bits 0 - 31 of "has_event[]"
3406 event_bits = getFile32BitBE(file);
3407 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3408 if (event_bits & (1u << j))
3409 ei->change->has_event[j] = TRUE;
3411 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3413 ei->change->delay_fixed = getFile16BitBE(file);
3414 ei->change->delay_random = getFile16BitBE(file);
3415 ei->change->delay_frames = getFile16BitBE(file);
3417 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3419 ei->change->explode = getFile8Bit(file);
3420 ei->change->use_target_content = getFile8Bit(file);
3421 ei->change->only_if_complete = getFile8Bit(file);
3422 ei->change->use_random_replace = getFile8Bit(file);
3424 ei->change->random_percentage = getFile8Bit(file);
3425 ei->change->replace_when = getFile8Bit(file);
3427 for (y = 0; y < 3; y++)
3428 for (x = 0; x < 3; x++)
3429 ei->change->target_content.e[x][y] =
3430 getMappedElement(getFile16BitBE(file));
3432 ei->slippery_type = getFile8Bit(file);
3434 // some free bytes for future properties and padding
3435 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3437 // mark that this custom element has been modified
3438 ei->modified_settings = TRUE;
3441 level->file_has_custom_elements = TRUE;
3446 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3448 struct ElementInfo *ei;
3449 int chunk_size_expected;
3453 // ---------- custom element base property values (96 bytes) ----------------
3455 element = getMappedElement(getFile16BitBE(file));
3457 if (!IS_CUSTOM_ELEMENT(element))
3459 Warn("invalid custom element number %d", element);
3461 ReadUnusedBytesFromFile(file, chunk_size - 2);
3466 ei = &element_info[element];
3468 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3469 ei->description[i] = getFile8Bit(file);
3470 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3472 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3474 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3476 ei->num_change_pages = getFile8Bit(file);
3478 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3479 if (chunk_size_expected != chunk_size)
3481 ReadUnusedBytesFromFile(file, chunk_size - 43);
3482 return chunk_size_expected;
3485 ei->ce_value_fixed_initial = getFile16BitBE(file);
3486 ei->ce_value_random_initial = getFile16BitBE(file);
3487 ei->use_last_ce_value = getFile8Bit(file);
3489 ei->use_gfx_element = getFile8Bit(file);
3490 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3492 ei->collect_score_initial = getFile8Bit(file);
3493 ei->collect_count_initial = getFile8Bit(file);
3495 ei->drop_delay_fixed = getFile8Bit(file);
3496 ei->push_delay_fixed = getFile8Bit(file);
3497 ei->drop_delay_random = getFile8Bit(file);
3498 ei->push_delay_random = getFile8Bit(file);
3499 ei->move_delay_fixed = getFile16BitBE(file);
3500 ei->move_delay_random = getFile16BitBE(file);
3502 // bits 0 - 15 of "move_pattern" ...
3503 ei->move_pattern = getFile16BitBE(file);
3504 ei->move_direction_initial = getFile8Bit(file);
3505 ei->move_stepsize = getFile8Bit(file);
3507 ei->slippery_type = getFile8Bit(file);
3509 for (y = 0; y < 3; y++)
3510 for (x = 0; x < 3; x++)
3511 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3513 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3514 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3515 ei->move_leave_type = getFile8Bit(file);
3517 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3518 ei->move_pattern |= (getFile16BitBE(file) << 16);
3520 ei->access_direction = getFile8Bit(file);
3522 ei->explosion_delay = getFile8Bit(file);
3523 ei->ignition_delay = getFile8Bit(file);
3524 ei->explosion_type = getFile8Bit(file);
3526 // some free bytes for future custom property values and padding
3527 ReadUnusedBytesFromFile(file, 1);
3529 // ---------- change page property values (48 bytes) ------------------------
3531 setElementChangePages(ei, ei->num_change_pages);
3533 for (i = 0; i < ei->num_change_pages; i++)
3535 struct ElementChangeInfo *change = &ei->change_page[i];
3536 unsigned int event_bits;
3538 // always start with reliable default values
3539 setElementChangeInfoToDefaults(change);
3541 // bits 0 - 31 of "has_event[]" ...
3542 event_bits = getFile32BitBE(file);
3543 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3544 if (event_bits & (1u << j))
3545 change->has_event[j] = TRUE;
3547 change->target_element = getMappedElement(getFile16BitBE(file));
3549 change->delay_fixed = getFile16BitBE(file);
3550 change->delay_random = getFile16BitBE(file);
3551 change->delay_frames = getFile16BitBE(file);
3553 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3555 change->explode = getFile8Bit(file);
3556 change->use_target_content = getFile8Bit(file);
3557 change->only_if_complete = getFile8Bit(file);
3558 change->use_random_replace = getFile8Bit(file);
3560 change->random_percentage = getFile8Bit(file);
3561 change->replace_when = getFile8Bit(file);
3563 for (y = 0; y < 3; y++)
3564 for (x = 0; x < 3; x++)
3565 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3567 change->can_change = getFile8Bit(file);
3569 change->trigger_side = getFile8Bit(file);
3571 change->trigger_player = getFile8Bit(file);
3572 change->trigger_page = getFile8Bit(file);
3574 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3575 CH_PAGE_ANY : (1 << change->trigger_page));
3577 change->has_action = getFile8Bit(file);
3578 change->action_type = getFile8Bit(file);
3579 change->action_mode = getFile8Bit(file);
3580 change->action_arg = getFile16BitBE(file);
3582 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3583 event_bits = getFile8Bit(file);
3584 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3585 if (event_bits & (1u << (j - 32)))
3586 change->has_event[j] = TRUE;
3589 // mark this custom element as modified
3590 ei->modified_settings = TRUE;
3592 level->file_has_custom_elements = TRUE;
3597 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3599 struct ElementInfo *ei;
3600 struct ElementGroupInfo *group;
3604 element = getMappedElement(getFile16BitBE(file));
3606 if (!IS_GROUP_ELEMENT(element))
3608 Warn("invalid group element number %d", element);
3610 ReadUnusedBytesFromFile(file, chunk_size - 2);
3615 ei = &element_info[element];
3617 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3618 ei->description[i] = getFile8Bit(file);
3619 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3621 group = element_info[element].group;
3623 group->num_elements = getFile8Bit(file);
3625 ei->use_gfx_element = getFile8Bit(file);
3626 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3628 group->choice_mode = getFile8Bit(file);
3630 // some free bytes for future values and padding
3631 ReadUnusedBytesFromFile(file, 3);
3633 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3634 group->element[i] = getMappedElement(getFile16BitBE(file));
3636 // mark this group element as modified
3637 element_info[element].modified_settings = TRUE;
3639 level->file_has_custom_elements = TRUE;
3644 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3645 int element, int real_element)
3647 int micro_chunk_size = 0;
3648 int conf_type = getFile8Bit(file);
3649 int byte_mask = conf_type & CONF_MASK_BYTES;
3650 boolean element_found = FALSE;
3653 micro_chunk_size += 1;
3655 if (byte_mask == CONF_MASK_MULTI_BYTES)
3657 int num_bytes = getFile16BitBE(file);
3658 byte *buffer = checked_malloc(num_bytes);
3660 ReadBytesFromFile(file, buffer, num_bytes);
3662 for (i = 0; conf[i].data_type != -1; i++)
3664 if (conf[i].element == element &&
3665 conf[i].conf_type == conf_type)
3667 int data_type = conf[i].data_type;
3668 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3669 int max_num_entities = conf[i].max_num_entities;
3671 if (num_entities > max_num_entities)
3673 Warn("truncating number of entities for element %d from %d to %d",
3674 element, num_entities, max_num_entities);
3676 num_entities = max_num_entities;
3679 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3680 data_type == TYPE_CONTENT_LIST))
3682 // for element and content lists, zero entities are not allowed
3683 Warn("found empty list of entities for element %d", element);
3685 // do not set "num_entities" here to prevent reading behind buffer
3687 *(int *)(conf[i].num_entities) = 1; // at least one is required
3691 *(int *)(conf[i].num_entities) = num_entities;
3694 element_found = TRUE;
3696 if (data_type == TYPE_STRING)
3698 char *string = (char *)(conf[i].value);
3701 for (j = 0; j < max_num_entities; j++)
3702 string[j] = (j < num_entities ? buffer[j] : '\0');
3704 else if (data_type == TYPE_ELEMENT_LIST)
3706 int *element_array = (int *)(conf[i].value);
3709 for (j = 0; j < num_entities; j++)
3711 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3713 else if (data_type == TYPE_CONTENT_LIST)
3715 struct Content *content= (struct Content *)(conf[i].value);
3718 for (c = 0; c < num_entities; c++)
3719 for (y = 0; y < 3; y++)
3720 for (x = 0; x < 3; x++)
3721 content[c].e[x][y] =
3722 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3725 element_found = FALSE;
3731 checked_free(buffer);
3733 micro_chunk_size += 2 + num_bytes;
3735 else // constant size configuration data (1, 2 or 4 bytes)
3737 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3738 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3739 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3741 for (i = 0; conf[i].data_type != -1; i++)
3743 if (conf[i].element == element &&
3744 conf[i].conf_type == conf_type)
3746 int data_type = conf[i].data_type;
3748 if (data_type == TYPE_ELEMENT)
3749 value = getMappedElement(value);
3751 if (data_type == TYPE_BOOLEAN)
3752 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3754 *(int *) (conf[i].value) = value;
3756 element_found = TRUE;
3762 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3767 char *error_conf_chunk_bytes =
3768 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3769 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3770 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3771 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3772 int error_element = real_element;
3774 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3775 error_conf_chunk_bytes, error_conf_chunk_token,
3776 error_element, EL_NAME(error_element));
3779 return micro_chunk_size;
3782 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3784 int real_chunk_size = 0;
3786 li = *level; // copy level data into temporary buffer
3788 while (!checkEndOfFile(file))
3790 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3792 if (real_chunk_size >= chunk_size)
3796 *level = li; // copy temporary buffer back to level data
3798 return real_chunk_size;
3801 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3803 int real_chunk_size = 0;
3805 li = *level; // copy level data into temporary buffer
3807 while (!checkEndOfFile(file))
3809 int element = getMappedElement(getFile16BitBE(file));
3811 real_chunk_size += 2;
3812 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3814 if (real_chunk_size >= chunk_size)
3818 *level = li; // copy temporary buffer back to level data
3820 return real_chunk_size;
3823 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3825 int real_chunk_size = 0;
3827 li = *level; // copy level data into temporary buffer
3829 while (!checkEndOfFile(file))
3831 int element = getMappedElement(getFile16BitBE(file));
3833 real_chunk_size += 2;
3834 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3836 if (real_chunk_size >= chunk_size)
3840 *level = li; // copy temporary buffer back to level data
3842 return real_chunk_size;
3845 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3847 int element = getMappedElement(getFile16BitBE(file));
3848 int envelope_nr = element - EL_ENVELOPE_1;
3849 int real_chunk_size = 2;
3851 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3853 while (!checkEndOfFile(file))
3855 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3858 if (real_chunk_size >= chunk_size)
3862 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3864 return real_chunk_size;
3867 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3869 int element = getMappedElement(getFile16BitBE(file));
3870 int real_chunk_size = 2;
3871 struct ElementInfo *ei = &element_info[element];
3874 xx_ei = *ei; // copy element data into temporary buffer
3876 xx_ei.num_change_pages = -1;
3878 while (!checkEndOfFile(file))
3880 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3882 if (xx_ei.num_change_pages != -1)
3885 if (real_chunk_size >= chunk_size)
3891 if (ei->num_change_pages == -1)
3893 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3896 ei->num_change_pages = 1;
3898 setElementChangePages(ei, 1);
3899 setElementChangeInfoToDefaults(ei->change);
3901 return real_chunk_size;
3904 // initialize number of change pages stored for this custom element
3905 setElementChangePages(ei, ei->num_change_pages);
3906 for (i = 0; i < ei->num_change_pages; i++)
3907 setElementChangeInfoToDefaults(&ei->change_page[i]);
3909 // start with reading properties for the first change page
3910 xx_current_change_page = 0;
3912 while (!checkEndOfFile(file))
3914 // level file might contain invalid change page number
3915 if (xx_current_change_page >= ei->num_change_pages)
3918 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3920 xx_change = *change; // copy change data into temporary buffer
3922 resetEventBits(); // reset bits; change page might have changed
3924 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3927 *change = xx_change;
3929 setEventFlagsFromEventBits(change);
3931 if (real_chunk_size >= chunk_size)
3935 level->file_has_custom_elements = TRUE;
3937 return real_chunk_size;
3940 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3942 int element = getMappedElement(getFile16BitBE(file));
3943 int real_chunk_size = 2;
3944 struct ElementInfo *ei = &element_info[element];
3945 struct ElementGroupInfo *group = ei->group;
3950 xx_ei = *ei; // copy element data into temporary buffer
3951 xx_group = *group; // copy group data into temporary buffer
3953 while (!checkEndOfFile(file))
3955 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3958 if (real_chunk_size >= chunk_size)
3965 level->file_has_custom_elements = TRUE;
3967 return real_chunk_size;
3970 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3972 int element = getMappedElement(getFile16BitBE(file));
3973 int real_chunk_size = 2;
3974 struct ElementInfo *ei = &element_info[element];
3976 xx_ei = *ei; // copy element data into temporary buffer
3978 while (!checkEndOfFile(file))
3980 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3983 if (real_chunk_size >= chunk_size)
3989 level->file_has_custom_elements = TRUE;
3991 return real_chunk_size;
3994 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3995 struct LevelFileInfo *level_file_info,
3996 boolean level_info_only)
3998 char *filename = level_file_info->filename;
3999 char cookie[MAX_LINE_LEN];
4000 char chunk_name[CHUNK_ID_LEN + 1];
4004 if (!(file = openFile(filename, MODE_READ)))
4006 level->no_valid_file = TRUE;
4007 level->no_level_file = TRUE;
4009 if (level_info_only)
4012 Warn("cannot read level '%s' -- using empty level", filename);
4014 if (!setup.editor.use_template_for_new_levels)
4017 // if level file not found, try to initialize level data from template
4018 filename = getGlobalLevelTemplateFilename();
4020 if (!(file = openFile(filename, MODE_READ)))
4023 // default: for empty levels, use level template for custom elements
4024 level->use_custom_template = TRUE;
4026 level->no_valid_file = FALSE;
4029 getFileChunkBE(file, chunk_name, NULL);
4030 if (strEqual(chunk_name, "RND1"))
4032 getFile32BitBE(file); // not used
4034 getFileChunkBE(file, chunk_name, NULL);
4035 if (!strEqual(chunk_name, "CAVE"))
4037 level->no_valid_file = TRUE;
4039 Warn("unknown format of level file '%s'", filename);
4046 else // check for pre-2.0 file format with cookie string
4048 strcpy(cookie, chunk_name);
4049 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4051 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4052 cookie[strlen(cookie) - 1] = '\0';
4054 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4056 level->no_valid_file = TRUE;
4058 Warn("unknown format of level file '%s'", filename);
4065 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4067 level->no_valid_file = TRUE;
4069 Warn("unsupported version of level file '%s'", filename);
4076 // pre-2.0 level files have no game version, so use file version here
4077 level->game_version = level->file_version;
4080 if (level->file_version < FILE_VERSION_1_2)
4082 // level files from versions before 1.2.0 without chunk structure
4083 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4084 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4092 int (*loader)(File *, int, struct LevelInfo *);
4096 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4097 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4098 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4099 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4100 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4101 { "INFO", -1, LoadLevel_INFO },
4102 { "BODY", -1, LoadLevel_BODY },
4103 { "CONT", -1, LoadLevel_CONT },
4104 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4105 { "CNT3", -1, LoadLevel_CNT3 },
4106 { "CUS1", -1, LoadLevel_CUS1 },
4107 { "CUS2", -1, LoadLevel_CUS2 },
4108 { "CUS3", -1, LoadLevel_CUS3 },
4109 { "CUS4", -1, LoadLevel_CUS4 },
4110 { "GRP1", -1, LoadLevel_GRP1 },
4111 { "CONF", -1, LoadLevel_CONF },
4112 { "ELEM", -1, LoadLevel_ELEM },
4113 { "NOTE", -1, LoadLevel_NOTE },
4114 { "CUSX", -1, LoadLevel_CUSX },
4115 { "GRPX", -1, LoadLevel_GRPX },
4116 { "EMPX", -1, LoadLevel_EMPX },
4121 while (getFileChunkBE(file, chunk_name, &chunk_size))
4125 while (chunk_info[i].name != NULL &&
4126 !strEqual(chunk_name, chunk_info[i].name))
4129 if (chunk_info[i].name == NULL)
4131 Warn("unknown chunk '%s' in level file '%s'",
4132 chunk_name, filename);
4134 ReadUnusedBytesFromFile(file, chunk_size);
4136 else if (chunk_info[i].size != -1 &&
4137 chunk_info[i].size != chunk_size)
4139 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4140 chunk_size, chunk_name, filename);
4142 ReadUnusedBytesFromFile(file, chunk_size);
4146 // call function to load this level chunk
4147 int chunk_size_expected =
4148 (chunk_info[i].loader)(file, chunk_size, level);
4150 if (chunk_size_expected < 0)
4152 Warn("error reading chunk '%s' in level file '%s'",
4153 chunk_name, filename);
4158 // the size of some chunks cannot be checked before reading other
4159 // chunks first (like "HEAD" and "BODY") that contain some header
4160 // information, so check them here
4161 if (chunk_size_expected != chunk_size)
4163 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4164 chunk_size, chunk_name, filename);
4176 // ----------------------------------------------------------------------------
4177 // functions for loading BD level
4178 // ----------------------------------------------------------------------------
4180 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4181 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4183 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4185 struct LevelInfo_BD *level_bd = level->native_bd_level;
4186 GdCave *cave = NULL; // will be changed below
4187 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4188 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4191 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4193 // cave and map newly allocated when set to defaults above
4194 cave = level_bd->cave;
4197 cave->intermission = level->bd_intermission;
4200 cave->level_time[0] = level->time;
4201 cave->level_diamonds[0] = level->gems_needed;
4204 cave->scheduling = level->bd_scheduling_type;
4205 cave->pal_timing = level->bd_pal_timing;
4206 cave->level_speed[0] = level->bd_cycle_delay_ms;
4207 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4208 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4209 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4212 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4213 cave->diamond_value = level->score[SC_EMERALD];
4214 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4216 // compatibility settings
4217 cave->lineshift = level->bd_line_shifting_borders;
4218 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4219 cave->short_explosions = level->bd_short_explosions;
4221 // player properties
4222 cave->diagonal_movements = level->bd_diagonal_movements;
4223 cave->active_is_first_found = level->bd_topmost_player_active;
4224 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4225 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4226 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4227 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4229 // element properties
4230 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4231 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4232 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4233 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4234 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4235 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4236 cave->level_magic_wall_time[0] = level->time_magic_wall;
4237 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4238 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4240 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4241 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4242 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4243 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4244 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4245 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4246 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4248 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4249 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4250 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4251 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4252 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4253 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4254 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4255 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4256 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4257 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4258 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4260 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4261 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4262 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4263 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4264 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4265 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4267 cave->slime_predictable = level->bd_slime_is_predictable;
4268 cave->slime_correct_random = level->bd_slime_correct_random;
4269 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4270 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4271 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4272 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4273 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4274 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4275 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4276 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4277 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4278 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4280 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4281 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4282 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4284 cave->biter_delay_frame = level->bd_biter_move_delay;
4285 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4287 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4289 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4291 cave->replicators_active = level->bd_replicators_active;
4292 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4294 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4295 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4297 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4299 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4301 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4302 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4303 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4305 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4306 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4308 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4309 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4311 cave->creatures_backwards = level->bd_creatures_start_backwards;
4312 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4313 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4315 cave->gravity = level->bd_gravity_direction;
4316 cave->gravity_switch_active = level->bd_gravity_switch_active;
4317 cave->gravity_change_time = level->bd_gravity_switch_delay;
4318 cave->gravity_affects_all = level->bd_gravity_affects_all;
4320 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4321 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4322 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4323 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4325 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4326 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4327 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4328 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4329 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4330 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4333 strncpy(cave->name, level->name, sizeof(GdString));
4334 cave->name[sizeof(GdString) - 1] = '\0';
4336 // playfield elements
4337 for (x = 0; x < cave->w; x++)
4338 for (y = 0; y < cave->h; y++)
4339 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4342 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4344 struct LevelInfo_BD *level_bd = level->native_bd_level;
4345 GdCave *cave = level_bd->cave;
4346 int bd_level_nr = level_bd->level_nr;
4349 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4350 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4353 level->bd_intermission = cave->intermission;
4356 level->time = cave->level_time[bd_level_nr];
4357 level->gems_needed = cave->level_diamonds[bd_level_nr];
4360 level->bd_scheduling_type = cave->scheduling;
4361 level->bd_pal_timing = cave->pal_timing;
4362 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4363 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4364 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4365 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4368 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4369 level->score[SC_EMERALD] = cave->diamond_value;
4370 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4372 // compatibility settings
4373 level->bd_line_shifting_borders = cave->lineshift;
4374 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4375 level->bd_short_explosions = cave->short_explosions;
4377 // player properties
4378 level->bd_diagonal_movements = cave->diagonal_movements;
4379 level->bd_topmost_player_active = cave->active_is_first_found;
4380 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4381 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4382 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4383 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4385 // element properties
4386 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4387 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4388 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4389 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4390 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4391 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4392 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4393 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4394 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4396 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4397 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4398 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4399 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4400 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4401 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4402 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4404 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4405 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4406 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4407 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4408 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4409 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4410 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4411 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4412 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4413 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4414 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4416 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4417 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4418 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4419 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4420 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4421 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4423 level->bd_slime_is_predictable = cave->slime_predictable;
4424 level->bd_slime_correct_random = cave->slime_correct_random;
4425 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4426 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4427 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4428 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4429 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4430 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4431 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4432 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4433 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4434 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4436 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4437 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4438 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4440 level->bd_biter_move_delay = cave->biter_delay_frame;
4441 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4443 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4445 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4447 level->bd_replicators_active = cave->replicators_active;
4448 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4450 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4451 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4453 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4455 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4457 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4458 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4459 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4461 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4462 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4464 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4465 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4467 level->bd_creatures_start_backwards = cave->creatures_backwards;
4468 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4469 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4471 level->bd_gravity_direction = cave->gravity;
4472 level->bd_gravity_switch_active = cave->gravity_switch_active;
4473 level->bd_gravity_switch_delay = cave->gravity_change_time;
4474 level->bd_gravity_affects_all = cave->gravity_affects_all;
4476 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4477 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4478 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4479 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4481 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4482 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4483 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4484 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4485 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4486 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4489 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4491 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4492 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4494 // playfield elements
4495 for (x = 0; x < level->fieldx; x++)
4496 for (y = 0; y < level->fieldy; y++)
4497 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4499 checked_free(cave_name);
4502 static void setTapeInfoToDefaults(void);
4504 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4506 struct LevelInfo_BD *level_bd = level->native_bd_level;
4507 GdCave *cave = level_bd->cave;
4508 GdReplay *replay = level_bd->replay;
4514 // always start with reliable default values
4515 setTapeInfoToDefaults();
4517 tape.level_nr = level_nr; // (currently not used)
4518 tape.random_seed = replay->seed;
4520 TapeSetDateFromIsoDateString(replay->date);
4523 tape.pos[tape.counter].delay = 0;
4525 tape.bd_replay = TRUE;
4527 // all time calculations only used to display approximate tape time
4528 int cave_speed = cave->speed;
4529 int milliseconds_game = 0;
4530 int milliseconds_elapsed = 20;
4532 for (i = 0; i < replay->movements->len; i++)
4534 int replay_action = replay->movements->data[i];
4535 int tape_action = map_action_BD_to_RND(replay_action);
4536 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4537 boolean success = 0;
4541 success = TapeAddAction(action);
4543 milliseconds_game += milliseconds_elapsed;
4545 if (milliseconds_game >= cave_speed)
4547 milliseconds_game -= cave_speed;
4554 tape.pos[tape.counter].delay = 0;
4555 tape.pos[tape.counter].action[0] = 0;
4559 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4565 TapeHaltRecording();
4569 // ----------------------------------------------------------------------------
4570 // functions for loading EM level
4571 // ----------------------------------------------------------------------------
4573 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4575 static int ball_xy[8][2] =
4586 struct LevelInfo_EM *level_em = level->native_em_level;
4587 struct CAVE *cav = level_em->cav;
4590 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4591 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4593 cav->time_seconds = level->time;
4594 cav->gems_needed = level->gems_needed;
4596 cav->emerald_score = level->score[SC_EMERALD];
4597 cav->diamond_score = level->score[SC_DIAMOND];
4598 cav->alien_score = level->score[SC_ROBOT];
4599 cav->tank_score = level->score[SC_SPACESHIP];
4600 cav->bug_score = level->score[SC_BUG];
4601 cav->eater_score = level->score[SC_YAMYAM];
4602 cav->nut_score = level->score[SC_NUT];
4603 cav->dynamite_score = level->score[SC_DYNAMITE];
4604 cav->key_score = level->score[SC_KEY];
4605 cav->exit_score = level->score[SC_TIME_BONUS];
4607 cav->num_eater_arrays = level->num_yamyam_contents;
4609 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4610 for (y = 0; y < 3; y++)
4611 for (x = 0; x < 3; x++)
4612 cav->eater_array[i][y * 3 + x] =
4613 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4615 cav->amoeba_time = level->amoeba_speed;
4616 cav->wonderwall_time = level->time_magic_wall;
4617 cav->wheel_time = level->time_wheel;
4619 cav->android_move_time = level->android_move_time;
4620 cav->android_clone_time = level->android_clone_time;
4621 cav->ball_random = level->ball_random;
4622 cav->ball_active = level->ball_active_initial;
4623 cav->ball_time = level->ball_time;
4624 cav->num_ball_arrays = level->num_ball_contents;
4626 cav->lenses_score = level->lenses_score;
4627 cav->magnify_score = level->magnify_score;
4628 cav->slurp_score = level->slurp_score;
4630 cav->lenses_time = level->lenses_time;
4631 cav->magnify_time = level->magnify_time;
4633 cav->wind_time = 9999;
4634 cav->wind_direction =
4635 map_direction_RND_to_EM(level->wind_direction_initial);
4637 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4638 for (j = 0; j < 8; j++)
4639 cav->ball_array[i][j] =
4640 map_element_RND_to_EM_cave(level->ball_content[i].
4641 e[ball_xy[j][0]][ball_xy[j][1]]);
4643 map_android_clone_elements_RND_to_EM(level);
4645 // first fill the complete playfield with the empty space element
4646 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4647 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4648 cav->cave[x][y] = Cblank;
4650 // then copy the real level contents from level file into the playfield
4651 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4653 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4655 if (level->field[x][y] == EL_AMOEBA_DEAD)
4656 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4658 cav->cave[x][y] = new_element;
4661 for (i = 0; i < MAX_PLAYERS; i++)
4663 cav->player_x[i] = -1;
4664 cav->player_y[i] = -1;
4667 // initialize player positions and delete players from the playfield
4668 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4670 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4672 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4674 cav->player_x[player_nr] = x;
4675 cav->player_y[player_nr] = y;
4677 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4682 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4684 static int ball_xy[8][2] =
4695 struct LevelInfo_EM *level_em = level->native_em_level;
4696 struct CAVE *cav = level_em->cav;
4699 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4700 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4702 level->time = cav->time_seconds;
4703 level->gems_needed = cav->gems_needed;
4705 sprintf(level->name, "Level %d", level->file_info.nr);
4707 level->score[SC_EMERALD] = cav->emerald_score;
4708 level->score[SC_DIAMOND] = cav->diamond_score;
4709 level->score[SC_ROBOT] = cav->alien_score;
4710 level->score[SC_SPACESHIP] = cav->tank_score;
4711 level->score[SC_BUG] = cav->bug_score;
4712 level->score[SC_YAMYAM] = cav->eater_score;
4713 level->score[SC_NUT] = cav->nut_score;
4714 level->score[SC_DYNAMITE] = cav->dynamite_score;
4715 level->score[SC_KEY] = cav->key_score;
4716 level->score[SC_TIME_BONUS] = cav->exit_score;
4718 level->num_yamyam_contents = cav->num_eater_arrays;
4720 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4721 for (y = 0; y < 3; y++)
4722 for (x = 0; x < 3; x++)
4723 level->yamyam_content[i].e[x][y] =
4724 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4726 level->amoeba_speed = cav->amoeba_time;
4727 level->time_magic_wall = cav->wonderwall_time;
4728 level->time_wheel = cav->wheel_time;
4730 level->android_move_time = cav->android_move_time;
4731 level->android_clone_time = cav->android_clone_time;
4732 level->ball_random = cav->ball_random;
4733 level->ball_active_initial = cav->ball_active;
4734 level->ball_time = cav->ball_time;
4735 level->num_ball_contents = cav->num_ball_arrays;
4737 level->lenses_score = cav->lenses_score;
4738 level->magnify_score = cav->magnify_score;
4739 level->slurp_score = cav->slurp_score;
4741 level->lenses_time = cav->lenses_time;
4742 level->magnify_time = cav->magnify_time;
4744 level->wind_direction_initial =
4745 map_direction_EM_to_RND(cav->wind_direction);
4747 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4748 for (j = 0; j < 8; j++)
4749 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4750 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4752 map_android_clone_elements_EM_to_RND(level);
4754 // convert the playfield (some elements need special treatment)
4755 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4757 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4759 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4760 new_element = EL_AMOEBA_DEAD;
4762 level->field[x][y] = new_element;
4765 for (i = 0; i < MAX_PLAYERS; i++)
4767 // in case of all players set to the same field, use the first player
4768 int nr = MAX_PLAYERS - i - 1;
4769 int jx = cav->player_x[nr];
4770 int jy = cav->player_y[nr];
4772 if (jx != -1 && jy != -1)
4773 level->field[jx][jy] = EL_PLAYER_1 + nr;
4776 // time score is counted for each 10 seconds left in Emerald Mine levels
4777 level->time_score_base = 10;
4781 // ----------------------------------------------------------------------------
4782 // functions for loading SP level
4783 // ----------------------------------------------------------------------------
4785 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4787 struct LevelInfo_SP *level_sp = level->native_sp_level;
4788 LevelInfoType *header = &level_sp->header;
4791 level_sp->width = level->fieldx;
4792 level_sp->height = level->fieldy;
4794 for (x = 0; x < level->fieldx; x++)
4795 for (y = 0; y < level->fieldy; y++)
4796 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4798 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4800 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4801 header->LevelTitle[i] = level->name[i];
4802 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4804 header->InfotronsNeeded = level->gems_needed;
4806 header->SpecialPortCount = 0;
4808 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4810 boolean gravity_port_found = FALSE;
4811 boolean gravity_port_valid = FALSE;
4812 int gravity_port_flag;
4813 int gravity_port_base_element;
4814 int element = level->field[x][y];
4816 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4817 element <= EL_SP_GRAVITY_ON_PORT_UP)
4819 gravity_port_found = TRUE;
4820 gravity_port_valid = TRUE;
4821 gravity_port_flag = 1;
4822 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4824 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4825 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4827 gravity_port_found = TRUE;
4828 gravity_port_valid = TRUE;
4829 gravity_port_flag = 0;
4830 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4832 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4833 element <= EL_SP_GRAVITY_PORT_UP)
4835 // change R'n'D style gravity inverting special port to normal port
4836 // (there are no gravity inverting ports in native Supaplex engine)
4838 gravity_port_found = TRUE;
4839 gravity_port_valid = FALSE;
4840 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4843 if (gravity_port_found)
4845 if (gravity_port_valid &&
4846 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4848 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4850 port->PortLocation = (y * level->fieldx + x) * 2;
4851 port->Gravity = gravity_port_flag;
4853 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4855 header->SpecialPortCount++;
4859 // change special gravity port to normal port
4861 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4864 level_sp->playfield[x][y] = element - EL_SP_START;
4869 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4871 struct LevelInfo_SP *level_sp = level->native_sp_level;
4872 LevelInfoType *header = &level_sp->header;
4873 boolean num_invalid_elements = 0;
4876 level->fieldx = level_sp->width;
4877 level->fieldy = level_sp->height;
4879 for (x = 0; x < level->fieldx; x++)
4881 for (y = 0; y < level->fieldy; y++)
4883 int element_old = level_sp->playfield[x][y];
4884 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4886 if (element_new == EL_UNKNOWN)
4888 num_invalid_elements++;
4890 Debug("level:native:SP", "invalid element %d at position %d, %d",
4894 level->field[x][y] = element_new;
4898 if (num_invalid_elements > 0)
4899 Warn("found %d invalid elements%s", num_invalid_elements,
4900 (!options.debug ? " (use '--debug' for more details)" : ""));
4902 for (i = 0; i < MAX_PLAYERS; i++)
4903 level->initial_player_gravity[i] =
4904 (header->InitialGravity == 1 ? TRUE : FALSE);
4906 // skip leading spaces
4907 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4908 if (header->LevelTitle[i] != ' ')
4912 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4913 level->name[j] = header->LevelTitle[i];
4914 level->name[j] = '\0';
4916 // cut trailing spaces
4918 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4919 level->name[j - 1] = '\0';
4921 level->gems_needed = header->InfotronsNeeded;
4923 for (i = 0; i < header->SpecialPortCount; i++)
4925 SpecialPortType *port = &header->SpecialPort[i];
4926 int port_location = port->PortLocation;
4927 int gravity = port->Gravity;
4928 int port_x, port_y, port_element;
4930 port_x = (port_location / 2) % level->fieldx;
4931 port_y = (port_location / 2) / level->fieldx;
4933 if (port_x < 0 || port_x >= level->fieldx ||
4934 port_y < 0 || port_y >= level->fieldy)
4936 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4941 port_element = level->field[port_x][port_y];
4943 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4944 port_element > EL_SP_GRAVITY_PORT_UP)
4946 Warn("no special port at position (%d, %d)", port_x, port_y);
4951 // change previous (wrong) gravity inverting special port to either
4952 // gravity enabling special port or gravity disabling special port
4953 level->field[port_x][port_y] +=
4954 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4955 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4958 // change special gravity ports without database entries to normal ports
4959 for (x = 0; x < level->fieldx; x++)
4960 for (y = 0; y < level->fieldy; y++)
4961 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4962 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4963 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4965 level->time = 0; // no time limit
4966 level->amoeba_speed = 0;
4967 level->time_magic_wall = 0;
4968 level->time_wheel = 0;
4969 level->amoeba_content = EL_EMPTY;
4971 // original Supaplex does not use score values -- rate by playing time
4972 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4973 level->score[i] = 0;
4975 level->rate_time_over_score = TRUE;
4977 // there are no yamyams in supaplex levels
4978 for (i = 0; i < level->num_yamyam_contents; i++)
4979 for (x = 0; x < 3; x++)
4980 for (y = 0; y < 3; y++)
4981 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4984 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4986 struct LevelInfo_SP *level_sp = level->native_sp_level;
4987 struct DemoInfo_SP *demo = &level_sp->demo;
4990 // always start with reliable default values
4991 demo->is_available = FALSE;
4994 if (TAPE_IS_EMPTY(tape))
4997 demo->level_nr = tape.level_nr; // (currently not used)
4999 level_sp->header.DemoRandomSeed = tape.random_seed;
5003 for (i = 0; i < tape.length; i++)
5005 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5006 int demo_repeat = tape.pos[i].delay;
5007 int demo_entries = (demo_repeat + 15) / 16;
5009 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5011 Warn("tape truncated: size exceeds maximum SP demo size %d",
5017 for (j = 0; j < demo_repeat / 16; j++)
5018 demo->data[demo->length++] = 0xf0 | demo_action;
5020 if (demo_repeat % 16)
5021 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5024 demo->is_available = TRUE;
5027 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5029 struct LevelInfo_SP *level_sp = level->native_sp_level;
5030 struct DemoInfo_SP *demo = &level_sp->demo;
5031 char *filename = level->file_info.filename;
5034 // always start with reliable default values
5035 setTapeInfoToDefaults();
5037 if (!demo->is_available)
5040 tape.level_nr = demo->level_nr; // (currently not used)
5041 tape.random_seed = level_sp->header.DemoRandomSeed;
5043 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5046 tape.pos[tape.counter].delay = 0;
5048 for (i = 0; i < demo->length; i++)
5050 int demo_action = demo->data[i] & 0x0f;
5051 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5052 int tape_action = map_key_SP_to_RND(demo_action);
5053 int tape_repeat = demo_repeat + 1;
5054 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5055 boolean success = 0;
5058 for (j = 0; j < tape_repeat; j++)
5059 success = TapeAddAction(action);
5063 Warn("SP demo truncated: size exceeds maximum tape size %d",
5070 TapeHaltRecording();
5074 // ----------------------------------------------------------------------------
5075 // functions for loading MM level
5076 // ----------------------------------------------------------------------------
5078 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5080 struct LevelInfo_MM *level_mm = level->native_mm_level;
5083 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5084 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5086 level_mm->time = level->time;
5087 level_mm->kettles_needed = level->gems_needed;
5088 level_mm->auto_count_kettles = level->auto_count_gems;
5090 level_mm->mm_laser_red = level->mm_laser_red;
5091 level_mm->mm_laser_green = level->mm_laser_green;
5092 level_mm->mm_laser_blue = level->mm_laser_blue;
5094 level_mm->df_laser_red = level->df_laser_red;
5095 level_mm->df_laser_green = level->df_laser_green;
5096 level_mm->df_laser_blue = level->df_laser_blue;
5098 strcpy(level_mm->name, level->name);
5099 strcpy(level_mm->author, level->author);
5101 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5102 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5103 level_mm->score[SC_KEY] = level->score[SC_KEY];
5104 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5105 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5107 level_mm->amoeba_speed = level->amoeba_speed;
5108 level_mm->time_fuse = level->mm_time_fuse;
5109 level_mm->time_bomb = level->mm_time_bomb;
5110 level_mm->time_ball = level->mm_time_ball;
5111 level_mm->time_block = level->mm_time_block;
5113 level_mm->num_ball_contents = level->num_mm_ball_contents;
5114 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5115 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5116 level_mm->explode_ball = level->explode_mm_ball;
5118 for (i = 0; i < level->num_mm_ball_contents; i++)
5119 level_mm->ball_content[i] =
5120 map_element_RND_to_MM(level->mm_ball_content[i]);
5122 for (x = 0; x < level->fieldx; x++)
5123 for (y = 0; y < level->fieldy; y++)
5125 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5128 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5130 struct LevelInfo_MM *level_mm = level->native_mm_level;
5133 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5134 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5136 level->time = level_mm->time;
5137 level->gems_needed = level_mm->kettles_needed;
5138 level->auto_count_gems = level_mm->auto_count_kettles;
5140 level->mm_laser_red = level_mm->mm_laser_red;
5141 level->mm_laser_green = level_mm->mm_laser_green;
5142 level->mm_laser_blue = level_mm->mm_laser_blue;
5144 level->df_laser_red = level_mm->df_laser_red;
5145 level->df_laser_green = level_mm->df_laser_green;
5146 level->df_laser_blue = level_mm->df_laser_blue;
5148 strcpy(level->name, level_mm->name);
5150 // only overwrite author from 'levelinfo.conf' if author defined in level
5151 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5152 strcpy(level->author, level_mm->author);
5154 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5155 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5156 level->score[SC_KEY] = level_mm->score[SC_KEY];
5157 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5158 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5160 level->amoeba_speed = level_mm->amoeba_speed;
5161 level->mm_time_fuse = level_mm->time_fuse;
5162 level->mm_time_bomb = level_mm->time_bomb;
5163 level->mm_time_ball = level_mm->time_ball;
5164 level->mm_time_block = level_mm->time_block;
5166 level->num_mm_ball_contents = level_mm->num_ball_contents;
5167 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5168 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5169 level->explode_mm_ball = level_mm->explode_ball;
5171 for (i = 0; i < level->num_mm_ball_contents; i++)
5172 level->mm_ball_content[i] =
5173 map_element_MM_to_RND(level_mm->ball_content[i]);
5175 for (x = 0; x < level->fieldx; x++)
5176 for (y = 0; y < level->fieldy; y++)
5177 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5181 // ----------------------------------------------------------------------------
5182 // functions for loading DC level
5183 // ----------------------------------------------------------------------------
5185 #define DC_LEVEL_HEADER_SIZE 344
5187 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5190 static int last_data_encoded;
5194 int diff_hi, diff_lo;
5195 int data_hi, data_lo;
5196 unsigned short data_decoded;
5200 last_data_encoded = 0;
5207 diff = data_encoded - last_data_encoded;
5208 diff_hi = diff & ~0xff;
5209 diff_lo = diff & 0xff;
5213 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5214 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5215 data_hi = data_hi & 0xff00;
5217 data_decoded = data_hi | data_lo;
5219 last_data_encoded = data_encoded;
5221 offset1 = (offset1 + 1) % 31;
5222 offset2 = offset2 & 0xff;
5224 return data_decoded;
5227 static int getMappedElement_DC(int element)
5235 // 0x0117 - 0x036e: (?)
5238 // 0x042d - 0x0684: (?)
5254 element = EL_CRYSTAL;
5257 case 0x0e77: // quicksand (boulder)
5258 element = EL_QUICKSAND_FAST_FULL;
5261 case 0x0e99: // slow quicksand (boulder)
5262 element = EL_QUICKSAND_FULL;
5266 element = EL_EM_EXIT_OPEN;
5270 element = EL_EM_EXIT_CLOSED;
5274 element = EL_EM_STEEL_EXIT_OPEN;
5278 element = EL_EM_STEEL_EXIT_CLOSED;
5281 case 0x0f4f: // dynamite (lit 1)
5282 element = EL_EM_DYNAMITE_ACTIVE;
5285 case 0x0f57: // dynamite (lit 2)
5286 element = EL_EM_DYNAMITE_ACTIVE;
5289 case 0x0f5f: // dynamite (lit 3)
5290 element = EL_EM_DYNAMITE_ACTIVE;
5293 case 0x0f67: // dynamite (lit 4)
5294 element = EL_EM_DYNAMITE_ACTIVE;
5301 element = EL_AMOEBA_WET;
5305 element = EL_AMOEBA_DROP;
5309 element = EL_DC_MAGIC_WALL;
5313 element = EL_SPACESHIP_UP;
5317 element = EL_SPACESHIP_DOWN;
5321 element = EL_SPACESHIP_LEFT;
5325 element = EL_SPACESHIP_RIGHT;
5329 element = EL_BUG_UP;
5333 element = EL_BUG_DOWN;
5337 element = EL_BUG_LEFT;
5341 element = EL_BUG_RIGHT;
5345 element = EL_MOLE_UP;
5349 element = EL_MOLE_DOWN;
5353 element = EL_MOLE_LEFT;
5357 element = EL_MOLE_RIGHT;
5365 element = EL_YAMYAM_UP;
5369 element = EL_SWITCHGATE_OPEN;
5373 element = EL_SWITCHGATE_CLOSED;
5377 element = EL_DC_SWITCHGATE_SWITCH_UP;
5381 element = EL_TIMEGATE_CLOSED;
5384 case 0x144c: // conveyor belt switch (green)
5385 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5388 case 0x144f: // conveyor belt switch (red)
5389 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5392 case 0x1452: // conveyor belt switch (blue)
5393 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5397 element = EL_CONVEYOR_BELT_3_MIDDLE;
5401 element = EL_CONVEYOR_BELT_3_LEFT;
5405 element = EL_CONVEYOR_BELT_3_RIGHT;
5409 element = EL_CONVEYOR_BELT_1_MIDDLE;
5413 element = EL_CONVEYOR_BELT_1_LEFT;
5417 element = EL_CONVEYOR_BELT_1_RIGHT;
5421 element = EL_CONVEYOR_BELT_4_MIDDLE;
5425 element = EL_CONVEYOR_BELT_4_LEFT;
5429 element = EL_CONVEYOR_BELT_4_RIGHT;
5433 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5437 element = EL_EXPANDABLE_WALL_VERTICAL;
5441 element = EL_EXPANDABLE_WALL_ANY;
5444 case 0x14ce: // growing steel wall (left/right)
5445 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5448 case 0x14df: // growing steel wall (up/down)
5449 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5452 case 0x14e8: // growing steel wall (up/down/left/right)
5453 element = EL_EXPANDABLE_STEELWALL_ANY;
5457 element = EL_SHIELD_DEADLY;
5461 element = EL_EXTRA_TIME;
5469 element = EL_EMPTY_SPACE;
5472 case 0x1578: // quicksand (empty)
5473 element = EL_QUICKSAND_FAST_EMPTY;
5476 case 0x1579: // slow quicksand (empty)
5477 element = EL_QUICKSAND_EMPTY;
5487 element = EL_EM_DYNAMITE;
5490 case 0x15a1: // key (red)
5491 element = EL_EM_KEY_1;
5494 case 0x15a2: // key (yellow)
5495 element = EL_EM_KEY_2;
5498 case 0x15a3: // key (blue)
5499 element = EL_EM_KEY_4;
5502 case 0x15a4: // key (green)
5503 element = EL_EM_KEY_3;
5506 case 0x15a5: // key (white)
5507 element = EL_DC_KEY_WHITE;
5511 element = EL_WALL_SLIPPERY;
5518 case 0x15a8: // wall (not round)
5522 case 0x15a9: // (blue)
5523 element = EL_CHAR_A;
5526 case 0x15aa: // (blue)
5527 element = EL_CHAR_B;
5530 case 0x15ab: // (blue)
5531 element = EL_CHAR_C;
5534 case 0x15ac: // (blue)
5535 element = EL_CHAR_D;
5538 case 0x15ad: // (blue)
5539 element = EL_CHAR_E;
5542 case 0x15ae: // (blue)
5543 element = EL_CHAR_F;
5546 case 0x15af: // (blue)
5547 element = EL_CHAR_G;
5550 case 0x15b0: // (blue)
5551 element = EL_CHAR_H;
5554 case 0x15b1: // (blue)
5555 element = EL_CHAR_I;
5558 case 0x15b2: // (blue)
5559 element = EL_CHAR_J;
5562 case 0x15b3: // (blue)
5563 element = EL_CHAR_K;
5566 case 0x15b4: // (blue)
5567 element = EL_CHAR_L;
5570 case 0x15b5: // (blue)
5571 element = EL_CHAR_M;
5574 case 0x15b6: // (blue)
5575 element = EL_CHAR_N;
5578 case 0x15b7: // (blue)
5579 element = EL_CHAR_O;
5582 case 0x15b8: // (blue)
5583 element = EL_CHAR_P;
5586 case 0x15b9: // (blue)
5587 element = EL_CHAR_Q;
5590 case 0x15ba: // (blue)
5591 element = EL_CHAR_R;
5594 case 0x15bb: // (blue)
5595 element = EL_CHAR_S;
5598 case 0x15bc: // (blue)
5599 element = EL_CHAR_T;
5602 case 0x15bd: // (blue)
5603 element = EL_CHAR_U;
5606 case 0x15be: // (blue)
5607 element = EL_CHAR_V;
5610 case 0x15bf: // (blue)
5611 element = EL_CHAR_W;
5614 case 0x15c0: // (blue)
5615 element = EL_CHAR_X;
5618 case 0x15c1: // (blue)
5619 element = EL_CHAR_Y;
5622 case 0x15c2: // (blue)
5623 element = EL_CHAR_Z;
5626 case 0x15c3: // (blue)
5627 element = EL_CHAR_AUMLAUT;
5630 case 0x15c4: // (blue)
5631 element = EL_CHAR_OUMLAUT;
5634 case 0x15c5: // (blue)
5635 element = EL_CHAR_UUMLAUT;
5638 case 0x15c6: // (blue)
5639 element = EL_CHAR_0;
5642 case 0x15c7: // (blue)
5643 element = EL_CHAR_1;
5646 case 0x15c8: // (blue)
5647 element = EL_CHAR_2;
5650 case 0x15c9: // (blue)
5651 element = EL_CHAR_3;
5654 case 0x15ca: // (blue)
5655 element = EL_CHAR_4;
5658 case 0x15cb: // (blue)
5659 element = EL_CHAR_5;
5662 case 0x15cc: // (blue)
5663 element = EL_CHAR_6;
5666 case 0x15cd: // (blue)
5667 element = EL_CHAR_7;
5670 case 0x15ce: // (blue)
5671 element = EL_CHAR_8;
5674 case 0x15cf: // (blue)
5675 element = EL_CHAR_9;
5678 case 0x15d0: // (blue)
5679 element = EL_CHAR_PERIOD;
5682 case 0x15d1: // (blue)
5683 element = EL_CHAR_EXCLAM;
5686 case 0x15d2: // (blue)
5687 element = EL_CHAR_COLON;
5690 case 0x15d3: // (blue)
5691 element = EL_CHAR_LESS;
5694 case 0x15d4: // (blue)
5695 element = EL_CHAR_GREATER;
5698 case 0x15d5: // (blue)
5699 element = EL_CHAR_QUESTION;
5702 case 0x15d6: // (blue)
5703 element = EL_CHAR_COPYRIGHT;
5706 case 0x15d7: // (blue)
5707 element = EL_CHAR_UP;
5710 case 0x15d8: // (blue)
5711 element = EL_CHAR_DOWN;
5714 case 0x15d9: // (blue)
5715 element = EL_CHAR_BUTTON;
5718 case 0x15da: // (blue)
5719 element = EL_CHAR_PLUS;
5722 case 0x15db: // (blue)
5723 element = EL_CHAR_MINUS;
5726 case 0x15dc: // (blue)
5727 element = EL_CHAR_APOSTROPHE;
5730 case 0x15dd: // (blue)
5731 element = EL_CHAR_PARENLEFT;
5734 case 0x15de: // (blue)
5735 element = EL_CHAR_PARENRIGHT;
5738 case 0x15df: // (green)
5739 element = EL_CHAR_A;
5742 case 0x15e0: // (green)
5743 element = EL_CHAR_B;
5746 case 0x15e1: // (green)
5747 element = EL_CHAR_C;
5750 case 0x15e2: // (green)
5751 element = EL_CHAR_D;
5754 case 0x15e3: // (green)
5755 element = EL_CHAR_E;
5758 case 0x15e4: // (green)
5759 element = EL_CHAR_F;
5762 case 0x15e5: // (green)
5763 element = EL_CHAR_G;
5766 case 0x15e6: // (green)
5767 element = EL_CHAR_H;
5770 case 0x15e7: // (green)
5771 element = EL_CHAR_I;
5774 case 0x15e8: // (green)
5775 element = EL_CHAR_J;
5778 case 0x15e9: // (green)
5779 element = EL_CHAR_K;
5782 case 0x15ea: // (green)
5783 element = EL_CHAR_L;
5786 case 0x15eb: // (green)
5787 element = EL_CHAR_M;
5790 case 0x15ec: // (green)
5791 element = EL_CHAR_N;
5794 case 0x15ed: // (green)
5795 element = EL_CHAR_O;
5798 case 0x15ee: // (green)
5799 element = EL_CHAR_P;
5802 case 0x15ef: // (green)
5803 element = EL_CHAR_Q;
5806 case 0x15f0: // (green)
5807 element = EL_CHAR_R;
5810 case 0x15f1: // (green)
5811 element = EL_CHAR_S;
5814 case 0x15f2: // (green)
5815 element = EL_CHAR_T;
5818 case 0x15f3: // (green)
5819 element = EL_CHAR_U;
5822 case 0x15f4: // (green)
5823 element = EL_CHAR_V;
5826 case 0x15f5: // (green)
5827 element = EL_CHAR_W;
5830 case 0x15f6: // (green)
5831 element = EL_CHAR_X;
5834 case 0x15f7: // (green)
5835 element = EL_CHAR_Y;
5838 case 0x15f8: // (green)
5839 element = EL_CHAR_Z;
5842 case 0x15f9: // (green)
5843 element = EL_CHAR_AUMLAUT;
5846 case 0x15fa: // (green)
5847 element = EL_CHAR_OUMLAUT;
5850 case 0x15fb: // (green)
5851 element = EL_CHAR_UUMLAUT;
5854 case 0x15fc: // (green)
5855 element = EL_CHAR_0;
5858 case 0x15fd: // (green)
5859 element = EL_CHAR_1;
5862 case 0x15fe: // (green)
5863 element = EL_CHAR_2;
5866 case 0x15ff: // (green)
5867 element = EL_CHAR_3;
5870 case 0x1600: // (green)
5871 element = EL_CHAR_4;
5874 case 0x1601: // (green)
5875 element = EL_CHAR_5;
5878 case 0x1602: // (green)
5879 element = EL_CHAR_6;
5882 case 0x1603: // (green)
5883 element = EL_CHAR_7;
5886 case 0x1604: // (green)
5887 element = EL_CHAR_8;
5890 case 0x1605: // (green)
5891 element = EL_CHAR_9;
5894 case 0x1606: // (green)
5895 element = EL_CHAR_PERIOD;
5898 case 0x1607: // (green)
5899 element = EL_CHAR_EXCLAM;
5902 case 0x1608: // (green)
5903 element = EL_CHAR_COLON;
5906 case 0x1609: // (green)
5907 element = EL_CHAR_LESS;
5910 case 0x160a: // (green)
5911 element = EL_CHAR_GREATER;
5914 case 0x160b: // (green)
5915 element = EL_CHAR_QUESTION;
5918 case 0x160c: // (green)
5919 element = EL_CHAR_COPYRIGHT;
5922 case 0x160d: // (green)
5923 element = EL_CHAR_UP;
5926 case 0x160e: // (green)
5927 element = EL_CHAR_DOWN;
5930 case 0x160f: // (green)
5931 element = EL_CHAR_BUTTON;
5934 case 0x1610: // (green)
5935 element = EL_CHAR_PLUS;
5938 case 0x1611: // (green)
5939 element = EL_CHAR_MINUS;
5942 case 0x1612: // (green)
5943 element = EL_CHAR_APOSTROPHE;
5946 case 0x1613: // (green)
5947 element = EL_CHAR_PARENLEFT;
5950 case 0x1614: // (green)
5951 element = EL_CHAR_PARENRIGHT;
5954 case 0x1615: // (blue steel)
5955 element = EL_STEEL_CHAR_A;
5958 case 0x1616: // (blue steel)
5959 element = EL_STEEL_CHAR_B;
5962 case 0x1617: // (blue steel)
5963 element = EL_STEEL_CHAR_C;
5966 case 0x1618: // (blue steel)
5967 element = EL_STEEL_CHAR_D;
5970 case 0x1619: // (blue steel)
5971 element = EL_STEEL_CHAR_E;
5974 case 0x161a: // (blue steel)
5975 element = EL_STEEL_CHAR_F;
5978 case 0x161b: // (blue steel)
5979 element = EL_STEEL_CHAR_G;
5982 case 0x161c: // (blue steel)
5983 element = EL_STEEL_CHAR_H;
5986 case 0x161d: // (blue steel)
5987 element = EL_STEEL_CHAR_I;
5990 case 0x161e: // (blue steel)
5991 element = EL_STEEL_CHAR_J;
5994 case 0x161f: // (blue steel)
5995 element = EL_STEEL_CHAR_K;
5998 case 0x1620: // (blue steel)
5999 element = EL_STEEL_CHAR_L;
6002 case 0x1621: // (blue steel)
6003 element = EL_STEEL_CHAR_M;
6006 case 0x1622: // (blue steel)
6007 element = EL_STEEL_CHAR_N;
6010 case 0x1623: // (blue steel)
6011 element = EL_STEEL_CHAR_O;
6014 case 0x1624: // (blue steel)
6015 element = EL_STEEL_CHAR_P;
6018 case 0x1625: // (blue steel)
6019 element = EL_STEEL_CHAR_Q;
6022 case 0x1626: // (blue steel)
6023 element = EL_STEEL_CHAR_R;
6026 case 0x1627: // (blue steel)
6027 element = EL_STEEL_CHAR_S;
6030 case 0x1628: // (blue steel)
6031 element = EL_STEEL_CHAR_T;
6034 case 0x1629: // (blue steel)
6035 element = EL_STEEL_CHAR_U;
6038 case 0x162a: // (blue steel)
6039 element = EL_STEEL_CHAR_V;
6042 case 0x162b: // (blue steel)
6043 element = EL_STEEL_CHAR_W;
6046 case 0x162c: // (blue steel)
6047 element = EL_STEEL_CHAR_X;
6050 case 0x162d: // (blue steel)
6051 element = EL_STEEL_CHAR_Y;
6054 case 0x162e: // (blue steel)
6055 element = EL_STEEL_CHAR_Z;
6058 case 0x162f: // (blue steel)
6059 element = EL_STEEL_CHAR_AUMLAUT;
6062 case 0x1630: // (blue steel)
6063 element = EL_STEEL_CHAR_OUMLAUT;
6066 case 0x1631: // (blue steel)
6067 element = EL_STEEL_CHAR_UUMLAUT;
6070 case 0x1632: // (blue steel)
6071 element = EL_STEEL_CHAR_0;
6074 case 0x1633: // (blue steel)
6075 element = EL_STEEL_CHAR_1;
6078 case 0x1634: // (blue steel)
6079 element = EL_STEEL_CHAR_2;
6082 case 0x1635: // (blue steel)
6083 element = EL_STEEL_CHAR_3;
6086 case 0x1636: // (blue steel)
6087 element = EL_STEEL_CHAR_4;
6090 case 0x1637: // (blue steel)
6091 element = EL_STEEL_CHAR_5;
6094 case 0x1638: // (blue steel)
6095 element = EL_STEEL_CHAR_6;
6098 case 0x1639: // (blue steel)
6099 element = EL_STEEL_CHAR_7;
6102 case 0x163a: // (blue steel)
6103 element = EL_STEEL_CHAR_8;
6106 case 0x163b: // (blue steel)
6107 element = EL_STEEL_CHAR_9;
6110 case 0x163c: // (blue steel)
6111 element = EL_STEEL_CHAR_PERIOD;
6114 case 0x163d: // (blue steel)
6115 element = EL_STEEL_CHAR_EXCLAM;
6118 case 0x163e: // (blue steel)
6119 element = EL_STEEL_CHAR_COLON;
6122 case 0x163f: // (blue steel)
6123 element = EL_STEEL_CHAR_LESS;
6126 case 0x1640: // (blue steel)
6127 element = EL_STEEL_CHAR_GREATER;
6130 case 0x1641: // (blue steel)
6131 element = EL_STEEL_CHAR_QUESTION;
6134 case 0x1642: // (blue steel)
6135 element = EL_STEEL_CHAR_COPYRIGHT;
6138 case 0x1643: // (blue steel)
6139 element = EL_STEEL_CHAR_UP;
6142 case 0x1644: // (blue steel)
6143 element = EL_STEEL_CHAR_DOWN;
6146 case 0x1645: // (blue steel)
6147 element = EL_STEEL_CHAR_BUTTON;
6150 case 0x1646: // (blue steel)
6151 element = EL_STEEL_CHAR_PLUS;
6154 case 0x1647: // (blue steel)
6155 element = EL_STEEL_CHAR_MINUS;
6158 case 0x1648: // (blue steel)
6159 element = EL_STEEL_CHAR_APOSTROPHE;
6162 case 0x1649: // (blue steel)
6163 element = EL_STEEL_CHAR_PARENLEFT;
6166 case 0x164a: // (blue steel)
6167 element = EL_STEEL_CHAR_PARENRIGHT;
6170 case 0x164b: // (green steel)
6171 element = EL_STEEL_CHAR_A;
6174 case 0x164c: // (green steel)
6175 element = EL_STEEL_CHAR_B;
6178 case 0x164d: // (green steel)
6179 element = EL_STEEL_CHAR_C;
6182 case 0x164e: // (green steel)
6183 element = EL_STEEL_CHAR_D;
6186 case 0x164f: // (green steel)
6187 element = EL_STEEL_CHAR_E;
6190 case 0x1650: // (green steel)
6191 element = EL_STEEL_CHAR_F;
6194 case 0x1651: // (green steel)
6195 element = EL_STEEL_CHAR_G;
6198 case 0x1652: // (green steel)
6199 element = EL_STEEL_CHAR_H;
6202 case 0x1653: // (green steel)
6203 element = EL_STEEL_CHAR_I;
6206 case 0x1654: // (green steel)
6207 element = EL_STEEL_CHAR_J;
6210 case 0x1655: // (green steel)
6211 element = EL_STEEL_CHAR_K;
6214 case 0x1656: // (green steel)
6215 element = EL_STEEL_CHAR_L;
6218 case 0x1657: // (green steel)
6219 element = EL_STEEL_CHAR_M;
6222 case 0x1658: // (green steel)
6223 element = EL_STEEL_CHAR_N;
6226 case 0x1659: // (green steel)
6227 element = EL_STEEL_CHAR_O;
6230 case 0x165a: // (green steel)
6231 element = EL_STEEL_CHAR_P;
6234 case 0x165b: // (green steel)
6235 element = EL_STEEL_CHAR_Q;
6238 case 0x165c: // (green steel)
6239 element = EL_STEEL_CHAR_R;
6242 case 0x165d: // (green steel)
6243 element = EL_STEEL_CHAR_S;
6246 case 0x165e: // (green steel)
6247 element = EL_STEEL_CHAR_T;
6250 case 0x165f: // (green steel)
6251 element = EL_STEEL_CHAR_U;
6254 case 0x1660: // (green steel)
6255 element = EL_STEEL_CHAR_V;
6258 case 0x1661: // (green steel)
6259 element = EL_STEEL_CHAR_W;
6262 case 0x1662: // (green steel)
6263 element = EL_STEEL_CHAR_X;
6266 case 0x1663: // (green steel)
6267 element = EL_STEEL_CHAR_Y;
6270 case 0x1664: // (green steel)
6271 element = EL_STEEL_CHAR_Z;
6274 case 0x1665: // (green steel)
6275 element = EL_STEEL_CHAR_AUMLAUT;
6278 case 0x1666: // (green steel)
6279 element = EL_STEEL_CHAR_OUMLAUT;
6282 case 0x1667: // (green steel)
6283 element = EL_STEEL_CHAR_UUMLAUT;
6286 case 0x1668: // (green steel)
6287 element = EL_STEEL_CHAR_0;
6290 case 0x1669: // (green steel)
6291 element = EL_STEEL_CHAR_1;
6294 case 0x166a: // (green steel)
6295 element = EL_STEEL_CHAR_2;
6298 case 0x166b: // (green steel)
6299 element = EL_STEEL_CHAR_3;
6302 case 0x166c: // (green steel)
6303 element = EL_STEEL_CHAR_4;
6306 case 0x166d: // (green steel)
6307 element = EL_STEEL_CHAR_5;
6310 case 0x166e: // (green steel)
6311 element = EL_STEEL_CHAR_6;
6314 case 0x166f: // (green steel)
6315 element = EL_STEEL_CHAR_7;
6318 case 0x1670: // (green steel)
6319 element = EL_STEEL_CHAR_8;
6322 case 0x1671: // (green steel)
6323 element = EL_STEEL_CHAR_9;
6326 case 0x1672: // (green steel)
6327 element = EL_STEEL_CHAR_PERIOD;
6330 case 0x1673: // (green steel)
6331 element = EL_STEEL_CHAR_EXCLAM;
6334 case 0x1674: // (green steel)
6335 element = EL_STEEL_CHAR_COLON;
6338 case 0x1675: // (green steel)
6339 element = EL_STEEL_CHAR_LESS;
6342 case 0x1676: // (green steel)
6343 element = EL_STEEL_CHAR_GREATER;
6346 case 0x1677: // (green steel)
6347 element = EL_STEEL_CHAR_QUESTION;
6350 case 0x1678: // (green steel)
6351 element = EL_STEEL_CHAR_COPYRIGHT;
6354 case 0x1679: // (green steel)
6355 element = EL_STEEL_CHAR_UP;
6358 case 0x167a: // (green steel)
6359 element = EL_STEEL_CHAR_DOWN;
6362 case 0x167b: // (green steel)
6363 element = EL_STEEL_CHAR_BUTTON;
6366 case 0x167c: // (green steel)
6367 element = EL_STEEL_CHAR_PLUS;
6370 case 0x167d: // (green steel)
6371 element = EL_STEEL_CHAR_MINUS;
6374 case 0x167e: // (green steel)
6375 element = EL_STEEL_CHAR_APOSTROPHE;
6378 case 0x167f: // (green steel)
6379 element = EL_STEEL_CHAR_PARENLEFT;
6382 case 0x1680: // (green steel)
6383 element = EL_STEEL_CHAR_PARENRIGHT;
6386 case 0x1681: // gate (red)
6387 element = EL_EM_GATE_1;
6390 case 0x1682: // secret gate (red)
6391 element = EL_EM_GATE_1_GRAY;
6394 case 0x1683: // gate (yellow)
6395 element = EL_EM_GATE_2;
6398 case 0x1684: // secret gate (yellow)
6399 element = EL_EM_GATE_2_GRAY;
6402 case 0x1685: // gate (blue)
6403 element = EL_EM_GATE_4;
6406 case 0x1686: // secret gate (blue)
6407 element = EL_EM_GATE_4_GRAY;
6410 case 0x1687: // gate (green)
6411 element = EL_EM_GATE_3;
6414 case 0x1688: // secret gate (green)
6415 element = EL_EM_GATE_3_GRAY;
6418 case 0x1689: // gate (white)
6419 element = EL_DC_GATE_WHITE;
6422 case 0x168a: // secret gate (white)
6423 element = EL_DC_GATE_WHITE_GRAY;
6426 case 0x168b: // secret gate (no key)
6427 element = EL_DC_GATE_FAKE_GRAY;
6431 element = EL_ROBOT_WHEEL;
6435 element = EL_DC_TIMEGATE_SWITCH;
6439 element = EL_ACID_POOL_BOTTOM;
6443 element = EL_ACID_POOL_TOPLEFT;
6447 element = EL_ACID_POOL_TOPRIGHT;
6451 element = EL_ACID_POOL_BOTTOMLEFT;
6455 element = EL_ACID_POOL_BOTTOMRIGHT;
6459 element = EL_STEELWALL;
6463 element = EL_STEELWALL_SLIPPERY;
6466 case 0x1695: // steel wall (not round)
6467 element = EL_STEELWALL;
6470 case 0x1696: // steel wall (left)
6471 element = EL_DC_STEELWALL_1_LEFT;
6474 case 0x1697: // steel wall (bottom)
6475 element = EL_DC_STEELWALL_1_BOTTOM;
6478 case 0x1698: // steel wall (right)
6479 element = EL_DC_STEELWALL_1_RIGHT;
6482 case 0x1699: // steel wall (top)
6483 element = EL_DC_STEELWALL_1_TOP;
6486 case 0x169a: // steel wall (left/bottom)
6487 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6490 case 0x169b: // steel wall (right/bottom)
6491 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6494 case 0x169c: // steel wall (right/top)
6495 element = EL_DC_STEELWALL_1_TOPRIGHT;
6498 case 0x169d: // steel wall (left/top)
6499 element = EL_DC_STEELWALL_1_TOPLEFT;
6502 case 0x169e: // steel wall (right/bottom small)
6503 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6506 case 0x169f: // steel wall (left/bottom small)
6507 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6510 case 0x16a0: // steel wall (right/top small)
6511 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6514 case 0x16a1: // steel wall (left/top small)
6515 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6518 case 0x16a2: // steel wall (left/right)
6519 element = EL_DC_STEELWALL_1_VERTICAL;
6522 case 0x16a3: // steel wall (top/bottom)
6523 element = EL_DC_STEELWALL_1_HORIZONTAL;
6526 case 0x16a4: // steel wall 2 (left end)
6527 element = EL_DC_STEELWALL_2_LEFT;
6530 case 0x16a5: // steel wall 2 (right end)
6531 element = EL_DC_STEELWALL_2_RIGHT;
6534 case 0x16a6: // steel wall 2 (top end)
6535 element = EL_DC_STEELWALL_2_TOP;
6538 case 0x16a7: // steel wall 2 (bottom end)
6539 element = EL_DC_STEELWALL_2_BOTTOM;
6542 case 0x16a8: // steel wall 2 (left/right)
6543 element = EL_DC_STEELWALL_2_HORIZONTAL;
6546 case 0x16a9: // steel wall 2 (up/down)
6547 element = EL_DC_STEELWALL_2_VERTICAL;
6550 case 0x16aa: // steel wall 2 (mid)
6551 element = EL_DC_STEELWALL_2_MIDDLE;
6555 element = EL_SIGN_EXCLAMATION;
6559 element = EL_SIGN_RADIOACTIVITY;
6563 element = EL_SIGN_STOP;
6567 element = EL_SIGN_WHEELCHAIR;
6571 element = EL_SIGN_PARKING;
6575 element = EL_SIGN_NO_ENTRY;
6579 element = EL_SIGN_HEART;
6583 element = EL_SIGN_GIVE_WAY;
6587 element = EL_SIGN_ENTRY_FORBIDDEN;
6591 element = EL_SIGN_EMERGENCY_EXIT;
6595 element = EL_SIGN_YIN_YANG;
6599 element = EL_WALL_EMERALD;
6603 element = EL_WALL_DIAMOND;
6607 element = EL_WALL_PEARL;
6611 element = EL_WALL_CRYSTAL;
6615 element = EL_INVISIBLE_WALL;
6619 element = EL_INVISIBLE_STEELWALL;
6623 // EL_INVISIBLE_SAND
6626 element = EL_LIGHT_SWITCH;
6630 element = EL_ENVELOPE_1;
6634 if (element >= 0x0117 && element <= 0x036e) // (?)
6635 element = EL_DIAMOND;
6636 else if (element >= 0x042d && element <= 0x0684) // (?)
6637 element = EL_EMERALD;
6638 else if (element >= 0x157c && element <= 0x158b)
6640 else if (element >= 0x1590 && element <= 0x159f)
6641 element = EL_DC_LANDMINE;
6642 else if (element >= 0x16bc && element <= 0x16cb)
6643 element = EL_INVISIBLE_SAND;
6646 Warn("unknown Diamond Caves element 0x%04x", element);
6648 element = EL_UNKNOWN;
6653 return getMappedElement(element);
6656 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6658 byte header[DC_LEVEL_HEADER_SIZE];
6660 int envelope_header_pos = 62;
6661 int envelope_content_pos = 94;
6662 int level_name_pos = 251;
6663 int level_author_pos = 292;
6664 int envelope_header_len;
6665 int envelope_content_len;
6667 int level_author_len;
6669 int num_yamyam_contents;
6672 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6674 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6676 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6678 header[i * 2 + 0] = header_word >> 8;
6679 header[i * 2 + 1] = header_word & 0xff;
6682 // read some values from level header to check level decoding integrity
6683 fieldx = header[6] | (header[7] << 8);
6684 fieldy = header[8] | (header[9] << 8);
6685 num_yamyam_contents = header[60] | (header[61] << 8);
6687 // do some simple sanity checks to ensure that level was correctly decoded
6688 if (fieldx < 1 || fieldx > 256 ||
6689 fieldy < 1 || fieldy > 256 ||
6690 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6692 level->no_valid_file = TRUE;
6694 Warn("cannot decode level from stream -- using empty level");
6699 // maximum envelope header size is 31 bytes
6700 envelope_header_len = header[envelope_header_pos];
6701 // maximum envelope content size is 110 (156?) bytes
6702 envelope_content_len = header[envelope_content_pos];
6704 // maximum level title size is 40 bytes
6705 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6706 // maximum level author size is 30 (51?) bytes
6707 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6711 for (i = 0; i < envelope_header_len; i++)
6712 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6713 level->envelope[0].text[envelope_size++] =
6714 header[envelope_header_pos + 1 + i];
6716 if (envelope_header_len > 0 && envelope_content_len > 0)
6718 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6719 level->envelope[0].text[envelope_size++] = '\n';
6720 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6721 level->envelope[0].text[envelope_size++] = '\n';
6724 for (i = 0; i < envelope_content_len; i++)
6725 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6726 level->envelope[0].text[envelope_size++] =
6727 header[envelope_content_pos + 1 + i];
6729 level->envelope[0].text[envelope_size] = '\0';
6731 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6732 level->envelope[0].ysize = 10;
6733 level->envelope[0].autowrap = TRUE;
6734 level->envelope[0].centered = TRUE;
6736 for (i = 0; i < level_name_len; i++)
6737 level->name[i] = header[level_name_pos + 1 + i];
6738 level->name[level_name_len] = '\0';
6740 for (i = 0; i < level_author_len; i++)
6741 level->author[i] = header[level_author_pos + 1 + i];
6742 level->author[level_author_len] = '\0';
6744 num_yamyam_contents = header[60] | (header[61] << 8);
6745 level->num_yamyam_contents =
6746 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6748 for (i = 0; i < num_yamyam_contents; i++)
6750 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6752 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6753 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6755 if (i < MAX_ELEMENT_CONTENTS)
6756 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6760 fieldx = header[6] | (header[7] << 8);
6761 fieldy = header[8] | (header[9] << 8);
6762 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6763 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6765 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6767 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6768 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6770 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6771 level->field[x][y] = getMappedElement_DC(element_dc);
6774 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6775 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6776 level->field[x][y] = EL_PLAYER_1;
6778 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6779 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6780 level->field[x][y] = EL_PLAYER_2;
6782 level->gems_needed = header[18] | (header[19] << 8);
6784 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6785 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6786 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6787 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6788 level->score[SC_NUT] = header[28] | (header[29] << 8);
6789 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6790 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6791 level->score[SC_BUG] = header[34] | (header[35] << 8);
6792 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6793 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6794 level->score[SC_KEY] = header[40] | (header[41] << 8);
6795 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6797 level->time = header[44] | (header[45] << 8);
6799 level->amoeba_speed = header[46] | (header[47] << 8);
6800 level->time_light = header[48] | (header[49] << 8);
6801 level->time_timegate = header[50] | (header[51] << 8);
6802 level->time_wheel = header[52] | (header[53] << 8);
6803 level->time_magic_wall = header[54] | (header[55] << 8);
6804 level->extra_time = header[56] | (header[57] << 8);
6805 level->shield_normal_time = header[58] | (header[59] << 8);
6807 // shield and extra time elements do not have a score
6808 level->score[SC_SHIELD] = 0;
6809 level->extra_time_score = 0;
6811 // set time for normal and deadly shields to the same value
6812 level->shield_deadly_time = level->shield_normal_time;
6814 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6815 // can slip down from flat walls, like normal walls and steel walls
6816 level->em_slippery_gems = TRUE;
6818 // time score is counted for each 10 seconds left in Diamond Caves levels
6819 level->time_score_base = 10;
6822 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6823 struct LevelFileInfo *level_file_info,
6824 boolean level_info_only)
6826 char *filename = level_file_info->filename;
6828 int num_magic_bytes = 8;
6829 char magic_bytes[num_magic_bytes + 1];
6830 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6832 if (!(file = openFile(filename, MODE_READ)))
6834 level->no_valid_file = TRUE;
6836 if (!level_info_only)
6837 Warn("cannot read level '%s' -- using empty level", filename);
6842 // fseek(file, 0x0000, SEEK_SET);
6844 if (level_file_info->packed)
6846 // read "magic bytes" from start of file
6847 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6848 magic_bytes[0] = '\0';
6850 // check "magic bytes" for correct file format
6851 if (!strPrefix(magic_bytes, "DC2"))
6853 level->no_valid_file = TRUE;
6855 Warn("unknown DC level file '%s' -- using empty level", filename);
6860 if (strPrefix(magic_bytes, "DC2Win95") ||
6861 strPrefix(magic_bytes, "DC2Win98"))
6863 int position_first_level = 0x00fa;
6864 int extra_bytes = 4;
6867 // advance file stream to first level inside the level package
6868 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6870 // each block of level data is followed by block of non-level data
6871 num_levels_to_skip *= 2;
6873 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6874 while (num_levels_to_skip >= 0)
6876 // advance file stream to next level inside the level package
6877 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6879 level->no_valid_file = TRUE;
6881 Warn("cannot fseek in file '%s' -- using empty level", filename);
6886 // skip apparently unused extra bytes following each level
6887 ReadUnusedBytesFromFile(file, extra_bytes);
6889 // read size of next level in level package
6890 skip_bytes = getFile32BitLE(file);
6892 num_levels_to_skip--;
6897 level->no_valid_file = TRUE;
6899 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6905 LoadLevelFromFileStream_DC(file, level);
6911 // ----------------------------------------------------------------------------
6912 // functions for loading SB level
6913 // ----------------------------------------------------------------------------
6915 int getMappedElement_SB(int element_ascii, boolean use_ces)
6923 sb_element_mapping[] =
6925 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6926 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6927 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6928 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6929 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6930 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6931 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6932 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6939 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6940 if (element_ascii == sb_element_mapping[i].ascii)
6941 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6943 return EL_UNDEFINED;
6946 static void SetLevelSettings_SB(struct LevelInfo *level)
6950 level->use_step_counter = TRUE;
6953 level->score[SC_TIME_BONUS] = 0;
6954 level->time_score_base = 1;
6955 level->rate_time_over_score = TRUE;
6958 level->auto_exit_sokoban = TRUE;
6961 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6962 struct LevelFileInfo *level_file_info,
6963 boolean level_info_only)
6965 char *filename = level_file_info->filename;
6966 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6967 char last_comment[MAX_LINE_LEN];
6968 char level_name[MAX_LINE_LEN];
6971 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6972 boolean read_continued_line = FALSE;
6973 boolean reading_playfield = FALSE;
6974 boolean got_valid_playfield_line = FALSE;
6975 boolean invalid_playfield_char = FALSE;
6976 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6977 int file_level_nr = 0;
6978 int x = 0, y = 0; // initialized to make compilers happy
6980 last_comment[0] = '\0';
6981 level_name[0] = '\0';
6983 if (!(file = openFile(filename, MODE_READ)))
6985 level->no_valid_file = TRUE;
6987 if (!level_info_only)
6988 Warn("cannot read level '%s' -- using empty level", filename);
6993 while (!checkEndOfFile(file))
6995 // level successfully read, but next level may follow here
6996 if (!got_valid_playfield_line && reading_playfield)
6998 // read playfield from single level file -- skip remaining file
6999 if (!level_file_info->packed)
7002 if (file_level_nr >= num_levels_to_skip)
7007 last_comment[0] = '\0';
7008 level_name[0] = '\0';
7010 reading_playfield = FALSE;
7013 got_valid_playfield_line = FALSE;
7015 // read next line of input file
7016 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7019 // cut trailing line break (this can be newline and/or carriage return)
7020 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7021 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7024 // copy raw input line for later use (mainly debugging output)
7025 strcpy(line_raw, line);
7027 if (read_continued_line)
7029 // append new line to existing line, if there is enough space
7030 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7031 strcat(previous_line, line_ptr);
7033 strcpy(line, previous_line); // copy storage buffer to line
7035 read_continued_line = FALSE;
7038 // if the last character is '\', continue at next line
7039 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7041 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7042 strcpy(previous_line, line); // copy line to storage buffer
7044 read_continued_line = TRUE;
7050 if (line[0] == '\0')
7053 // extract comment text from comment line
7056 for (line_ptr = line; *line_ptr; line_ptr++)
7057 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7060 strcpy(last_comment, line_ptr);
7065 // extract level title text from line containing level title
7066 if (line[0] == '\'')
7068 strcpy(level_name, &line[1]);
7070 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7071 level_name[strlen(level_name) - 1] = '\0';
7076 // skip lines containing only spaces (or empty lines)
7077 for (line_ptr = line; *line_ptr; line_ptr++)
7078 if (*line_ptr != ' ')
7080 if (*line_ptr == '\0')
7083 // at this point, we have found a line containing part of a playfield
7085 got_valid_playfield_line = TRUE;
7087 if (!reading_playfield)
7089 reading_playfield = TRUE;
7090 invalid_playfield_char = FALSE;
7092 for (x = 0; x < MAX_LEV_FIELDX; x++)
7093 for (y = 0; y < MAX_LEV_FIELDY; y++)
7094 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7099 // start with topmost tile row
7103 // skip playfield line if larger row than allowed
7104 if (y >= MAX_LEV_FIELDY)
7107 // start with leftmost tile column
7110 // read playfield elements from line
7111 for (line_ptr = line; *line_ptr; line_ptr++)
7113 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7115 // stop parsing playfield line if larger column than allowed
7116 if (x >= MAX_LEV_FIELDX)
7119 if (mapped_sb_element == EL_UNDEFINED)
7121 invalid_playfield_char = TRUE;
7126 level->field[x][y] = mapped_sb_element;
7128 // continue with next tile column
7131 level->fieldx = MAX(x, level->fieldx);
7134 if (invalid_playfield_char)
7136 // if first playfield line, treat invalid lines as comment lines
7138 reading_playfield = FALSE;
7143 // continue with next tile row
7151 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7152 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7154 if (!reading_playfield)
7156 level->no_valid_file = TRUE;
7158 Warn("cannot read level '%s' -- using empty level", filename);
7163 if (*level_name != '\0')
7165 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7166 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7168 else if (*last_comment != '\0')
7170 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7171 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7175 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7178 // set all empty fields beyond the border walls to invisible steel wall
7179 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7181 if ((x == 0 || x == level->fieldx - 1 ||
7182 y == 0 || y == level->fieldy - 1) &&
7183 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7184 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7185 level->field, level->fieldx, level->fieldy);
7188 // set special level settings for Sokoban levels
7189 SetLevelSettings_SB(level);
7191 if (load_xsb_to_ces)
7193 // special global settings can now be set in level template
7194 level->use_custom_template = TRUE;
7199 // -------------------------------------------------------------------------
7200 // functions for handling native levels
7201 // -------------------------------------------------------------------------
7203 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7204 struct LevelFileInfo *level_file_info,
7205 boolean level_info_only)
7209 // determine position of requested level inside level package
7210 if (level_file_info->packed)
7211 pos = level_file_info->nr - leveldir_current->first_level;
7213 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7214 level->no_valid_file = TRUE;
7217 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7218 struct LevelFileInfo *level_file_info,
7219 boolean level_info_only)
7221 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7222 level->no_valid_file = TRUE;
7225 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7226 struct LevelFileInfo *level_file_info,
7227 boolean level_info_only)
7231 // determine position of requested level inside level package
7232 if (level_file_info->packed)
7233 pos = level_file_info->nr - leveldir_current->first_level;
7235 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7236 level->no_valid_file = TRUE;
7239 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7240 struct LevelFileInfo *level_file_info,
7241 boolean level_info_only)
7243 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7244 level->no_valid_file = TRUE;
7247 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7249 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7250 CopyNativeLevel_RND_to_BD(level);
7251 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7252 CopyNativeLevel_RND_to_EM(level);
7253 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7254 CopyNativeLevel_RND_to_SP(level);
7255 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7256 CopyNativeLevel_RND_to_MM(level);
7259 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7261 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7262 CopyNativeLevel_BD_to_RND(level);
7263 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7264 CopyNativeLevel_EM_to_RND(level);
7265 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7266 CopyNativeLevel_SP_to_RND(level);
7267 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7268 CopyNativeLevel_MM_to_RND(level);
7271 void SaveNativeLevel(struct LevelInfo *level)
7273 // saving native level files only supported for some game engines
7274 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7275 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7278 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7279 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7280 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7281 char *filename = getLevelFilenameFromBasename(basename);
7283 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7286 boolean success = FALSE;
7288 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7290 CopyNativeLevel_RND_to_BD(level);
7291 // CopyNativeTape_RND_to_BD(level);
7293 success = SaveNativeLevel_BD(filename);
7295 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7297 CopyNativeLevel_RND_to_SP(level);
7298 CopyNativeTape_RND_to_SP(level);
7300 success = SaveNativeLevel_SP(filename);
7304 Request("Native level file saved!", REQ_CONFIRM);
7306 Request("Failed to save native level file!", REQ_CONFIRM);
7310 // ----------------------------------------------------------------------------
7311 // functions for loading generic level
7312 // ----------------------------------------------------------------------------
7314 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7315 struct LevelFileInfo *level_file_info,
7316 boolean level_info_only)
7318 // always start with reliable default values
7319 setLevelInfoToDefaults(level, level_info_only, TRUE);
7321 switch (level_file_info->type)
7323 case LEVEL_FILE_TYPE_RND:
7324 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7327 case LEVEL_FILE_TYPE_BD:
7328 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7329 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7332 case LEVEL_FILE_TYPE_EM:
7333 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7334 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7337 case LEVEL_FILE_TYPE_SP:
7338 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7339 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7342 case LEVEL_FILE_TYPE_MM:
7343 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7344 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7347 case LEVEL_FILE_TYPE_DC:
7348 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7351 case LEVEL_FILE_TYPE_SB:
7352 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7356 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7360 // if level file is invalid, restore level structure to default values
7361 if (level->no_valid_file)
7362 setLevelInfoToDefaults(level, level_info_only, FALSE);
7364 if (check_special_flags("use_native_bd_game_engine"))
7365 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7367 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7368 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7370 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7371 CopyNativeLevel_Native_to_RND(level);
7374 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7376 static struct LevelFileInfo level_file_info;
7378 // always start with reliable default values
7379 setFileInfoToDefaults(&level_file_info);
7381 level_file_info.nr = 0; // unknown level number
7382 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7384 setString(&level_file_info.filename, filename);
7386 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7389 static void LoadLevel_InitVersion(struct LevelInfo *level)
7393 if (leveldir_current == NULL) // only when dumping level
7396 // all engine modifications also valid for levels which use latest engine
7397 if (level->game_version < VERSION_IDENT(3,2,0,5))
7399 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7400 level->time_score_base = 10;
7403 if (leveldir_current->latest_engine)
7405 // ---------- use latest game engine --------------------------------------
7407 /* For all levels which are forced to use the latest game engine version
7408 (normally all but user contributed, private and undefined levels), set
7409 the game engine version to the actual version; this allows for actual
7410 corrections in the game engine to take effect for existing, converted
7411 levels (from "classic" or other existing games) to make the emulation
7412 of the corresponding game more accurate, while (hopefully) not breaking
7413 existing levels created from other players. */
7415 level->game_version = GAME_VERSION_ACTUAL;
7417 /* Set special EM style gems behaviour: EM style gems slip down from
7418 normal, steel and growing wall. As this is a more fundamental change,
7419 it seems better to set the default behaviour to "off" (as it is more
7420 natural) and make it configurable in the level editor (as a property
7421 of gem style elements). Already existing converted levels (neither
7422 private nor contributed levels) are changed to the new behaviour. */
7424 if (level->file_version < FILE_VERSION_2_0)
7425 level->em_slippery_gems = TRUE;
7430 // ---------- use game engine the level was created with --------------------
7432 /* For all levels which are not forced to use the latest game engine
7433 version (normally user contributed, private and undefined levels),
7434 use the version of the game engine the levels were created for.
7436 Since 2.0.1, the game engine version is now directly stored
7437 in the level file (chunk "VERS"), so there is no need anymore
7438 to set the game version from the file version (except for old,
7439 pre-2.0 levels, where the game version is still taken from the
7440 file format version used to store the level -- see above). */
7442 // player was faster than enemies in 1.0.0 and before
7443 if (level->file_version == FILE_VERSION_1_0)
7444 for (i = 0; i < MAX_PLAYERS; i++)
7445 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7447 // default behaviour for EM style gems was "slippery" only in 2.0.1
7448 if (level->game_version == VERSION_IDENT(2,0,1,0))
7449 level->em_slippery_gems = TRUE;
7451 // springs could be pushed over pits before (pre-release version) 2.2.0
7452 if (level->game_version < VERSION_IDENT(2,2,0,0))
7453 level->use_spring_bug = TRUE;
7455 if (level->game_version < VERSION_IDENT(3,2,0,5))
7457 // time orb caused limited time in endless time levels before 3.2.0-5
7458 level->use_time_orb_bug = TRUE;
7460 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7461 level->block_snap_field = FALSE;
7463 // extra time score was same value as time left score before 3.2.0-5
7464 level->extra_time_score = level->score[SC_TIME_BONUS];
7467 if (level->game_version < VERSION_IDENT(3,2,0,7))
7469 // default behaviour for snapping was "not continuous" before 3.2.0-7
7470 level->continuous_snapping = FALSE;
7473 // only few elements were able to actively move into acid before 3.1.0
7474 // trigger settings did not exist before 3.1.0; set to default "any"
7475 if (level->game_version < VERSION_IDENT(3,1,0,0))
7477 // correct "can move into acid" settings (all zero in old levels)
7479 level->can_move_into_acid_bits = 0; // nothing can move into acid
7480 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7482 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7483 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7484 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7485 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7487 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7488 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7490 // correct trigger settings (stored as zero == "none" in old levels)
7492 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7494 int element = EL_CUSTOM_START + i;
7495 struct ElementInfo *ei = &element_info[element];
7497 for (j = 0; j < ei->num_change_pages; j++)
7499 struct ElementChangeInfo *change = &ei->change_page[j];
7501 change->trigger_player = CH_PLAYER_ANY;
7502 change->trigger_page = CH_PAGE_ANY;
7507 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7509 int element = EL_CUSTOM_256;
7510 struct ElementInfo *ei = &element_info[element];
7511 struct ElementChangeInfo *change = &ei->change_page[0];
7513 /* This is needed to fix a problem that was caused by a bugfix in function
7514 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7515 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7516 not replace walkable elements, but instead just placed the player on it,
7517 without placing the Sokoban field under the player). Unfortunately, this
7518 breaks "Snake Bite" style levels when the snake is halfway through a door
7519 that just closes (the snake head is still alive and can be moved in this
7520 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7521 player (without Sokoban element) which then gets killed as designed). */
7523 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7524 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7525 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7526 change->target_element = EL_PLAYER_1;
7529 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7530 if (level->game_version < VERSION_IDENT(3,2,5,0))
7532 /* This is needed to fix a problem that was caused by a bugfix in function
7533 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7534 corrects the behaviour when a custom element changes to another custom
7535 element with a higher element number that has change actions defined.
7536 Normally, only one change per frame is allowed for custom elements.
7537 Therefore, it is checked if a custom element already changed in the
7538 current frame; if it did, subsequent changes are suppressed.
7539 Unfortunately, this is only checked for element changes, but not for
7540 change actions, which are still executed. As the function above loops
7541 through all custom elements from lower to higher, an element change
7542 resulting in a lower CE number won't be checked again, while a target
7543 element with a higher number will also be checked, and potential change
7544 actions will get executed for this CE, too (which is wrong), while
7545 further changes are ignored (which is correct). As this bugfix breaks
7546 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7547 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7548 behaviour for existing levels and tapes that make use of this bug */
7550 level->use_action_after_change_bug = TRUE;
7553 // not centering level after relocating player was default only in 3.2.3
7554 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7555 level->shifted_relocation = TRUE;
7557 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7558 if (level->game_version < VERSION_IDENT(3,2,6,0))
7559 level->em_explodes_by_fire = TRUE;
7561 // levels were solved by the first player entering an exit up to 4.1.0.0
7562 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7563 level->solved_by_one_player = TRUE;
7565 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7566 if (level->game_version < VERSION_IDENT(4,1,1,1))
7567 level->use_life_bugs = TRUE;
7569 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7570 if (level->game_version < VERSION_IDENT(4,1,1,1))
7571 level->sb_objects_needed = FALSE;
7573 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7574 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7575 level->finish_dig_collect = FALSE;
7577 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7578 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7579 level->keep_walkable_ce = TRUE;
7582 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7584 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7587 // check if this level is (not) a Sokoban level
7588 for (y = 0; y < level->fieldy; y++)
7589 for (x = 0; x < level->fieldx; x++)
7590 if (!IS_SB_ELEMENT(Tile[x][y]))
7591 is_sokoban_level = FALSE;
7593 if (is_sokoban_level)
7595 // set special level settings for Sokoban levels
7596 SetLevelSettings_SB(level);
7600 static void LoadLevel_InitSettings(struct LevelInfo *level)
7602 // adjust level settings for (non-native) Sokoban-style levels
7603 LoadLevel_InitSettings_SB(level);
7605 // rename levels with title "nameless level" or if renaming is forced
7606 if (leveldir_current->empty_level_name != NULL &&
7607 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7608 leveldir_current->force_level_name))
7609 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7610 leveldir_current->empty_level_name, level_nr);
7613 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7617 // map elements that have changed in newer versions
7618 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7619 level->game_version);
7620 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7621 for (x = 0; x < 3; x++)
7622 for (y = 0; y < 3; y++)
7623 level->yamyam_content[i].e[x][y] =
7624 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7625 level->game_version);
7629 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7633 // map custom element change events that have changed in newer versions
7634 // (these following values were accidentally changed in version 3.0.1)
7635 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7636 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7638 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7640 int element = EL_CUSTOM_START + i;
7642 // order of checking and copying events to be mapped is important
7643 // (do not change the start and end value -- they are constant)
7644 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7646 if (HAS_CHANGE_EVENT(element, j - 2))
7648 SET_CHANGE_EVENT(element, j - 2, FALSE);
7649 SET_CHANGE_EVENT(element, j, TRUE);
7653 // order of checking and copying events to be mapped is important
7654 // (do not change the start and end value -- they are constant)
7655 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7657 if (HAS_CHANGE_EVENT(element, j - 1))
7659 SET_CHANGE_EVENT(element, j - 1, FALSE);
7660 SET_CHANGE_EVENT(element, j, TRUE);
7666 // initialize "can_change" field for old levels with only one change page
7667 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7669 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7671 int element = EL_CUSTOM_START + i;
7673 if (CAN_CHANGE(element))
7674 element_info[element].change->can_change = TRUE;
7678 // correct custom element values (for old levels without these options)
7679 if (level->game_version < VERSION_IDENT(3,1,1,0))
7681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7683 int element = EL_CUSTOM_START + i;
7684 struct ElementInfo *ei = &element_info[element];
7686 if (ei->access_direction == MV_NO_DIRECTION)
7687 ei->access_direction = MV_ALL_DIRECTIONS;
7691 // correct custom element values (fix invalid values for all versions)
7694 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7696 int element = EL_CUSTOM_START + i;
7697 struct ElementInfo *ei = &element_info[element];
7699 for (j = 0; j < ei->num_change_pages; j++)
7701 struct ElementChangeInfo *change = &ei->change_page[j];
7703 if (change->trigger_player == CH_PLAYER_NONE)
7704 change->trigger_player = CH_PLAYER_ANY;
7706 if (change->trigger_side == CH_SIDE_NONE)
7707 change->trigger_side = CH_SIDE_ANY;
7712 // initialize "can_explode" field for old levels which did not store this
7713 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7714 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7716 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7718 int element = EL_CUSTOM_START + i;
7720 if (EXPLODES_1X1_OLD(element))
7721 element_info[element].explosion_type = EXPLODES_1X1;
7723 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7724 EXPLODES_SMASHED(element) ||
7725 EXPLODES_IMPACT(element)));
7729 // correct previously hard-coded move delay values for maze runner style
7730 if (level->game_version < VERSION_IDENT(3,1,1,0))
7732 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7734 int element = EL_CUSTOM_START + i;
7736 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7738 // previously hard-coded and therefore ignored
7739 element_info[element].move_delay_fixed = 9;
7740 element_info[element].move_delay_random = 0;
7745 // set some other uninitialized values of custom elements in older levels
7746 if (level->game_version < VERSION_IDENT(3,1,0,0))
7748 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7750 int element = EL_CUSTOM_START + i;
7752 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7754 element_info[element].explosion_delay = 17;
7755 element_info[element].ignition_delay = 8;
7759 // set mouse click change events to work for left/middle/right mouse button
7760 if (level->game_version < VERSION_IDENT(4,2,3,0))
7762 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7764 int element = EL_CUSTOM_START + i;
7765 struct ElementInfo *ei = &element_info[element];
7767 for (j = 0; j < ei->num_change_pages; j++)
7769 struct ElementChangeInfo *change = &ei->change_page[j];
7771 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7772 change->has_event[CE_PRESSED_BY_MOUSE] ||
7773 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7774 change->has_event[CE_MOUSE_PRESSED_ON_X])
7775 change->trigger_side = CH_SIDE_ANY;
7781 static void LoadLevel_InitElements(struct LevelInfo *level)
7783 LoadLevel_InitStandardElements(level);
7785 if (level->file_has_custom_elements)
7786 LoadLevel_InitCustomElements(level);
7788 // initialize element properties for level editor etc.
7789 InitElementPropertiesEngine(level->game_version);
7790 InitElementPropertiesGfxElement();
7793 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7797 // map elements that have changed in newer versions
7798 for (y = 0; y < level->fieldy; y++)
7799 for (x = 0; x < level->fieldx; x++)
7800 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7801 level->game_version);
7803 // clear unused playfield data (nicer if level gets resized in editor)
7804 for (x = 0; x < MAX_LEV_FIELDX; x++)
7805 for (y = 0; y < MAX_LEV_FIELDY; y++)
7806 if (x >= level->fieldx || y >= level->fieldy)
7807 level->field[x][y] = EL_EMPTY;
7809 // copy elements to runtime playfield array
7810 for (x = 0; x < MAX_LEV_FIELDX; x++)
7811 for (y = 0; y < MAX_LEV_FIELDY; y++)
7812 Tile[x][y] = level->field[x][y];
7814 // initialize level size variables for faster access
7815 lev_fieldx = level->fieldx;
7816 lev_fieldy = level->fieldy;
7818 // determine border element for this level
7819 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7820 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7825 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7827 struct LevelFileInfo *level_file_info = &level->file_info;
7829 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7830 CopyNativeLevel_RND_to_Native(level);
7833 static void LoadLevelTemplate_LoadAndInit(void)
7835 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7837 LoadLevel_InitVersion(&level_template);
7838 LoadLevel_InitElements(&level_template);
7839 LoadLevel_InitSettings(&level_template);
7841 ActivateLevelTemplate();
7844 void LoadLevelTemplate(int nr)
7846 if (!fileExists(getGlobalLevelTemplateFilename()))
7848 Warn("no level template found for this level");
7853 setLevelFileInfo(&level_template.file_info, nr);
7855 LoadLevelTemplate_LoadAndInit();
7858 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7860 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7862 LoadLevelTemplate_LoadAndInit();
7865 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7867 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7869 if (level.use_custom_template)
7871 if (network_level != NULL)
7872 LoadNetworkLevelTemplate(network_level);
7874 LoadLevelTemplate(-1);
7877 LoadLevel_InitVersion(&level);
7878 LoadLevel_InitElements(&level);
7879 LoadLevel_InitPlayfield(&level);
7880 LoadLevel_InitSettings(&level);
7882 LoadLevel_InitNativeEngines(&level);
7885 void LoadLevel(int nr)
7887 SetLevelSetInfo(leveldir_current->identifier, nr);
7889 setLevelFileInfo(&level.file_info, nr);
7891 LoadLevel_LoadAndInit(NULL);
7894 void LoadLevelInfoOnly(int nr)
7896 setLevelFileInfo(&level.file_info, nr);
7898 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7901 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7903 SetLevelSetInfo(network_level->leveldir_identifier,
7904 network_level->file_info.nr);
7906 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7908 LoadLevel_LoadAndInit(network_level);
7911 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7915 chunk_size += putFileVersion(file, level->file_version);
7916 chunk_size += putFileVersion(file, level->game_version);
7921 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7925 chunk_size += putFile16BitBE(file, level->creation_date.year);
7926 chunk_size += putFile8Bit(file, level->creation_date.month);
7927 chunk_size += putFile8Bit(file, level->creation_date.day);
7932 #if ENABLE_HISTORIC_CHUNKS
7933 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7937 putFile8Bit(file, level->fieldx);
7938 putFile8Bit(file, level->fieldy);
7940 putFile16BitBE(file, level->time);
7941 putFile16BitBE(file, level->gems_needed);
7943 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7944 putFile8Bit(file, level->name[i]);
7946 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7947 putFile8Bit(file, level->score[i]);
7949 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7950 for (y = 0; y < 3; y++)
7951 for (x = 0; x < 3; x++)
7952 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7953 level->yamyam_content[i].e[x][y]));
7954 putFile8Bit(file, level->amoeba_speed);
7955 putFile8Bit(file, level->time_magic_wall);
7956 putFile8Bit(file, level->time_wheel);
7957 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7958 level->amoeba_content));
7959 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7960 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7961 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7962 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7964 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7966 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7967 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7968 putFile32BitBE(file, level->can_move_into_acid_bits);
7969 putFile8Bit(file, level->dont_collide_with_bits);
7971 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7972 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7974 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7975 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7976 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7978 putFile8Bit(file, level->game_engine_type);
7980 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7984 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7989 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7990 chunk_size += putFile8Bit(file, level->name[i]);
7995 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8000 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8001 chunk_size += putFile8Bit(file, level->author[i]);
8006 #if ENABLE_HISTORIC_CHUNKS
8007 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8012 for (y = 0; y < level->fieldy; y++)
8013 for (x = 0; x < level->fieldx; x++)
8014 if (level->encoding_16bit_field)
8015 chunk_size += putFile16BitBE(file, level->field[x][y]);
8017 chunk_size += putFile8Bit(file, level->field[x][y]);
8023 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8028 for (y = 0; y < level->fieldy; y++)
8029 for (x = 0; x < level->fieldx; x++)
8030 chunk_size += putFile16BitBE(file, level->field[x][y]);
8035 #if ENABLE_HISTORIC_CHUNKS
8036 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8040 putFile8Bit(file, EL_YAMYAM);
8041 putFile8Bit(file, level->num_yamyam_contents);
8042 putFile8Bit(file, 0);
8043 putFile8Bit(file, 0);
8045 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8046 for (y = 0; y < 3; y++)
8047 for (x = 0; x < 3; x++)
8048 if (level->encoding_16bit_field)
8049 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8051 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8055 #if ENABLE_HISTORIC_CHUNKS
8056 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8059 int num_contents, content_xsize, content_ysize;
8060 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8062 if (element == EL_YAMYAM)
8064 num_contents = level->num_yamyam_contents;
8068 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8069 for (y = 0; y < 3; y++)
8070 for (x = 0; x < 3; x++)
8071 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8073 else if (element == EL_BD_AMOEBA)
8079 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8080 for (y = 0; y < 3; y++)
8081 for (x = 0; x < 3; x++)
8082 content_array[i][x][y] = EL_EMPTY;
8083 content_array[0][0][0] = level->amoeba_content;
8087 // chunk header already written -- write empty chunk data
8088 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8090 Warn("cannot save content for element '%d'", element);
8095 putFile16BitBE(file, element);
8096 putFile8Bit(file, num_contents);
8097 putFile8Bit(file, content_xsize);
8098 putFile8Bit(file, content_ysize);
8100 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8102 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8103 for (y = 0; y < 3; y++)
8104 for (x = 0; x < 3; x++)
8105 putFile16BitBE(file, content_array[i][x][y]);
8109 #if ENABLE_HISTORIC_CHUNKS
8110 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8112 int envelope_nr = element - EL_ENVELOPE_1;
8113 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8117 chunk_size += putFile16BitBE(file, element);
8118 chunk_size += putFile16BitBE(file, envelope_len);
8119 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8120 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8122 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8123 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8125 for (i = 0; i < envelope_len; i++)
8126 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8132 #if ENABLE_HISTORIC_CHUNKS
8133 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8134 int num_changed_custom_elements)
8138 putFile16BitBE(file, num_changed_custom_elements);
8140 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8142 int element = EL_CUSTOM_START + i;
8144 struct ElementInfo *ei = &element_info[element];
8146 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8148 if (check < num_changed_custom_elements)
8150 putFile16BitBE(file, element);
8151 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8158 if (check != num_changed_custom_elements) // should not happen
8159 Warn("inconsistent number of custom element properties");
8163 #if ENABLE_HISTORIC_CHUNKS
8164 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8165 int num_changed_custom_elements)
8169 putFile16BitBE(file, num_changed_custom_elements);
8171 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8173 int element = EL_CUSTOM_START + i;
8175 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8177 if (check < num_changed_custom_elements)
8179 putFile16BitBE(file, element);
8180 putFile16BitBE(file, element_info[element].change->target_element);
8187 if (check != num_changed_custom_elements) // should not happen
8188 Warn("inconsistent number of custom target elements");
8192 #if ENABLE_HISTORIC_CHUNKS
8193 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8194 int num_changed_custom_elements)
8196 int i, j, x, y, check = 0;
8198 putFile16BitBE(file, num_changed_custom_elements);
8200 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8202 int element = EL_CUSTOM_START + i;
8203 struct ElementInfo *ei = &element_info[element];
8205 if (ei->modified_settings)
8207 if (check < num_changed_custom_elements)
8209 putFile16BitBE(file, element);
8211 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8212 putFile8Bit(file, ei->description[j]);
8214 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8216 // some free bytes for future properties and padding
8217 WriteUnusedBytesToFile(file, 7);
8219 putFile8Bit(file, ei->use_gfx_element);
8220 putFile16BitBE(file, ei->gfx_element_initial);
8222 putFile8Bit(file, ei->collect_score_initial);
8223 putFile8Bit(file, ei->collect_count_initial);
8225 putFile16BitBE(file, ei->push_delay_fixed);
8226 putFile16BitBE(file, ei->push_delay_random);
8227 putFile16BitBE(file, ei->move_delay_fixed);
8228 putFile16BitBE(file, ei->move_delay_random);
8230 putFile16BitBE(file, ei->move_pattern);
8231 putFile8Bit(file, ei->move_direction_initial);
8232 putFile8Bit(file, ei->move_stepsize);
8234 for (y = 0; y < 3; y++)
8235 for (x = 0; x < 3; x++)
8236 putFile16BitBE(file, ei->content.e[x][y]);
8238 putFile32BitBE(file, ei->change->events);
8240 putFile16BitBE(file, ei->change->target_element);
8242 putFile16BitBE(file, ei->change->delay_fixed);
8243 putFile16BitBE(file, ei->change->delay_random);
8244 putFile16BitBE(file, ei->change->delay_frames);
8246 putFile16BitBE(file, ei->change->initial_trigger_element);
8248 putFile8Bit(file, ei->change->explode);
8249 putFile8Bit(file, ei->change->use_target_content);
8250 putFile8Bit(file, ei->change->only_if_complete);
8251 putFile8Bit(file, ei->change->use_random_replace);
8253 putFile8Bit(file, ei->change->random_percentage);
8254 putFile8Bit(file, ei->change->replace_when);
8256 for (y = 0; y < 3; y++)
8257 for (x = 0; x < 3; x++)
8258 putFile16BitBE(file, ei->change->content.e[x][y]);
8260 putFile8Bit(file, ei->slippery_type);
8262 // some free bytes for future properties and padding
8263 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8270 if (check != num_changed_custom_elements) // should not happen
8271 Warn("inconsistent number of custom element properties");
8275 #if ENABLE_HISTORIC_CHUNKS
8276 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8278 struct ElementInfo *ei = &element_info[element];
8281 // ---------- custom element base property values (96 bytes) ----------------
8283 putFile16BitBE(file, element);
8285 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8286 putFile8Bit(file, ei->description[i]);
8288 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8290 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8292 putFile8Bit(file, ei->num_change_pages);
8294 putFile16BitBE(file, ei->ce_value_fixed_initial);
8295 putFile16BitBE(file, ei->ce_value_random_initial);
8296 putFile8Bit(file, ei->use_last_ce_value);
8298 putFile8Bit(file, ei->use_gfx_element);
8299 putFile16BitBE(file, ei->gfx_element_initial);
8301 putFile8Bit(file, ei->collect_score_initial);
8302 putFile8Bit(file, ei->collect_count_initial);
8304 putFile8Bit(file, ei->drop_delay_fixed);
8305 putFile8Bit(file, ei->push_delay_fixed);
8306 putFile8Bit(file, ei->drop_delay_random);
8307 putFile8Bit(file, ei->push_delay_random);
8308 putFile16BitBE(file, ei->move_delay_fixed);
8309 putFile16BitBE(file, ei->move_delay_random);
8311 // bits 0 - 15 of "move_pattern" ...
8312 putFile16BitBE(file, ei->move_pattern & 0xffff);
8313 putFile8Bit(file, ei->move_direction_initial);
8314 putFile8Bit(file, ei->move_stepsize);
8316 putFile8Bit(file, ei->slippery_type);
8318 for (y = 0; y < 3; y++)
8319 for (x = 0; x < 3; x++)
8320 putFile16BitBE(file, ei->content.e[x][y]);
8322 putFile16BitBE(file, ei->move_enter_element);
8323 putFile16BitBE(file, ei->move_leave_element);
8324 putFile8Bit(file, ei->move_leave_type);
8326 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8327 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8329 putFile8Bit(file, ei->access_direction);
8331 putFile8Bit(file, ei->explosion_delay);
8332 putFile8Bit(file, ei->ignition_delay);
8333 putFile8Bit(file, ei->explosion_type);
8335 // some free bytes for future custom property values and padding
8336 WriteUnusedBytesToFile(file, 1);
8338 // ---------- change page property values (48 bytes) ------------------------
8340 for (i = 0; i < ei->num_change_pages; i++)
8342 struct ElementChangeInfo *change = &ei->change_page[i];
8343 unsigned int event_bits;
8345 // bits 0 - 31 of "has_event[]" ...
8347 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8348 if (change->has_event[j])
8349 event_bits |= (1u << j);
8350 putFile32BitBE(file, event_bits);
8352 putFile16BitBE(file, change->target_element);
8354 putFile16BitBE(file, change->delay_fixed);
8355 putFile16BitBE(file, change->delay_random);
8356 putFile16BitBE(file, change->delay_frames);
8358 putFile16BitBE(file, change->initial_trigger_element);
8360 putFile8Bit(file, change->explode);
8361 putFile8Bit(file, change->use_target_content);
8362 putFile8Bit(file, change->only_if_complete);
8363 putFile8Bit(file, change->use_random_replace);
8365 putFile8Bit(file, change->random_percentage);
8366 putFile8Bit(file, change->replace_when);
8368 for (y = 0; y < 3; y++)
8369 for (x = 0; x < 3; x++)
8370 putFile16BitBE(file, change->target_content.e[x][y]);
8372 putFile8Bit(file, change->can_change);
8374 putFile8Bit(file, change->trigger_side);
8376 putFile8Bit(file, change->trigger_player);
8377 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8378 log_2(change->trigger_page)));
8380 putFile8Bit(file, change->has_action);
8381 putFile8Bit(file, change->action_type);
8382 putFile8Bit(file, change->action_mode);
8383 putFile16BitBE(file, change->action_arg);
8385 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8387 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8388 if (change->has_event[j])
8389 event_bits |= (1u << (j - 32));
8390 putFile8Bit(file, event_bits);
8395 #if ENABLE_HISTORIC_CHUNKS
8396 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8398 struct ElementInfo *ei = &element_info[element];
8399 struct ElementGroupInfo *group = ei->group;
8402 putFile16BitBE(file, element);
8404 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8405 putFile8Bit(file, ei->description[i]);
8407 putFile8Bit(file, group->num_elements);
8409 putFile8Bit(file, ei->use_gfx_element);
8410 putFile16BitBE(file, ei->gfx_element_initial);
8412 putFile8Bit(file, group->choice_mode);
8414 // some free bytes for future values and padding
8415 WriteUnusedBytesToFile(file, 3);
8417 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8418 putFile16BitBE(file, group->element[i]);
8422 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8423 boolean write_element)
8425 int save_type = entry->save_type;
8426 int data_type = entry->data_type;
8427 int conf_type = entry->conf_type;
8428 int byte_mask = conf_type & CONF_MASK_BYTES;
8429 int element = entry->element;
8430 int default_value = entry->default_value;
8432 boolean modified = FALSE;
8434 if (byte_mask != CONF_MASK_MULTI_BYTES)
8436 void *value_ptr = entry->value;
8437 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8440 // check if any settings have been modified before saving them
8441 if (value != default_value)
8444 // do not save if explicitly told or if unmodified default settings
8445 if ((save_type == SAVE_CONF_NEVER) ||
8446 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8450 num_bytes += putFile16BitBE(file, element);
8452 num_bytes += putFile8Bit(file, conf_type);
8453 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8454 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8455 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8458 else if (data_type == TYPE_STRING)
8460 char *default_string = entry->default_string;
8461 char *string = (char *)(entry->value);
8462 int string_length = strlen(string);
8465 // check if any settings have been modified before saving them
8466 if (!strEqual(string, default_string))
8469 // do not save if explicitly told or if unmodified default settings
8470 if ((save_type == SAVE_CONF_NEVER) ||
8471 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8475 num_bytes += putFile16BitBE(file, element);
8477 num_bytes += putFile8Bit(file, conf_type);
8478 num_bytes += putFile16BitBE(file, string_length);
8480 for (i = 0; i < string_length; i++)
8481 num_bytes += putFile8Bit(file, string[i]);
8483 else if (data_type == TYPE_ELEMENT_LIST)
8485 int *element_array = (int *)(entry->value);
8486 int num_elements = *(int *)(entry->num_entities);
8489 // check if any settings have been modified before saving them
8490 for (i = 0; i < num_elements; i++)
8491 if (element_array[i] != default_value)
8494 // do not save if explicitly told or if unmodified default settings
8495 if ((save_type == SAVE_CONF_NEVER) ||
8496 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8500 num_bytes += putFile16BitBE(file, element);
8502 num_bytes += putFile8Bit(file, conf_type);
8503 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8505 for (i = 0; i < num_elements; i++)
8506 num_bytes += putFile16BitBE(file, element_array[i]);
8508 else if (data_type == TYPE_CONTENT_LIST)
8510 struct Content *content = (struct Content *)(entry->value);
8511 int num_contents = *(int *)(entry->num_entities);
8514 // check if any settings have been modified before saving them
8515 for (i = 0; i < num_contents; i++)
8516 for (y = 0; y < 3; y++)
8517 for (x = 0; x < 3; x++)
8518 if (content[i].e[x][y] != default_value)
8521 // do not save if explicitly told or if unmodified default settings
8522 if ((save_type == SAVE_CONF_NEVER) ||
8523 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8527 num_bytes += putFile16BitBE(file, element);
8529 num_bytes += putFile8Bit(file, conf_type);
8530 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8532 for (i = 0; i < num_contents; i++)
8533 for (y = 0; y < 3; y++)
8534 for (x = 0; x < 3; x++)
8535 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8541 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8546 li = *level; // copy level data into temporary buffer
8548 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8549 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8554 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8559 li = *level; // copy level data into temporary buffer
8561 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8562 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8567 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8569 int envelope_nr = element - EL_ENVELOPE_1;
8573 chunk_size += putFile16BitBE(file, element);
8575 // copy envelope data into temporary buffer
8576 xx_envelope = level->envelope[envelope_nr];
8578 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8579 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8584 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8586 struct ElementInfo *ei = &element_info[element];
8590 chunk_size += putFile16BitBE(file, element);
8592 xx_ei = *ei; // copy element data into temporary buffer
8594 // set default description string for this specific element
8595 strcpy(xx_default_description, getDefaultElementDescription(ei));
8597 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8598 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8600 for (i = 0; i < ei->num_change_pages; i++)
8602 struct ElementChangeInfo *change = &ei->change_page[i];
8604 xx_current_change_page = i;
8606 xx_change = *change; // copy change data into temporary buffer
8609 setEventBitsFromEventFlags(change);
8611 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8612 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8619 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8621 struct ElementInfo *ei = &element_info[element];
8622 struct ElementGroupInfo *group = ei->group;
8626 chunk_size += putFile16BitBE(file, element);
8628 xx_ei = *ei; // copy element data into temporary buffer
8629 xx_group = *group; // copy group data into temporary buffer
8631 // set default description string for this specific element
8632 strcpy(xx_default_description, getDefaultElementDescription(ei));
8634 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8635 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8640 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8642 struct ElementInfo *ei = &element_info[element];
8646 chunk_size += putFile16BitBE(file, element);
8648 xx_ei = *ei; // copy element data into temporary buffer
8650 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8651 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8656 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8657 boolean save_as_template)
8663 if (!(file = fopen(filename, MODE_WRITE)))
8665 Warn("cannot save level file '%s'", filename);
8670 level->file_version = FILE_VERSION_ACTUAL;
8671 level->game_version = GAME_VERSION_ACTUAL;
8673 level->creation_date = getCurrentDate();
8675 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8676 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8678 chunk_size = SaveLevel_VERS(NULL, level);
8679 putFileChunkBE(file, "VERS", chunk_size);
8680 SaveLevel_VERS(file, level);
8682 chunk_size = SaveLevel_DATE(NULL, level);
8683 putFileChunkBE(file, "DATE", chunk_size);
8684 SaveLevel_DATE(file, level);
8686 chunk_size = SaveLevel_NAME(NULL, level);
8687 putFileChunkBE(file, "NAME", chunk_size);
8688 SaveLevel_NAME(file, level);
8690 chunk_size = SaveLevel_AUTH(NULL, level);
8691 putFileChunkBE(file, "AUTH", chunk_size);
8692 SaveLevel_AUTH(file, level);
8694 chunk_size = SaveLevel_INFO(NULL, level);
8695 putFileChunkBE(file, "INFO", chunk_size);
8696 SaveLevel_INFO(file, level);
8698 chunk_size = SaveLevel_BODY(NULL, level);
8699 putFileChunkBE(file, "BODY", chunk_size);
8700 SaveLevel_BODY(file, level);
8702 chunk_size = SaveLevel_ELEM(NULL, level);
8703 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8705 putFileChunkBE(file, "ELEM", chunk_size);
8706 SaveLevel_ELEM(file, level);
8709 for (i = 0; i < NUM_ENVELOPES; i++)
8711 int element = EL_ENVELOPE_1 + i;
8713 chunk_size = SaveLevel_NOTE(NULL, level, element);
8714 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8716 putFileChunkBE(file, "NOTE", chunk_size);
8717 SaveLevel_NOTE(file, level, element);
8721 // if not using template level, check for non-default custom/group elements
8722 if (!level->use_custom_template || save_as_template)
8724 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8726 int element = EL_CUSTOM_START + i;
8728 chunk_size = SaveLevel_CUSX(NULL, level, element);
8729 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8731 putFileChunkBE(file, "CUSX", chunk_size);
8732 SaveLevel_CUSX(file, level, element);
8736 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8738 int element = EL_GROUP_START + i;
8740 chunk_size = SaveLevel_GRPX(NULL, level, element);
8741 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8743 putFileChunkBE(file, "GRPX", chunk_size);
8744 SaveLevel_GRPX(file, level, element);
8748 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8750 int element = GET_EMPTY_ELEMENT(i);
8752 chunk_size = SaveLevel_EMPX(NULL, level, element);
8753 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8755 putFileChunkBE(file, "EMPX", chunk_size);
8756 SaveLevel_EMPX(file, level, element);
8763 SetFilePermissions(filename, PERMS_PRIVATE);
8766 void SaveLevel(int nr)
8768 char *filename = getDefaultLevelFilename(nr);
8770 SaveLevelFromFilename(&level, filename, FALSE);
8773 void SaveLevelTemplate(void)
8775 char *filename = getLocalLevelTemplateFilename();
8777 SaveLevelFromFilename(&level, filename, TRUE);
8780 boolean SaveLevelChecked(int nr)
8782 char *filename = getDefaultLevelFilename(nr);
8783 boolean new_level = !fileExists(filename);
8784 boolean level_saved = FALSE;
8786 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8791 Request("Level saved!", REQ_CONFIRM);
8799 void DumpLevel(struct LevelInfo *level)
8801 if (level->no_level_file || level->no_valid_file)
8803 Warn("cannot dump -- no valid level file found");
8809 Print("Level xxx (file version %08d, game version %08d)\n",
8810 level->file_version, level->game_version);
8813 Print("Level author: '%s'\n", level->author);
8814 Print("Level title: '%s'\n", level->name);
8816 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8818 Print("Level time: %d seconds\n", level->time);
8819 Print("Gems needed: %d\n", level->gems_needed);
8821 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8822 Print("Time for wheel: %d seconds\n", level->time_wheel);
8823 Print("Time for light: %d seconds\n", level->time_light);
8824 Print("Time for timegate: %d seconds\n", level->time_timegate);
8826 Print("Amoeba speed: %d\n", level->amoeba_speed);
8829 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8830 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8831 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8832 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8833 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8834 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8840 for (i = 0; i < NUM_ENVELOPES; i++)
8842 char *text = level->envelope[i].text;
8843 int text_len = strlen(text);
8844 boolean has_text = FALSE;
8846 for (j = 0; j < text_len; j++)
8847 if (text[j] != ' ' && text[j] != '\n')
8853 Print("Envelope %d:\n'%s'\n", i + 1, text);
8861 void DumpLevels(void)
8863 static LevelDirTree *dumplevel_leveldir = NULL;
8865 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8866 global.dumplevel_leveldir);
8868 if (dumplevel_leveldir == NULL)
8869 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8871 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8872 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8873 Fail("no such level number: %d", global.dumplevel_level_nr);
8875 leveldir_current = dumplevel_leveldir;
8877 LoadLevel(global.dumplevel_level_nr);
8884 // ============================================================================
8885 // tape file functions
8886 // ============================================================================
8888 static void setTapeInfoToDefaults(void)
8892 // always start with reliable default values (empty tape)
8895 // default values (also for pre-1.2 tapes) with only the first player
8896 tape.player_participates[0] = TRUE;
8897 for (i = 1; i < MAX_PLAYERS; i++)
8898 tape.player_participates[i] = FALSE;
8900 // at least one (default: the first) player participates in every tape
8901 tape.num_participating_players = 1;
8903 tape.property_bits = TAPE_PROPERTY_NONE;
8905 tape.level_nr = level_nr;
8907 tape.changed = FALSE;
8908 tape.solved = FALSE;
8910 tape.recording = FALSE;
8911 tape.playing = FALSE;
8912 tape.pausing = FALSE;
8914 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8915 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8917 tape.no_info_chunk = TRUE;
8918 tape.no_valid_file = FALSE;
8921 static int getTapePosSize(struct TapeInfo *tape)
8923 int tape_pos_size = 0;
8925 if (tape->use_key_actions)
8926 tape_pos_size += tape->num_participating_players;
8928 if (tape->use_mouse_actions)
8929 tape_pos_size += 3; // x and y position and mouse button mask
8931 tape_pos_size += 1; // tape action delay value
8933 return tape_pos_size;
8936 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8938 tape->use_key_actions = FALSE;
8939 tape->use_mouse_actions = FALSE;
8941 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8942 tape->use_key_actions = TRUE;
8944 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8945 tape->use_mouse_actions = TRUE;
8948 static int getTapeActionValue(struct TapeInfo *tape)
8950 return (tape->use_key_actions &&
8951 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8952 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8953 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8954 TAPE_ACTIONS_DEFAULT);
8957 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8959 tape->file_version = getFileVersion(file);
8960 tape->game_version = getFileVersion(file);
8965 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8969 tape->random_seed = getFile32BitBE(file);
8970 tape->date = getFile32BitBE(file);
8971 tape->length = getFile32BitBE(file);
8973 // read header fields that are new since version 1.2
8974 if (tape->file_version >= FILE_VERSION_1_2)
8976 byte store_participating_players = getFile8Bit(file);
8979 // since version 1.2, tapes store which players participate in the tape
8980 tape->num_participating_players = 0;
8981 for (i = 0; i < MAX_PLAYERS; i++)
8983 tape->player_participates[i] = FALSE;
8985 if (store_participating_players & (1 << i))
8987 tape->player_participates[i] = TRUE;
8988 tape->num_participating_players++;
8992 setTapeActionFlags(tape, getFile8Bit(file));
8994 tape->property_bits = getFile8Bit(file);
8995 tape->solved = getFile8Bit(file);
8997 engine_version = getFileVersion(file);
8998 if (engine_version > 0)
8999 tape->engine_version = engine_version;
9001 tape->engine_version = tape->game_version;
9007 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9009 tape->scr_fieldx = getFile8Bit(file);
9010 tape->scr_fieldy = getFile8Bit(file);
9015 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9017 char *level_identifier = NULL;
9018 int level_identifier_size;
9021 tape->no_info_chunk = FALSE;
9023 level_identifier_size = getFile16BitBE(file);
9025 level_identifier = checked_malloc(level_identifier_size);
9027 for (i = 0; i < level_identifier_size; i++)
9028 level_identifier[i] = getFile8Bit(file);
9030 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9031 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9033 checked_free(level_identifier);
9035 tape->level_nr = getFile16BitBE(file);
9037 chunk_size = 2 + level_identifier_size + 2;
9042 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9045 int tape_pos_size = getTapePosSize(tape);
9046 int chunk_size_expected = tape_pos_size * tape->length;
9048 if (chunk_size_expected != chunk_size)
9050 ReadUnusedBytesFromFile(file, chunk_size);
9051 return chunk_size_expected;
9054 for (i = 0; i < tape->length; i++)
9056 if (i >= MAX_TAPE_LEN)
9058 Warn("tape truncated -- size exceeds maximum tape size %d",
9061 // tape too large; read and ignore remaining tape data from this chunk
9062 for (;i < tape->length; i++)
9063 ReadUnusedBytesFromFile(file, tape_pos_size);
9068 if (tape->use_key_actions)
9070 for (j = 0; j < MAX_PLAYERS; j++)
9072 tape->pos[i].action[j] = MV_NONE;
9074 if (tape->player_participates[j])
9075 tape->pos[i].action[j] = getFile8Bit(file);
9079 if (tape->use_mouse_actions)
9081 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9082 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9083 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9086 tape->pos[i].delay = getFile8Bit(file);
9088 if (tape->file_version == FILE_VERSION_1_0)
9090 // eliminate possible diagonal moves in old tapes
9091 // this is only for backward compatibility
9093 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9094 byte action = tape->pos[i].action[0];
9095 int k, num_moves = 0;
9097 for (k = 0; k < 4; k++)
9099 if (action & joy_dir[k])
9101 tape->pos[i + num_moves].action[0] = joy_dir[k];
9103 tape->pos[i + num_moves].delay = 0;
9112 tape->length += num_moves;
9115 else if (tape->file_version < FILE_VERSION_2_0)
9117 // convert pre-2.0 tapes to new tape format
9119 if (tape->pos[i].delay > 1)
9122 tape->pos[i + 1] = tape->pos[i];
9123 tape->pos[i + 1].delay = 1;
9126 for (j = 0; j < MAX_PLAYERS; j++)
9127 tape->pos[i].action[j] = MV_NONE;
9128 tape->pos[i].delay--;
9135 if (checkEndOfFile(file))
9139 if (i != tape->length)
9140 chunk_size = tape_pos_size * i;
9145 static void LoadTape_SokobanSolution(char *filename)
9148 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9150 if (!(file = openFile(filename, MODE_READ)))
9152 tape.no_valid_file = TRUE;
9157 while (!checkEndOfFile(file))
9159 unsigned char c = getByteFromFile(file);
9161 if (checkEndOfFile(file))
9168 tape.pos[tape.length].action[0] = MV_UP;
9169 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9175 tape.pos[tape.length].action[0] = MV_DOWN;
9176 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9182 tape.pos[tape.length].action[0] = MV_LEFT;
9183 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9189 tape.pos[tape.length].action[0] = MV_RIGHT;
9190 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9198 // ignore white-space characters
9202 tape.no_valid_file = TRUE;
9204 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9212 if (tape.no_valid_file)
9215 tape.length_frames = GetTapeLengthFrames();
9216 tape.length_seconds = GetTapeLengthSeconds();
9219 void LoadTapeFromFilename(char *filename)
9221 char cookie[MAX_LINE_LEN];
9222 char chunk_name[CHUNK_ID_LEN + 1];
9226 // always start with reliable default values
9227 setTapeInfoToDefaults();
9229 if (strSuffix(filename, ".sln"))
9231 LoadTape_SokobanSolution(filename);
9236 if (!(file = openFile(filename, MODE_READ)))
9238 tape.no_valid_file = TRUE;
9243 getFileChunkBE(file, chunk_name, NULL);
9244 if (strEqual(chunk_name, "RND1"))
9246 getFile32BitBE(file); // not used
9248 getFileChunkBE(file, chunk_name, NULL);
9249 if (!strEqual(chunk_name, "TAPE"))
9251 tape.no_valid_file = TRUE;
9253 Warn("unknown format of tape file '%s'", filename);
9260 else // check for pre-2.0 file format with cookie string
9262 strcpy(cookie, chunk_name);
9263 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9265 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9266 cookie[strlen(cookie) - 1] = '\0';
9268 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9270 tape.no_valid_file = TRUE;
9272 Warn("unknown format of tape file '%s'", filename);
9279 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9281 tape.no_valid_file = TRUE;
9283 Warn("unsupported version of tape file '%s'", filename);
9290 // pre-2.0 tape files have no game version, so use file version here
9291 tape.game_version = tape.file_version;
9294 if (tape.file_version < FILE_VERSION_1_2)
9296 // tape files from versions before 1.2.0 without chunk structure
9297 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9298 LoadTape_BODY(file, 2 * tape.length, &tape);
9306 int (*loader)(File *, int, struct TapeInfo *);
9310 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9311 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9312 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9313 { "INFO", -1, LoadTape_INFO },
9314 { "BODY", -1, LoadTape_BODY },
9318 while (getFileChunkBE(file, chunk_name, &chunk_size))
9322 while (chunk_info[i].name != NULL &&
9323 !strEqual(chunk_name, chunk_info[i].name))
9326 if (chunk_info[i].name == NULL)
9328 Warn("unknown chunk '%s' in tape file '%s'",
9329 chunk_name, filename);
9331 ReadUnusedBytesFromFile(file, chunk_size);
9333 else if (chunk_info[i].size != -1 &&
9334 chunk_info[i].size != chunk_size)
9336 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9337 chunk_size, chunk_name, filename);
9339 ReadUnusedBytesFromFile(file, chunk_size);
9343 // call function to load this tape chunk
9344 int chunk_size_expected =
9345 (chunk_info[i].loader)(file, chunk_size, &tape);
9347 // the size of some chunks cannot be checked before reading other
9348 // chunks first (like "HEAD" and "BODY") that contain some header
9349 // information, so check them here
9350 if (chunk_size_expected != chunk_size)
9352 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9353 chunk_size, chunk_name, filename);
9361 tape.length_frames = GetTapeLengthFrames();
9362 tape.length_seconds = GetTapeLengthSeconds();
9365 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9367 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9369 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9370 tape.engine_version);
9374 void LoadTape(int nr)
9376 char *filename = getTapeFilename(nr);
9378 LoadTapeFromFilename(filename);
9381 void LoadSolutionTape(int nr)
9383 char *filename = getSolutionTapeFilename(nr);
9385 LoadTapeFromFilename(filename);
9387 if (TAPE_IS_EMPTY(tape))
9389 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9390 level.native_bd_level->replay != NULL)
9391 CopyNativeTape_BD_to_RND(&level);
9392 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9393 level.native_sp_level->demo.is_available)
9394 CopyNativeTape_SP_to_RND(&level);
9398 void LoadScoreTape(char *score_tape_basename, int nr)
9400 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9402 LoadTapeFromFilename(filename);
9405 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9407 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9409 LoadTapeFromFilename(filename);
9412 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9414 // chunk required for team mode tapes with non-default screen size
9415 return (tape->num_participating_players > 1 &&
9416 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9417 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9420 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9422 putFileVersion(file, tape->file_version);
9423 putFileVersion(file, tape->game_version);
9426 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9429 byte store_participating_players = 0;
9431 // set bits for participating players for compact storage
9432 for (i = 0; i < MAX_PLAYERS; i++)
9433 if (tape->player_participates[i])
9434 store_participating_players |= (1 << i);
9436 putFile32BitBE(file, tape->random_seed);
9437 putFile32BitBE(file, tape->date);
9438 putFile32BitBE(file, tape->length);
9440 putFile8Bit(file, store_participating_players);
9442 putFile8Bit(file, getTapeActionValue(tape));
9444 putFile8Bit(file, tape->property_bits);
9445 putFile8Bit(file, tape->solved);
9447 putFileVersion(file, tape->engine_version);
9450 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9452 putFile8Bit(file, tape->scr_fieldx);
9453 putFile8Bit(file, tape->scr_fieldy);
9456 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9458 int level_identifier_size = strlen(tape->level_identifier) + 1;
9461 putFile16BitBE(file, level_identifier_size);
9463 for (i = 0; i < level_identifier_size; i++)
9464 putFile8Bit(file, tape->level_identifier[i]);
9466 putFile16BitBE(file, tape->level_nr);
9469 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9473 for (i = 0; i < tape->length; i++)
9475 if (tape->use_key_actions)
9477 for (j = 0; j < MAX_PLAYERS; j++)
9478 if (tape->player_participates[j])
9479 putFile8Bit(file, tape->pos[i].action[j]);
9482 if (tape->use_mouse_actions)
9484 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9485 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9486 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9489 putFile8Bit(file, tape->pos[i].delay);
9493 void SaveTapeToFilename(char *filename)
9497 int info_chunk_size;
9498 int body_chunk_size;
9500 if (!(file = fopen(filename, MODE_WRITE)))
9502 Warn("cannot save level recording file '%s'", filename);
9507 tape_pos_size = getTapePosSize(&tape);
9509 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9510 body_chunk_size = tape_pos_size * tape.length;
9512 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9513 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9515 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9516 SaveTape_VERS(file, &tape);
9518 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9519 SaveTape_HEAD(file, &tape);
9521 if (checkSaveTape_SCRN(&tape))
9523 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9524 SaveTape_SCRN(file, &tape);
9527 putFileChunkBE(file, "INFO", info_chunk_size);
9528 SaveTape_INFO(file, &tape);
9530 putFileChunkBE(file, "BODY", body_chunk_size);
9531 SaveTape_BODY(file, &tape);
9535 SetFilePermissions(filename, PERMS_PRIVATE);
9538 static void SaveTapeExt(char *filename)
9542 tape.file_version = FILE_VERSION_ACTUAL;
9543 tape.game_version = GAME_VERSION_ACTUAL;
9545 tape.num_participating_players = 0;
9547 // count number of participating players
9548 for (i = 0; i < MAX_PLAYERS; i++)
9549 if (tape.player_participates[i])
9550 tape.num_participating_players++;
9552 SaveTapeToFilename(filename);
9554 tape.changed = FALSE;
9557 void SaveTape(int nr)
9559 char *filename = getTapeFilename(nr);
9561 InitTapeDirectory(leveldir_current->subdir);
9563 SaveTapeExt(filename);
9566 void SaveScoreTape(int nr)
9568 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9570 // used instead of "leveldir_current->subdir" (for network games)
9571 InitScoreTapeDirectory(levelset.identifier, nr);
9573 SaveTapeExt(filename);
9576 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9577 unsigned int req_state_added)
9579 char *filename = getTapeFilename(nr);
9580 boolean new_tape = !fileExists(filename);
9581 boolean tape_saved = FALSE;
9583 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9588 Request(msg_saved, REQ_CONFIRM | req_state_added);
9596 boolean SaveTapeChecked(int nr)
9598 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9601 boolean SaveTapeChecked_LevelSolved(int nr)
9603 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9604 "Level solved! Tape saved!", REQ_STAY_OPEN);
9607 void DumpTape(struct TapeInfo *tape)
9609 int tape_frame_counter;
9612 if (tape->no_valid_file)
9614 Warn("cannot dump -- no valid tape file found");
9621 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9622 tape->level_nr, tape->file_version, tape->game_version);
9623 Print(" (effective engine version %08d)\n",
9624 tape->engine_version);
9625 Print("Level series identifier: '%s'\n", tape->level_identifier);
9627 Print("Solution tape: %s\n",
9628 tape->solved ? "yes" :
9629 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9631 Print("Special tape properties: ");
9632 if (tape->property_bits == TAPE_PROPERTY_NONE)
9634 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9635 Print("[em_random_bug]");
9636 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9637 Print("[game_speed]");
9638 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9640 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9641 Print("[single_step]");
9642 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9643 Print("[snapshot]");
9644 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9645 Print("[replayed]");
9646 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9647 Print("[tas_keys]");
9648 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9649 Print("[small_graphics]");
9652 int year2 = tape->date / 10000;
9653 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9654 int month_index_raw = (tape->date / 100) % 100;
9655 int month_index = month_index_raw % 12; // prevent invalid index
9656 int month = month_index + 1;
9657 int day = tape->date % 100;
9659 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9663 tape_frame_counter = 0;
9665 for (i = 0; i < tape->length; i++)
9667 if (i >= MAX_TAPE_LEN)
9672 for (j = 0; j < MAX_PLAYERS; j++)
9674 if (tape->player_participates[j])
9676 int action = tape->pos[i].action[j];
9678 Print("%d:%02x ", j, action);
9679 Print("[%c%c%c%c|%c%c] - ",
9680 (action & JOY_LEFT ? '<' : ' '),
9681 (action & JOY_RIGHT ? '>' : ' '),
9682 (action & JOY_UP ? '^' : ' '),
9683 (action & JOY_DOWN ? 'v' : ' '),
9684 (action & JOY_BUTTON_1 ? '1' : ' '),
9685 (action & JOY_BUTTON_2 ? '2' : ' '));
9689 Print("(%03d) ", tape->pos[i].delay);
9690 Print("[%05d]\n", tape_frame_counter);
9692 tape_frame_counter += tape->pos[i].delay;
9698 void DumpTapes(void)
9700 static LevelDirTree *dumptape_leveldir = NULL;
9702 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9703 global.dumptape_leveldir);
9705 if (dumptape_leveldir == NULL)
9706 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9708 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9709 global.dumptape_level_nr > dumptape_leveldir->last_level)
9710 Fail("no such level number: %d", global.dumptape_level_nr);
9712 leveldir_current = dumptape_leveldir;
9714 if (options.mytapes)
9715 LoadTape(global.dumptape_level_nr);
9717 LoadSolutionTape(global.dumptape_level_nr);
9725 // ============================================================================
9726 // score file functions
9727 // ============================================================================
9729 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9733 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9735 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9736 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9737 scores->entry[i].score = 0;
9738 scores->entry[i].time = 0;
9740 scores->entry[i].id = -1;
9741 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9742 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9743 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9744 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9745 strcpy(scores->entry[i].country_code, "??");
9748 scores->num_entries = 0;
9749 scores->last_added = -1;
9750 scores->last_added_local = -1;
9752 scores->updated = FALSE;
9753 scores->uploaded = FALSE;
9754 scores->tape_downloaded = FALSE;
9755 scores->force_last_added = FALSE;
9757 // The following values are intentionally not reset here:
9761 // - continue_playing
9762 // - continue_on_return
9765 static void setScoreInfoToDefaults(void)
9767 setScoreInfoToDefaultsExt(&scores);
9770 static void setServerScoreInfoToDefaults(void)
9772 setScoreInfoToDefaultsExt(&server_scores);
9775 static void LoadScore_OLD(int nr)
9778 char *filename = getScoreFilename(nr);
9779 char cookie[MAX_LINE_LEN];
9780 char line[MAX_LINE_LEN];
9784 if (!(file = fopen(filename, MODE_READ)))
9787 // check file identifier
9788 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9790 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9791 cookie[strlen(cookie) - 1] = '\0';
9793 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9795 Warn("unknown format of score file '%s'", filename);
9802 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9804 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9805 Warn("fscanf() failed; %s", strerror(errno));
9807 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9810 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9811 line[strlen(line) - 1] = '\0';
9813 for (line_ptr = line; *line_ptr; line_ptr++)
9815 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9817 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9818 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9827 static void ConvertScore_OLD(void)
9829 // only convert score to time for levels that rate playing time over score
9830 if (!level.rate_time_over_score)
9833 // convert old score to playing time for score-less levels (like Supaplex)
9834 int time_final_max = 999;
9837 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9839 int score = scores.entry[i].score;
9841 if (score > 0 && score < time_final_max)
9842 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9846 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9848 scores->file_version = getFileVersion(file);
9849 scores->game_version = getFileVersion(file);
9854 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9856 char *level_identifier = NULL;
9857 int level_identifier_size;
9860 level_identifier_size = getFile16BitBE(file);
9862 level_identifier = checked_malloc(level_identifier_size);
9864 for (i = 0; i < level_identifier_size; i++)
9865 level_identifier[i] = getFile8Bit(file);
9867 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9868 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9870 checked_free(level_identifier);
9872 scores->level_nr = getFile16BitBE(file);
9873 scores->num_entries = getFile16BitBE(file);
9875 chunk_size = 2 + level_identifier_size + 2 + 2;
9880 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9884 for (i = 0; i < scores->num_entries; i++)
9886 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9887 scores->entry[i].name[j] = getFile8Bit(file);
9889 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9892 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9897 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9901 for (i = 0; i < scores->num_entries; i++)
9902 scores->entry[i].score = getFile16BitBE(file);
9904 chunk_size = scores->num_entries * 2;
9909 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9913 for (i = 0; i < scores->num_entries; i++)
9914 scores->entry[i].score = getFile32BitBE(file);
9916 chunk_size = scores->num_entries * 4;
9921 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9925 for (i = 0; i < scores->num_entries; i++)
9926 scores->entry[i].time = getFile32BitBE(file);
9928 chunk_size = scores->num_entries * 4;
9933 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9937 for (i = 0; i < scores->num_entries; i++)
9939 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9940 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9942 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9945 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9950 void LoadScore(int nr)
9952 char *filename = getScoreFilename(nr);
9953 char cookie[MAX_LINE_LEN];
9954 char chunk_name[CHUNK_ID_LEN + 1];
9956 boolean old_score_file_format = FALSE;
9959 // always start with reliable default values
9960 setScoreInfoToDefaults();
9962 if (!(file = openFile(filename, MODE_READ)))
9965 getFileChunkBE(file, chunk_name, NULL);
9966 if (strEqual(chunk_name, "RND1"))
9968 getFile32BitBE(file); // not used
9970 getFileChunkBE(file, chunk_name, NULL);
9971 if (!strEqual(chunk_name, "SCOR"))
9973 Warn("unknown format of score file '%s'", filename);
9980 else // check for old file format with cookie string
9982 strcpy(cookie, chunk_name);
9983 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9985 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9986 cookie[strlen(cookie) - 1] = '\0';
9988 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9990 Warn("unknown format of score file '%s'", filename);
9997 old_score_file_format = TRUE;
10000 if (old_score_file_format)
10002 // score files from versions before 4.2.4.0 without chunk structure
10005 // convert score to time, if possible (mainly for Supaplex levels)
10006 ConvertScore_OLD();
10014 int (*loader)(File *, int, struct ScoreInfo *);
10018 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10019 { "INFO", -1, LoadScore_INFO },
10020 { "NAME", -1, LoadScore_NAME },
10021 { "SCOR", -1, LoadScore_SCOR },
10022 { "SC4R", -1, LoadScore_SC4R },
10023 { "TIME", -1, LoadScore_TIME },
10024 { "TAPE", -1, LoadScore_TAPE },
10029 while (getFileChunkBE(file, chunk_name, &chunk_size))
10033 while (chunk_info[i].name != NULL &&
10034 !strEqual(chunk_name, chunk_info[i].name))
10037 if (chunk_info[i].name == NULL)
10039 Warn("unknown chunk '%s' in score file '%s'",
10040 chunk_name, filename);
10042 ReadUnusedBytesFromFile(file, chunk_size);
10044 else if (chunk_info[i].size != -1 &&
10045 chunk_info[i].size != chunk_size)
10047 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10048 chunk_size, chunk_name, filename);
10050 ReadUnusedBytesFromFile(file, chunk_size);
10054 // call function to load this score chunk
10055 int chunk_size_expected =
10056 (chunk_info[i].loader)(file, chunk_size, &scores);
10058 // the size of some chunks cannot be checked before reading other
10059 // chunks first (like "HEAD" and "BODY") that contain some header
10060 // information, so check them here
10061 if (chunk_size_expected != chunk_size)
10063 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10064 chunk_size, chunk_name, filename);
10073 #if ENABLE_HISTORIC_CHUNKS
10074 void SaveScore_OLD(int nr)
10077 char *filename = getScoreFilename(nr);
10080 // used instead of "leveldir_current->subdir" (for network games)
10081 InitScoreDirectory(levelset.identifier);
10083 if (!(file = fopen(filename, MODE_WRITE)))
10085 Warn("cannot save score for level %d", nr);
10090 fprintf(file, "%s\n\n", SCORE_COOKIE);
10092 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10093 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10097 SetFilePermissions(filename, PERMS_PRIVATE);
10101 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10103 putFileVersion(file, scores->file_version);
10104 putFileVersion(file, scores->game_version);
10107 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10109 int level_identifier_size = strlen(scores->level_identifier) + 1;
10112 putFile16BitBE(file, level_identifier_size);
10114 for (i = 0; i < level_identifier_size; i++)
10115 putFile8Bit(file, scores->level_identifier[i]);
10117 putFile16BitBE(file, scores->level_nr);
10118 putFile16BitBE(file, scores->num_entries);
10121 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10125 for (i = 0; i < scores->num_entries; i++)
10127 int name_size = strlen(scores->entry[i].name);
10129 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10130 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10134 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10138 for (i = 0; i < scores->num_entries; i++)
10139 putFile16BitBE(file, scores->entry[i].score);
10142 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10146 for (i = 0; i < scores->num_entries; i++)
10147 putFile32BitBE(file, scores->entry[i].score);
10150 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10154 for (i = 0; i < scores->num_entries; i++)
10155 putFile32BitBE(file, scores->entry[i].time);
10158 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10162 for (i = 0; i < scores->num_entries; i++)
10164 int size = strlen(scores->entry[i].tape_basename);
10166 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10167 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10171 static void SaveScoreToFilename(char *filename)
10174 int info_chunk_size;
10175 int name_chunk_size;
10176 int scor_chunk_size;
10177 int sc4r_chunk_size;
10178 int time_chunk_size;
10179 int tape_chunk_size;
10180 boolean has_large_score_values;
10183 if (!(file = fopen(filename, MODE_WRITE)))
10185 Warn("cannot save score file '%s'", filename);
10190 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10191 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10192 scor_chunk_size = scores.num_entries * 2;
10193 sc4r_chunk_size = scores.num_entries * 4;
10194 time_chunk_size = scores.num_entries * 4;
10195 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10197 has_large_score_values = FALSE;
10198 for (i = 0; i < scores.num_entries; i++)
10199 if (scores.entry[i].score > 0xffff)
10200 has_large_score_values = TRUE;
10202 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10203 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10205 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10206 SaveScore_VERS(file, &scores);
10208 putFileChunkBE(file, "INFO", info_chunk_size);
10209 SaveScore_INFO(file, &scores);
10211 putFileChunkBE(file, "NAME", name_chunk_size);
10212 SaveScore_NAME(file, &scores);
10214 if (has_large_score_values)
10216 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10217 SaveScore_SC4R(file, &scores);
10221 putFileChunkBE(file, "SCOR", scor_chunk_size);
10222 SaveScore_SCOR(file, &scores);
10225 putFileChunkBE(file, "TIME", time_chunk_size);
10226 SaveScore_TIME(file, &scores);
10228 putFileChunkBE(file, "TAPE", tape_chunk_size);
10229 SaveScore_TAPE(file, &scores);
10233 SetFilePermissions(filename, PERMS_PRIVATE);
10236 void SaveScore(int nr)
10238 char *filename = getScoreFilename(nr);
10241 // used instead of "leveldir_current->subdir" (for network games)
10242 InitScoreDirectory(levelset.identifier);
10244 scores.file_version = FILE_VERSION_ACTUAL;
10245 scores.game_version = GAME_VERSION_ACTUAL;
10247 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10248 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10249 scores.level_nr = level_nr;
10251 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10252 if (scores.entry[i].score == 0 &&
10253 scores.entry[i].time == 0 &&
10254 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10257 scores.num_entries = i;
10259 if (scores.num_entries == 0)
10262 SaveScoreToFilename(filename);
10265 static void LoadServerScoreFromCache(int nr)
10267 struct ScoreEntry score_entry;
10276 { &score_entry.score, FALSE, 0 },
10277 { &score_entry.time, FALSE, 0 },
10278 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10279 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10280 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10281 { &score_entry.id, FALSE, 0 },
10282 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10283 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10284 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10285 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10289 char *filename = getScoreCacheFilename(nr);
10290 SetupFileHash *score_hash = loadSetupFileHash(filename);
10293 server_scores.num_entries = 0;
10295 if (score_hash == NULL)
10298 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10300 score_entry = server_scores.entry[i];
10302 for (j = 0; score_mapping[j].value != NULL; j++)
10306 sprintf(token, "%02d.%d", i, j);
10308 char *value = getHashEntry(score_hash, token);
10313 if (score_mapping[j].is_string)
10315 char *score_value = (char *)score_mapping[j].value;
10316 int value_size = score_mapping[j].string_size;
10318 strncpy(score_value, value, value_size);
10319 score_value[value_size] = '\0';
10323 int *score_value = (int *)score_mapping[j].value;
10325 *score_value = atoi(value);
10328 server_scores.num_entries = i + 1;
10331 server_scores.entry[i] = score_entry;
10334 freeSetupFileHash(score_hash);
10337 void LoadServerScore(int nr, boolean download_score)
10339 if (!setup.use_api_server)
10342 // always start with reliable default values
10343 setServerScoreInfoToDefaults();
10345 // 1st step: load server scores from cache file (which may not exist)
10346 // (this should prevent reading it while the thread is writing to it)
10347 LoadServerScoreFromCache(nr);
10349 if (download_score && runtime.use_api_server)
10351 // 2nd step: download server scores from score server to cache file
10352 // (as thread, as it might time out if the server is not reachable)
10353 ApiGetScoreAsThread(nr);
10357 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10359 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10361 // if score tape not uploaded, ask for uploading missing tapes later
10362 if (!setup.has_remaining_tapes)
10363 setup.ask_for_remaining_tapes = TRUE;
10365 setup.provide_uploading_tapes = TRUE;
10366 setup.has_remaining_tapes = TRUE;
10368 SaveSetup_ServerSetup();
10371 void SaveServerScore(int nr, boolean tape_saved)
10373 if (!runtime.use_api_server)
10375 PrepareScoreTapesForUpload(leveldir_current->subdir);
10380 ApiAddScoreAsThread(nr, tape_saved, NULL);
10383 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10384 char *score_tape_filename)
10386 if (!runtime.use_api_server)
10389 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10392 void LoadLocalAndServerScore(int nr, boolean download_score)
10394 int last_added_local = scores.last_added_local;
10395 boolean force_last_added = scores.force_last_added;
10397 // needed if only showing server scores
10398 setScoreInfoToDefaults();
10400 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10403 // restore last added local score entry (before merging server scores)
10404 scores.last_added = scores.last_added_local = last_added_local;
10406 if (setup.use_api_server &&
10407 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10409 // load server scores from cache file and trigger update from server
10410 LoadServerScore(nr, download_score);
10412 // merge local scores with scores from server
10413 MergeServerScore();
10416 if (force_last_added)
10417 scores.force_last_added = force_last_added;
10421 // ============================================================================
10422 // setup file functions
10423 // ============================================================================
10425 #define TOKEN_STR_PLAYER_PREFIX "player_"
10428 static struct TokenInfo global_setup_tokens[] =
10432 &setup.player_name, "player_name"
10436 &setup.multiple_users, "multiple_users"
10440 &setup.sound, "sound"
10444 &setup.sound_loops, "repeating_sound_loops"
10448 &setup.sound_music, "background_music"
10452 &setup.sound_simple, "simple_sound_effects"
10456 &setup.toons, "toons"
10460 &setup.global_animations, "global_animations"
10464 &setup.scroll_delay, "scroll_delay"
10468 &setup.forced_scroll_delay, "forced_scroll_delay"
10472 &setup.scroll_delay_value, "scroll_delay_value"
10476 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10480 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10484 &setup.fade_screens, "fade_screens"
10488 &setup.autorecord, "automatic_tape_recording"
10492 &setup.autorecord_after_replay, "autorecord_after_replay"
10496 &setup.auto_pause_on_start, "auto_pause_on_start"
10500 &setup.show_titlescreen, "show_titlescreen"
10504 &setup.quick_doors, "quick_doors"
10508 &setup.team_mode, "team_mode"
10512 &setup.handicap, "handicap"
10516 &setup.skip_levels, "skip_levels"
10520 &setup.increment_levels, "increment_levels"
10524 &setup.auto_play_next_level, "auto_play_next_level"
10528 &setup.count_score_after_game, "count_score_after_game"
10532 &setup.show_scores_after_game, "show_scores_after_game"
10536 &setup.time_limit, "time_limit"
10540 &setup.fullscreen, "fullscreen"
10544 &setup.window_scaling_percent, "window_scaling_percent"
10548 &setup.window_scaling_quality, "window_scaling_quality"
10552 &setup.screen_rendering_mode, "screen_rendering_mode"
10556 &setup.vsync_mode, "vsync_mode"
10560 &setup.ask_on_escape, "ask_on_escape"
10564 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10568 &setup.ask_on_game_over, "ask_on_game_over"
10572 &setup.ask_on_quit_game, "ask_on_quit_game"
10576 &setup.ask_on_quit_program, "ask_on_quit_program"
10580 &setup.quick_switch, "quick_player_switch"
10584 &setup.input_on_focus, "input_on_focus"
10588 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10592 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10596 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10600 &setup.game_speed_extended, "game_speed_extended"
10604 &setup.game_frame_delay, "game_frame_delay"
10608 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10612 &setup.bd_skip_hatching, "bd_skip_hatching"
10616 &setup.bd_scroll_delay, "bd_scroll_delay"
10620 &setup.bd_smooth_movements, "bd_smooth_movements"
10624 &setup.sp_show_border_elements, "sp_show_border_elements"
10628 &setup.small_game_graphics, "small_game_graphics"
10632 &setup.show_load_save_buttons, "show_load_save_buttons"
10636 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10640 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10644 &setup.graphics_set, "graphics_set"
10648 &setup.sounds_set, "sounds_set"
10652 &setup.music_set, "music_set"
10656 &setup.override_level_graphics, "override_level_graphics"
10660 &setup.override_level_sounds, "override_level_sounds"
10664 &setup.override_level_music, "override_level_music"
10668 &setup.volume_simple, "volume_simple"
10672 &setup.volume_loops, "volume_loops"
10676 &setup.volume_music, "volume_music"
10680 &setup.network_mode, "network_mode"
10684 &setup.network_player_nr, "network_player"
10688 &setup.network_server_hostname, "network_server_hostname"
10692 &setup.touch.control_type, "touch.control_type"
10696 &setup.touch.move_distance, "touch.move_distance"
10700 &setup.touch.drop_distance, "touch.drop_distance"
10704 &setup.touch.transparency, "touch.transparency"
10708 &setup.touch.draw_outlined, "touch.draw_outlined"
10712 &setup.touch.draw_pressed, "touch.draw_pressed"
10716 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10720 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10724 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10728 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10732 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10736 static struct TokenInfo auto_setup_tokens[] =
10740 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10744 static struct TokenInfo server_setup_tokens[] =
10748 &setup.player_uuid, "player_uuid"
10752 &setup.player_version, "player_version"
10756 &setup.use_api_server, TEST_PREFIX "use_api_server"
10760 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10764 &setup.api_server_password, TEST_PREFIX "api_server_password"
10768 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10772 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10776 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10780 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10784 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10788 static struct TokenInfo editor_setup_tokens[] =
10792 &setup.editor.el_classic, "editor.el_classic"
10796 &setup.editor.el_custom, "editor.el_custom"
10800 &setup.editor.el_user_defined, "editor.el_user_defined"
10804 &setup.editor.el_dynamic, "editor.el_dynamic"
10808 &setup.editor.el_headlines, "editor.el_headlines"
10812 &setup.editor.show_element_token, "editor.show_element_token"
10816 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10820 static struct TokenInfo editor_cascade_setup_tokens[] =
10824 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10828 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10832 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10836 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10840 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10844 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10848 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10852 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10856 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10860 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10864 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10868 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10872 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10876 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10880 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10884 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10888 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10892 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10896 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10900 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10904 static struct TokenInfo shortcut_setup_tokens[] =
10908 &setup.shortcut.save_game, "shortcut.save_game"
10912 &setup.shortcut.load_game, "shortcut.load_game"
10916 &setup.shortcut.restart_game, "shortcut.restart_game"
10920 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10924 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10928 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10932 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10936 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10940 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10944 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10948 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10952 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10956 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10960 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10964 &setup.shortcut.tape_record, "shortcut.tape_record"
10968 &setup.shortcut.tape_play, "shortcut.tape_play"
10972 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10976 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10980 &setup.shortcut.sound_music, "shortcut.sound_music"
10984 &setup.shortcut.snap_left, "shortcut.snap_left"
10988 &setup.shortcut.snap_right, "shortcut.snap_right"
10992 &setup.shortcut.snap_up, "shortcut.snap_up"
10996 &setup.shortcut.snap_down, "shortcut.snap_down"
11000 static struct SetupInputInfo setup_input;
11001 static struct TokenInfo player_setup_tokens[] =
11005 &setup_input.use_joystick, ".use_joystick"
11009 &setup_input.joy.device_name, ".joy.device_name"
11013 &setup_input.joy.xleft, ".joy.xleft"
11017 &setup_input.joy.xmiddle, ".joy.xmiddle"
11021 &setup_input.joy.xright, ".joy.xright"
11025 &setup_input.joy.yupper, ".joy.yupper"
11029 &setup_input.joy.ymiddle, ".joy.ymiddle"
11033 &setup_input.joy.ylower, ".joy.ylower"
11037 &setup_input.joy.snap, ".joy.snap_field"
11041 &setup_input.joy.drop, ".joy.place_bomb"
11045 &setup_input.key.left, ".key.move_left"
11049 &setup_input.key.right, ".key.move_right"
11053 &setup_input.key.up, ".key.move_up"
11057 &setup_input.key.down, ".key.move_down"
11061 &setup_input.key.snap, ".key.snap_field"
11065 &setup_input.key.drop, ".key.place_bomb"
11069 static struct TokenInfo system_setup_tokens[] =
11073 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11077 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11081 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11085 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11089 static struct TokenInfo internal_setup_tokens[] =
11093 &setup.internal.program_title, "program_title"
11097 &setup.internal.program_version, "program_version"
11101 &setup.internal.program_author, "program_author"
11105 &setup.internal.program_email, "program_email"
11109 &setup.internal.program_website, "program_website"
11113 &setup.internal.program_copyright, "program_copyright"
11117 &setup.internal.program_company, "program_company"
11121 &setup.internal.program_icon_file, "program_icon_file"
11125 &setup.internal.default_graphics_set, "default_graphics_set"
11129 &setup.internal.default_sounds_set, "default_sounds_set"
11133 &setup.internal.default_music_set, "default_music_set"
11137 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11141 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11145 &setup.internal.fallback_music_file, "fallback_music_file"
11149 &setup.internal.default_level_series, "default_level_series"
11153 &setup.internal.default_window_width, "default_window_width"
11157 &setup.internal.default_window_height, "default_window_height"
11161 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11165 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11169 &setup.internal.create_user_levelset, "create_user_levelset"
11173 &setup.internal.info_screens_from_main, "info_screens_from_main"
11177 &setup.internal.menu_game, "menu_game"
11181 &setup.internal.menu_engines, "menu_engines"
11185 &setup.internal.menu_editor, "menu_editor"
11189 &setup.internal.menu_graphics, "menu_graphics"
11193 &setup.internal.menu_sound, "menu_sound"
11197 &setup.internal.menu_artwork, "menu_artwork"
11201 &setup.internal.menu_input, "menu_input"
11205 &setup.internal.menu_touch, "menu_touch"
11209 &setup.internal.menu_shortcuts, "menu_shortcuts"
11213 &setup.internal.menu_exit, "menu_exit"
11217 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11221 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11225 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11229 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11233 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11237 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11241 &setup.internal.info_title, "info_title"
11245 &setup.internal.info_elements, "info_elements"
11249 &setup.internal.info_music, "info_music"
11253 &setup.internal.info_credits, "info_credits"
11257 &setup.internal.info_program, "info_program"
11261 &setup.internal.info_version, "info_version"
11265 &setup.internal.info_levelset, "info_levelset"
11269 &setup.internal.info_exit, "info_exit"
11273 static struct TokenInfo debug_setup_tokens[] =
11277 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11281 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11285 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11289 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11293 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11297 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11301 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11305 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11309 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11313 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11317 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11321 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11325 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11329 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11333 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11337 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11341 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11345 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11349 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11353 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11357 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11360 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11364 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11368 &setup.debug.xsn_mode, "debug.xsn_mode"
11372 &setup.debug.xsn_percent, "debug.xsn_percent"
11376 static struct TokenInfo options_setup_tokens[] =
11380 &setup.options.verbose, "options.verbose"
11384 &setup.options.debug, "options.debug"
11388 &setup.options.debug_mode, "options.debug_mode"
11392 static void setSetupInfoToDefaults(struct SetupInfo *si)
11396 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11398 si->multiple_users = TRUE;
11401 si->sound_loops = TRUE;
11402 si->sound_music = TRUE;
11403 si->sound_simple = TRUE;
11405 si->global_animations = TRUE;
11406 si->scroll_delay = TRUE;
11407 si->forced_scroll_delay = FALSE;
11408 si->scroll_delay_value = STD_SCROLL_DELAY;
11409 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11410 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11411 si->fade_screens = TRUE;
11412 si->autorecord = TRUE;
11413 si->autorecord_after_replay = TRUE;
11414 si->auto_pause_on_start = FALSE;
11415 si->show_titlescreen = TRUE;
11416 si->quick_doors = FALSE;
11417 si->team_mode = FALSE;
11418 si->handicap = TRUE;
11419 si->skip_levels = TRUE;
11420 si->increment_levels = TRUE;
11421 si->auto_play_next_level = TRUE;
11422 si->count_score_after_game = TRUE;
11423 si->show_scores_after_game = TRUE;
11424 si->time_limit = TRUE;
11425 si->fullscreen = FALSE;
11426 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11427 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11428 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11429 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11430 si->ask_on_escape = TRUE;
11431 si->ask_on_escape_editor = TRUE;
11432 si->ask_on_game_over = TRUE;
11433 si->ask_on_quit_game = TRUE;
11434 si->ask_on_quit_program = TRUE;
11435 si->quick_switch = FALSE;
11436 si->input_on_focus = FALSE;
11437 si->prefer_aga_graphics = TRUE;
11438 si->prefer_lowpass_sounds = FALSE;
11439 si->prefer_extra_panel_items = TRUE;
11440 si->game_speed_extended = FALSE;
11441 si->game_frame_delay = GAME_FRAME_DELAY;
11442 si->bd_skip_uncovering = FALSE;
11443 si->bd_skip_hatching = FALSE;
11444 si->bd_scroll_delay = TRUE;
11445 si->bd_smooth_movements = AUTO;
11446 si->sp_show_border_elements = FALSE;
11447 si->small_game_graphics = FALSE;
11448 si->show_load_save_buttons = FALSE;
11449 si->show_undo_redo_buttons = FALSE;
11450 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11452 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11453 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11454 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11456 si->override_level_graphics = FALSE;
11457 si->override_level_sounds = FALSE;
11458 si->override_level_music = FALSE;
11460 si->volume_simple = 100; // percent
11461 si->volume_loops = 100; // percent
11462 si->volume_music = 100; // percent
11464 si->network_mode = FALSE;
11465 si->network_player_nr = 0; // first player
11466 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11468 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11469 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11470 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11471 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11472 si->touch.draw_outlined = TRUE;
11473 si->touch.draw_pressed = TRUE;
11475 for (i = 0; i < 2; i++)
11477 char *default_grid_button[6][2] =
11483 { "111222", " vv " },
11484 { "111222", " vv " }
11486 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11487 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11488 int min_xsize = MIN(6, grid_xsize);
11489 int min_ysize = MIN(6, grid_ysize);
11490 int startx = grid_xsize - min_xsize;
11491 int starty = grid_ysize - min_ysize;
11494 // virtual buttons grid can only be set to defaults if video is initialized
11495 // (this will be repeated if virtual buttons are not loaded from setup file)
11496 if (video.initialized)
11498 si->touch.grid_xsize[i] = grid_xsize;
11499 si->touch.grid_ysize[i] = grid_ysize;
11503 si->touch.grid_xsize[i] = -1;
11504 si->touch.grid_ysize[i] = -1;
11507 for (x = 0; x < MAX_GRID_XSIZE; x++)
11508 for (y = 0; y < MAX_GRID_YSIZE; y++)
11509 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11511 for (x = 0; x < min_xsize; x++)
11512 for (y = 0; y < min_ysize; y++)
11513 si->touch.grid_button[i][x][starty + y] =
11514 default_grid_button[y][0][x];
11516 for (x = 0; x < min_xsize; x++)
11517 for (y = 0; y < min_ysize; y++)
11518 si->touch.grid_button[i][startx + x][starty + y] =
11519 default_grid_button[y][1][x];
11522 si->touch.grid_initialized = video.initialized;
11524 si->touch.overlay_buttons = FALSE;
11526 si->editor.el_boulderdash = TRUE;
11527 si->editor.el_boulderdash_native = TRUE;
11528 si->editor.el_boulderdash_effects = TRUE;
11529 si->editor.el_emerald_mine = TRUE;
11530 si->editor.el_emerald_mine_club = TRUE;
11531 si->editor.el_more = TRUE;
11532 si->editor.el_sokoban = TRUE;
11533 si->editor.el_supaplex = TRUE;
11534 si->editor.el_diamond_caves = TRUE;
11535 si->editor.el_dx_boulderdash = TRUE;
11537 si->editor.el_mirror_magic = TRUE;
11538 si->editor.el_deflektor = TRUE;
11540 si->editor.el_chars = TRUE;
11541 si->editor.el_steel_chars = TRUE;
11543 si->editor.el_classic = TRUE;
11544 si->editor.el_custom = TRUE;
11546 si->editor.el_user_defined = FALSE;
11547 si->editor.el_dynamic = TRUE;
11549 si->editor.el_headlines = TRUE;
11551 si->editor.show_element_token = FALSE;
11553 si->editor.show_read_only_warning = TRUE;
11555 si->editor.use_template_for_new_levels = TRUE;
11557 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11558 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11559 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11560 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11561 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11563 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11564 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11565 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11566 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11567 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11569 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11570 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11571 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11572 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11573 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11574 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11576 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11577 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11578 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11580 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11581 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11582 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11583 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11585 for (i = 0; i < MAX_PLAYERS; i++)
11587 si->input[i].use_joystick = FALSE;
11588 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11589 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11590 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11591 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11592 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11593 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11594 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11595 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11596 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11597 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11598 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11599 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11600 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11601 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11602 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11605 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11606 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11607 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11608 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11610 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11611 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11612 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11613 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11614 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11615 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11616 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11618 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11620 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11621 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11622 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11624 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11625 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11626 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11628 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11629 si->internal.choose_from_top_leveldir = FALSE;
11630 si->internal.show_scaling_in_title = TRUE;
11631 si->internal.create_user_levelset = TRUE;
11632 si->internal.info_screens_from_main = FALSE;
11634 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11635 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11637 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11638 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11639 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11640 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11641 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11642 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11643 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11644 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11645 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11646 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11648 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11649 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11650 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11651 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11652 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11653 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11654 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11655 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11656 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11657 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11659 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11660 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11662 si->debug.show_frames_per_second = FALSE;
11664 si->debug.xsn_mode = AUTO;
11665 si->debug.xsn_percent = 0;
11667 si->options.verbose = FALSE;
11668 si->options.debug = FALSE;
11669 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11671 #if defined(PLATFORM_ANDROID)
11672 si->fullscreen = TRUE;
11673 si->touch.overlay_buttons = TRUE;
11676 setHideSetupEntry(&setup.debug.xsn_mode);
11679 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11681 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11684 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11686 si->player_uuid = NULL; // (will be set later)
11687 si->player_version = 1; // (will be set later)
11689 si->use_api_server = TRUE;
11690 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11691 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11692 si->ask_for_uploading_tapes = TRUE;
11693 si->ask_for_remaining_tapes = FALSE;
11694 si->provide_uploading_tapes = TRUE;
11695 si->ask_for_using_api_server = TRUE;
11696 si->has_remaining_tapes = FALSE;
11699 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11701 si->editor_cascade.el_bd = TRUE;
11702 si->editor_cascade.el_bd_native = TRUE;
11703 si->editor_cascade.el_bd_effects = FALSE;
11704 si->editor_cascade.el_em = TRUE;
11705 si->editor_cascade.el_emc = TRUE;
11706 si->editor_cascade.el_rnd = TRUE;
11707 si->editor_cascade.el_sb = TRUE;
11708 si->editor_cascade.el_sp = TRUE;
11709 si->editor_cascade.el_dc = TRUE;
11710 si->editor_cascade.el_dx = TRUE;
11712 si->editor_cascade.el_mm = TRUE;
11713 si->editor_cascade.el_df = TRUE;
11715 si->editor_cascade.el_chars = FALSE;
11716 si->editor_cascade.el_steel_chars = FALSE;
11717 si->editor_cascade.el_ce = FALSE;
11718 si->editor_cascade.el_ge = FALSE;
11719 si->editor_cascade.el_es = FALSE;
11720 si->editor_cascade.el_ref = FALSE;
11721 si->editor_cascade.el_user = FALSE;
11722 si->editor_cascade.el_dynamic = FALSE;
11725 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11727 static char *getHideSetupToken(void *setup_value)
11729 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11731 if (setup_value != NULL)
11732 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11734 return hide_setup_token;
11737 void setHideSetupEntry(void *setup_value)
11739 char *hide_setup_token = getHideSetupToken(setup_value);
11741 if (hide_setup_hash == NULL)
11742 hide_setup_hash = newSetupFileHash();
11744 if (setup_value != NULL)
11745 setHashEntry(hide_setup_hash, hide_setup_token, "");
11748 void removeHideSetupEntry(void *setup_value)
11750 char *hide_setup_token = getHideSetupToken(setup_value);
11752 if (setup_value != NULL)
11753 removeHashEntry(hide_setup_hash, hide_setup_token);
11756 boolean hideSetupEntry(void *setup_value)
11758 char *hide_setup_token = getHideSetupToken(setup_value);
11760 return (setup_value != NULL &&
11761 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11764 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11765 struct TokenInfo *token_info,
11766 int token_nr, char *token_text)
11768 char *token_hide_text = getStringCat2(token_text, ".hide");
11769 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11771 // set the value of this setup option in the setup option structure
11772 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11774 // check if this setup option should be hidden in the setup menu
11775 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11776 setHideSetupEntry(token_info[token_nr].value);
11778 free(token_hide_text);
11781 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11782 struct TokenInfo *token_info,
11785 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11786 token_info[token_nr].text);
11789 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11793 if (!setup_file_hash)
11796 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11797 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11799 setup.touch.grid_initialized = TRUE;
11800 for (i = 0; i < 2; i++)
11802 int grid_xsize = setup.touch.grid_xsize[i];
11803 int grid_ysize = setup.touch.grid_ysize[i];
11806 // if virtual buttons are not loaded from setup file, repeat initializing
11807 // virtual buttons grid with default values later when video is initialized
11808 if (grid_xsize == -1 ||
11811 setup.touch.grid_initialized = FALSE;
11816 for (y = 0; y < grid_ysize; y++)
11818 char token_string[MAX_LINE_LEN];
11820 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11822 char *value_string = getHashEntry(setup_file_hash, token_string);
11824 if (value_string == NULL)
11827 for (x = 0; x < grid_xsize; x++)
11829 char c = value_string[x];
11831 setup.touch.grid_button[i][x][y] =
11832 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11837 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11838 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11840 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11841 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11843 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11847 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11849 setup_input = setup.input[pnr];
11850 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11852 char full_token[100];
11854 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11855 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11858 setup.input[pnr] = setup_input;
11861 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11862 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11864 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11865 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11867 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11868 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11870 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11871 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11873 setHideRelatedSetupEntries();
11876 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11880 if (!setup_file_hash)
11883 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11884 setSetupInfo(auto_setup_tokens, i,
11885 getHashEntry(setup_file_hash,
11886 auto_setup_tokens[i].text));
11889 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11893 if (!setup_file_hash)
11896 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11897 setSetupInfo(server_setup_tokens, i,
11898 getHashEntry(setup_file_hash,
11899 server_setup_tokens[i].text));
11902 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11906 if (!setup_file_hash)
11909 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11910 setSetupInfo(editor_cascade_setup_tokens, i,
11911 getHashEntry(setup_file_hash,
11912 editor_cascade_setup_tokens[i].text));
11915 void LoadUserNames(void)
11917 int last_user_nr = user.nr;
11920 if (global.user_names != NULL)
11922 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11923 checked_free(global.user_names[i]);
11925 checked_free(global.user_names);
11928 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11930 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11934 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11936 if (setup_file_hash)
11938 char *player_name = getHashEntry(setup_file_hash, "player_name");
11940 global.user_names[i] = getFixedUserName(player_name);
11942 freeSetupFileHash(setup_file_hash);
11945 if (global.user_names[i] == NULL)
11946 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11949 user.nr = last_user_nr;
11952 void LoadSetupFromFilename(char *filename)
11954 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11956 if (setup_file_hash)
11958 decodeSetupFileHash_Default(setup_file_hash);
11960 freeSetupFileHash(setup_file_hash);
11964 Debug("setup", "using default setup values");
11968 static void LoadSetup_SpecialPostProcessing(void)
11970 char *player_name_new;
11972 // needed to work around problems with fixed length strings
11973 player_name_new = getFixedUserName(setup.player_name);
11974 free(setup.player_name);
11975 setup.player_name = player_name_new;
11977 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11978 if (setup.scroll_delay == FALSE)
11980 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11981 setup.scroll_delay = TRUE; // now always "on"
11984 // make sure that scroll delay value stays inside valid range
11985 setup.scroll_delay_value =
11986 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11989 void LoadSetup_Default(void)
11993 // always start with reliable default values
11994 setSetupInfoToDefaults(&setup);
11996 // try to load setup values from default setup file
11997 filename = getDefaultSetupFilename();
11999 if (fileExists(filename))
12000 LoadSetupFromFilename(filename);
12002 // try to load setup values from platform setup file
12003 filename = getPlatformSetupFilename();
12005 if (fileExists(filename))
12006 LoadSetupFromFilename(filename);
12008 // try to load setup values from user setup file
12009 filename = getSetupFilename();
12011 LoadSetupFromFilename(filename);
12013 LoadSetup_SpecialPostProcessing();
12016 void LoadSetup_AutoSetup(void)
12018 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12019 SetupFileHash *setup_file_hash = NULL;
12021 // always start with reliable default values
12022 setSetupInfoToDefaults_AutoSetup(&setup);
12024 setup_file_hash = loadSetupFileHash(filename);
12026 if (setup_file_hash)
12028 decodeSetupFileHash_AutoSetup(setup_file_hash);
12030 freeSetupFileHash(setup_file_hash);
12036 void LoadSetup_ServerSetup(void)
12038 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12039 SetupFileHash *setup_file_hash = NULL;
12041 // always start with reliable default values
12042 setSetupInfoToDefaults_ServerSetup(&setup);
12044 setup_file_hash = loadSetupFileHash(filename);
12046 if (setup_file_hash)
12048 decodeSetupFileHash_ServerSetup(setup_file_hash);
12050 freeSetupFileHash(setup_file_hash);
12055 if (setup.player_uuid == NULL)
12057 // player UUID does not yet exist in setup file
12058 setup.player_uuid = getStringCopy(getUUID());
12059 setup.player_version = 2;
12061 SaveSetup_ServerSetup();
12065 void LoadSetup_EditorCascade(void)
12067 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12068 SetupFileHash *setup_file_hash = NULL;
12070 // always start with reliable default values
12071 setSetupInfoToDefaults_EditorCascade(&setup);
12073 setup_file_hash = loadSetupFileHash(filename);
12075 if (setup_file_hash)
12077 decodeSetupFileHash_EditorCascade(setup_file_hash);
12079 freeSetupFileHash(setup_file_hash);
12085 void LoadSetup(void)
12087 LoadSetup_Default();
12088 LoadSetup_AutoSetup();
12089 LoadSetup_ServerSetup();
12090 LoadSetup_EditorCascade();
12093 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12094 char *mapping_line)
12096 char mapping_guid[MAX_LINE_LEN];
12097 char *mapping_start, *mapping_end;
12099 // get GUID from game controller mapping line: copy complete line
12100 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12101 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12103 // get GUID from game controller mapping line: cut after GUID part
12104 mapping_start = strchr(mapping_guid, ',');
12105 if (mapping_start != NULL)
12106 *mapping_start = '\0';
12108 // cut newline from game controller mapping line
12109 mapping_end = strchr(mapping_line, '\n');
12110 if (mapping_end != NULL)
12111 *mapping_end = '\0';
12113 // add mapping entry to game controller mappings hash
12114 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12117 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12122 if (!(file = fopen(filename, MODE_READ)))
12124 Warn("cannot read game controller mappings file '%s'", filename);
12129 while (!feof(file))
12131 char line[MAX_LINE_LEN];
12133 if (!fgets(line, MAX_LINE_LEN, file))
12136 addGameControllerMappingToHash(mappings_hash, line);
12142 void SaveSetup_Default(void)
12144 char *filename = getSetupFilename();
12148 InitUserDataDirectory();
12150 if (!(file = fopen(filename, MODE_WRITE)))
12152 Warn("cannot write setup file '%s'", filename);
12157 fprintFileHeader(file, SETUP_FILENAME);
12159 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12161 // just to make things nicer :)
12162 if (global_setup_tokens[i].value == &setup.multiple_users ||
12163 global_setup_tokens[i].value == &setup.sound ||
12164 global_setup_tokens[i].value == &setup.graphics_set ||
12165 global_setup_tokens[i].value == &setup.volume_simple ||
12166 global_setup_tokens[i].value == &setup.network_mode ||
12167 global_setup_tokens[i].value == &setup.touch.control_type ||
12168 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12169 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12170 fprintf(file, "\n");
12172 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12175 for (i = 0; i < 2; i++)
12177 int grid_xsize = setup.touch.grid_xsize[i];
12178 int grid_ysize = setup.touch.grid_ysize[i];
12181 fprintf(file, "\n");
12183 for (y = 0; y < grid_ysize; y++)
12185 char token_string[MAX_LINE_LEN];
12186 char value_string[MAX_LINE_LEN];
12188 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12190 for (x = 0; x < grid_xsize; x++)
12192 char c = setup.touch.grid_button[i][x][y];
12194 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12197 value_string[grid_xsize] = '\0';
12199 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12203 fprintf(file, "\n");
12204 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12205 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12207 fprintf(file, "\n");
12208 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12209 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12211 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12215 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12216 fprintf(file, "\n");
12218 setup_input = setup.input[pnr];
12219 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12220 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12223 fprintf(file, "\n");
12224 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12225 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12227 // (internal setup values not saved to user setup file)
12229 fprintf(file, "\n");
12230 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12231 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12232 setup.debug.xsn_mode != AUTO)
12233 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12235 fprintf(file, "\n");
12236 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12237 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12241 SetFilePermissions(filename, PERMS_PRIVATE);
12244 void SaveSetup_AutoSetup(void)
12246 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12250 InitUserDataDirectory();
12252 if (!(file = fopen(filename, MODE_WRITE)))
12254 Warn("cannot write auto setup file '%s'", filename);
12261 fprintFileHeader(file, AUTOSETUP_FILENAME);
12263 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12264 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12268 SetFilePermissions(filename, PERMS_PRIVATE);
12273 void SaveSetup_ServerSetup(void)
12275 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12279 InitUserDataDirectory();
12281 if (!(file = fopen(filename, MODE_WRITE)))
12283 Warn("cannot write server setup file '%s'", filename);
12290 fprintFileHeader(file, SERVERSETUP_FILENAME);
12292 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12294 // just to make things nicer :)
12295 if (server_setup_tokens[i].value == &setup.use_api_server)
12296 fprintf(file, "\n");
12298 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12303 SetFilePermissions(filename, PERMS_PRIVATE);
12308 void SaveSetup_EditorCascade(void)
12310 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12314 InitUserDataDirectory();
12316 if (!(file = fopen(filename, MODE_WRITE)))
12318 Warn("cannot write editor cascade state file '%s'", filename);
12325 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12327 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12328 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12332 SetFilePermissions(filename, PERMS_PRIVATE);
12337 void SaveSetup(void)
12339 SaveSetup_Default();
12340 SaveSetup_AutoSetup();
12341 SaveSetup_ServerSetup();
12342 SaveSetup_EditorCascade();
12345 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12350 if (!(file = fopen(filename, MODE_WRITE)))
12352 Warn("cannot write game controller mappings file '%s'", filename);
12357 BEGIN_HASH_ITERATION(mappings_hash, itr)
12359 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12361 END_HASH_ITERATION(mappings_hash, itr)
12366 void SaveSetup_AddGameControllerMapping(char *mapping)
12368 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12369 SetupFileHash *mappings_hash = newSetupFileHash();
12371 InitUserDataDirectory();
12373 // load existing personal game controller mappings
12374 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12376 // add new mapping to personal game controller mappings
12377 addGameControllerMappingToHash(mappings_hash, mapping);
12379 // save updated personal game controller mappings
12380 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12382 freeSetupFileHash(mappings_hash);
12386 void LoadCustomElementDescriptions(void)
12388 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12389 SetupFileHash *setup_file_hash;
12392 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12394 if (element_info[i].custom_description != NULL)
12396 free(element_info[i].custom_description);
12397 element_info[i].custom_description = NULL;
12401 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12404 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12406 char *token = getStringCat2(element_info[i].token_name, ".name");
12407 char *value = getHashEntry(setup_file_hash, token);
12410 element_info[i].custom_description = getStringCopy(value);
12415 freeSetupFileHash(setup_file_hash);
12418 static int getElementFromToken(char *token)
12420 char *value = getHashEntry(element_token_hash, token);
12423 return atoi(value);
12425 Warn("unknown element token '%s'", token);
12427 return EL_UNDEFINED;
12430 void FreeGlobalAnimEventInfo(void)
12432 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12434 if (gaei->event_list == NULL)
12439 for (i = 0; i < gaei->num_event_lists; i++)
12441 checked_free(gaei->event_list[i]->event_value);
12442 checked_free(gaei->event_list[i]);
12445 checked_free(gaei->event_list);
12447 gaei->event_list = NULL;
12448 gaei->num_event_lists = 0;
12451 static int AddGlobalAnimEventList(void)
12453 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12454 int list_pos = gaei->num_event_lists++;
12456 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12457 sizeof(struct GlobalAnimEventListInfo *));
12459 gaei->event_list[list_pos] =
12460 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12462 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12464 gaeli->event_value = NULL;
12465 gaeli->num_event_values = 0;
12470 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12472 // do not add empty global animation events
12473 if (event_value == ANIM_EVENT_NONE)
12476 // if list position is undefined, create new list
12477 if (list_pos == ANIM_EVENT_UNDEFINED)
12478 list_pos = AddGlobalAnimEventList();
12480 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12481 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12482 int value_pos = gaeli->num_event_values++;
12484 gaeli->event_value = checked_realloc(gaeli->event_value,
12485 gaeli->num_event_values * sizeof(int *));
12487 gaeli->event_value[value_pos] = event_value;
12492 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12494 if (list_pos == ANIM_EVENT_UNDEFINED)
12495 return ANIM_EVENT_NONE;
12497 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12498 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12500 return gaeli->event_value[value_pos];
12503 int GetGlobalAnimEventValueCount(int list_pos)
12505 if (list_pos == ANIM_EVENT_UNDEFINED)
12508 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12509 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12511 return gaeli->num_event_values;
12514 // This function checks if a string <s> of the format "string1, string2, ..."
12515 // exactly contains a string <s_contained>.
12517 static boolean string_has_parameter(char *s, char *s_contained)
12521 if (s == NULL || s_contained == NULL)
12524 if (strlen(s_contained) > strlen(s))
12527 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12529 char next_char = s[strlen(s_contained)];
12531 // check if next character is delimiter or whitespace
12532 if (next_char == ',' || next_char == '\0' ||
12533 next_char == ' ' || next_char == '\t')
12537 // check if string contains another parameter string after a comma
12538 substring = strchr(s, ',');
12539 if (substring == NULL) // string does not contain a comma
12542 // advance string pointer to next character after the comma
12545 // skip potential whitespaces after the comma
12546 while (*substring == ' ' || *substring == '\t')
12549 return string_has_parameter(substring, s_contained);
12552 static int get_anim_parameter_value_ce(char *s)
12555 char *pattern_1 = "ce_change:custom_";
12556 char *pattern_2 = ".page_";
12557 int pattern_1_len = strlen(pattern_1);
12558 char *matching_char = strstr(s_ptr, pattern_1);
12559 int result = ANIM_EVENT_NONE;
12561 if (matching_char == NULL)
12562 return ANIM_EVENT_NONE;
12564 result = ANIM_EVENT_CE_CHANGE;
12566 s_ptr = matching_char + pattern_1_len;
12568 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12569 if (*s_ptr >= '0' && *s_ptr <= '9')
12571 int gic_ce_nr = (*s_ptr++ - '0');
12573 if (*s_ptr >= '0' && *s_ptr <= '9')
12575 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12577 if (*s_ptr >= '0' && *s_ptr <= '9')
12578 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12581 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12582 return ANIM_EVENT_NONE;
12584 // custom element stored as 0 to 255
12587 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12591 // invalid custom element number specified
12593 return ANIM_EVENT_NONE;
12596 // check for change page number ("page_X" or "page_XX") (optional)
12597 if (strPrefix(s_ptr, pattern_2))
12599 s_ptr += strlen(pattern_2);
12601 if (*s_ptr >= '0' && *s_ptr <= '9')
12603 int gic_page_nr = (*s_ptr++ - '0');
12605 if (*s_ptr >= '0' && *s_ptr <= '9')
12606 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12608 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12609 return ANIM_EVENT_NONE;
12611 // change page stored as 1 to 32 (0 means "all change pages")
12613 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12617 // invalid animation part number specified
12619 return ANIM_EVENT_NONE;
12623 // discard result if next character is neither delimiter nor whitespace
12624 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12625 *s_ptr == ' ' || *s_ptr == '\t'))
12626 return ANIM_EVENT_NONE;
12631 static int get_anim_parameter_value(char *s)
12633 int event_value[] =
12641 char *pattern_1[] =
12649 char *pattern_2 = ".part_";
12650 char *matching_char = NULL;
12652 int pattern_1_len = 0;
12653 int result = ANIM_EVENT_NONE;
12656 result = get_anim_parameter_value_ce(s);
12658 if (result != ANIM_EVENT_NONE)
12661 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12663 matching_char = strstr(s_ptr, pattern_1[i]);
12664 pattern_1_len = strlen(pattern_1[i]);
12665 result = event_value[i];
12667 if (matching_char != NULL)
12671 if (matching_char == NULL)
12672 return ANIM_EVENT_NONE;
12674 s_ptr = matching_char + pattern_1_len;
12676 // check for main animation number ("anim_X" or "anim_XX")
12677 if (*s_ptr >= '0' && *s_ptr <= '9')
12679 int gic_anim_nr = (*s_ptr++ - '0');
12681 if (*s_ptr >= '0' && *s_ptr <= '9')
12682 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12684 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12685 return ANIM_EVENT_NONE;
12687 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12691 // invalid main animation number specified
12693 return ANIM_EVENT_NONE;
12696 // check for animation part number ("part_X" or "part_XX") (optional)
12697 if (strPrefix(s_ptr, pattern_2))
12699 s_ptr += strlen(pattern_2);
12701 if (*s_ptr >= '0' && *s_ptr <= '9')
12703 int gic_part_nr = (*s_ptr++ - '0');
12705 if (*s_ptr >= '0' && *s_ptr <= '9')
12706 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12708 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12709 return ANIM_EVENT_NONE;
12711 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12715 // invalid animation part number specified
12717 return ANIM_EVENT_NONE;
12721 // discard result if next character is neither delimiter nor whitespace
12722 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12723 *s_ptr == ' ' || *s_ptr == '\t'))
12724 return ANIM_EVENT_NONE;
12729 static int get_anim_parameter_values(char *s)
12731 int list_pos = ANIM_EVENT_UNDEFINED;
12732 int event_value = ANIM_EVENT_DEFAULT;
12734 if (string_has_parameter(s, "any"))
12735 event_value |= ANIM_EVENT_ANY;
12737 if (string_has_parameter(s, "click:self") ||
12738 string_has_parameter(s, "click") ||
12739 string_has_parameter(s, "self"))
12740 event_value |= ANIM_EVENT_SELF;
12742 if (string_has_parameter(s, "unclick:any"))
12743 event_value |= ANIM_EVENT_UNCLICK_ANY;
12745 // if animation event found, add it to global animation event list
12746 if (event_value != ANIM_EVENT_NONE)
12747 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12751 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12752 event_value = get_anim_parameter_value(s);
12754 // if animation event found, add it to global animation event list
12755 if (event_value != ANIM_EVENT_NONE)
12756 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12758 // continue with next part of the string, starting with next comma
12759 s = strchr(s + 1, ',');
12765 static int get_anim_action_parameter_value(char *token)
12767 // check most common default case first to massively speed things up
12768 if (strEqual(token, ARG_UNDEFINED))
12769 return ANIM_EVENT_ACTION_NONE;
12771 int result = getImageIDFromToken(token);
12775 char *gfx_token = getStringCat2("gfx.", token);
12777 result = getImageIDFromToken(gfx_token);
12779 checked_free(gfx_token);
12784 Key key = getKeyFromX11KeyName(token);
12786 if (key != KSYM_UNDEFINED)
12787 result = -(int)key;
12794 result = get_hash_from_string(token); // unsigned int => int
12795 result = ABS(result); // may be negative now
12796 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12798 setHashEntry(anim_url_hash, int2str(result, 0), token);
12803 result = ANIM_EVENT_ACTION_NONE;
12808 int get_parameter_value(char *value_raw, char *suffix, int type)
12810 char *value = getStringToLower(value_raw);
12811 int result = 0; // probably a save default value
12813 if (strEqual(suffix, ".direction"))
12815 result = (strEqual(value, "left") ? MV_LEFT :
12816 strEqual(value, "right") ? MV_RIGHT :
12817 strEqual(value, "up") ? MV_UP :
12818 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12820 else if (strEqual(suffix, ".position"))
12822 result = (strEqual(value, "left") ? POS_LEFT :
12823 strEqual(value, "right") ? POS_RIGHT :
12824 strEqual(value, "top") ? POS_TOP :
12825 strEqual(value, "upper") ? POS_UPPER :
12826 strEqual(value, "middle") ? POS_MIDDLE :
12827 strEqual(value, "lower") ? POS_LOWER :
12828 strEqual(value, "bottom") ? POS_BOTTOM :
12829 strEqual(value, "any") ? POS_ANY :
12830 strEqual(value, "ce") ? POS_CE :
12831 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12832 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12834 else if (strEqual(suffix, ".align"))
12836 result = (strEqual(value, "left") ? ALIGN_LEFT :
12837 strEqual(value, "right") ? ALIGN_RIGHT :
12838 strEqual(value, "center") ? ALIGN_CENTER :
12839 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12841 else if (strEqual(suffix, ".valign"))
12843 result = (strEqual(value, "top") ? VALIGN_TOP :
12844 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12845 strEqual(value, "middle") ? VALIGN_MIDDLE :
12846 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12848 else if (strEqual(suffix, ".anim_mode"))
12850 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12851 string_has_parameter(value, "loop") ? ANIM_LOOP :
12852 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12853 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12854 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12855 string_has_parameter(value, "random") ? ANIM_RANDOM :
12856 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12857 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12858 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12859 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12860 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12861 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12862 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12863 string_has_parameter(value, "all") ? ANIM_ALL :
12864 string_has_parameter(value, "tiled") ? ANIM_TILED :
12865 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12868 if (string_has_parameter(value, "once"))
12869 result |= ANIM_ONCE;
12871 if (string_has_parameter(value, "reverse"))
12872 result |= ANIM_REVERSE;
12874 if (string_has_parameter(value, "opaque_player"))
12875 result |= ANIM_OPAQUE_PLAYER;
12877 if (string_has_parameter(value, "static_panel"))
12878 result |= ANIM_STATIC_PANEL;
12880 else if (strEqual(suffix, ".init_event") ||
12881 strEqual(suffix, ".anim_event"))
12883 result = get_anim_parameter_values(value);
12885 else if (strEqual(suffix, ".init_delay_action") ||
12886 strEqual(suffix, ".anim_delay_action") ||
12887 strEqual(suffix, ".post_delay_action") ||
12888 strEqual(suffix, ".init_event_action") ||
12889 strEqual(suffix, ".anim_event_action"))
12891 result = get_anim_action_parameter_value(value_raw);
12893 else if (strEqual(suffix, ".class"))
12895 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12896 get_hash_from_string(value));
12898 else if (strEqual(suffix, ".style"))
12900 result = STYLE_DEFAULT;
12902 if (string_has_parameter(value, "accurate_borders"))
12903 result |= STYLE_ACCURATE_BORDERS;
12905 if (string_has_parameter(value, "inner_corners"))
12906 result |= STYLE_INNER_CORNERS;
12908 if (string_has_parameter(value, "reverse"))
12909 result |= STYLE_REVERSE;
12911 if (string_has_parameter(value, "leftmost_position"))
12912 result |= STYLE_LEFTMOST_POSITION;
12914 if (string_has_parameter(value, "block_clicks"))
12915 result |= STYLE_BLOCK;
12917 if (string_has_parameter(value, "passthrough_clicks"))
12918 result |= STYLE_PASSTHROUGH;
12920 if (string_has_parameter(value, "multiple_actions"))
12921 result |= STYLE_MULTIPLE_ACTIONS;
12923 if (string_has_parameter(value, "consume_ce_event"))
12924 result |= STYLE_CONSUME_CE_EVENT;
12926 else if (strEqual(suffix, ".fade_mode"))
12928 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12929 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12930 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12931 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12932 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12933 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12934 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12935 FADE_MODE_DEFAULT);
12937 else if (strEqual(suffix, ".auto_delay_unit"))
12939 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12940 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12941 AUTO_DELAY_UNIT_DEFAULT);
12943 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12945 result = gfx.get_font_from_token_function(value);
12947 else // generic parameter of type integer or boolean
12949 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12950 type == TYPE_INTEGER ? get_integer_from_string(value) :
12951 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12952 ARG_UNDEFINED_VALUE);
12960 static int get_token_parameter_value(char *token, char *value_raw)
12964 if (token == NULL || value_raw == NULL)
12965 return ARG_UNDEFINED_VALUE;
12967 suffix = strrchr(token, '.');
12968 if (suffix == NULL)
12971 if (strEqual(suffix, ".element"))
12972 return getElementFromToken(value_raw);
12974 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12975 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12978 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12979 boolean ignore_defaults)
12983 for (i = 0; image_config_vars[i].token != NULL; i++)
12985 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12987 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12988 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12992 *image_config_vars[i].value =
12993 get_token_parameter_value(image_config_vars[i].token, value);
12997 void InitMenuDesignSettings_Static(void)
12999 // always start with reliable default values from static default config
13000 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13003 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13007 // the following initializes hierarchical values from static configuration
13009 // special case: initialize "ARG_DEFAULT" values in static default config
13010 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13011 titlescreen_initial_first_default.fade_mode =
13012 title_initial_first_default.fade_mode;
13013 titlescreen_initial_first_default.fade_delay =
13014 title_initial_first_default.fade_delay;
13015 titlescreen_initial_first_default.post_delay =
13016 title_initial_first_default.post_delay;
13017 titlescreen_initial_first_default.auto_delay =
13018 title_initial_first_default.auto_delay;
13019 titlescreen_initial_first_default.auto_delay_unit =
13020 title_initial_first_default.auto_delay_unit;
13021 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13022 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13023 titlescreen_first_default.post_delay = title_first_default.post_delay;
13024 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13025 titlescreen_first_default.auto_delay_unit =
13026 title_first_default.auto_delay_unit;
13027 titlemessage_initial_first_default.fade_mode =
13028 title_initial_first_default.fade_mode;
13029 titlemessage_initial_first_default.fade_delay =
13030 title_initial_first_default.fade_delay;
13031 titlemessage_initial_first_default.post_delay =
13032 title_initial_first_default.post_delay;
13033 titlemessage_initial_first_default.auto_delay =
13034 title_initial_first_default.auto_delay;
13035 titlemessage_initial_first_default.auto_delay_unit =
13036 title_initial_first_default.auto_delay_unit;
13037 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13038 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13039 titlemessage_first_default.post_delay = title_first_default.post_delay;
13040 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13041 titlemessage_first_default.auto_delay_unit =
13042 title_first_default.auto_delay_unit;
13044 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13045 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13046 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13047 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13048 titlescreen_initial_default.auto_delay_unit =
13049 title_initial_default.auto_delay_unit;
13050 titlescreen_default.fade_mode = title_default.fade_mode;
13051 titlescreen_default.fade_delay = title_default.fade_delay;
13052 titlescreen_default.post_delay = title_default.post_delay;
13053 titlescreen_default.auto_delay = title_default.auto_delay;
13054 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13055 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13056 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13057 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13058 titlemessage_initial_default.auto_delay_unit =
13059 title_initial_default.auto_delay_unit;
13060 titlemessage_default.fade_mode = title_default.fade_mode;
13061 titlemessage_default.fade_delay = title_default.fade_delay;
13062 titlemessage_default.post_delay = title_default.post_delay;
13063 titlemessage_default.auto_delay = title_default.auto_delay;
13064 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13066 // special case: initialize "ARG_DEFAULT" values in static default config
13067 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13068 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13070 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13071 titlescreen_first[i] = titlescreen_first_default;
13072 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13073 titlemessage_first[i] = titlemessage_first_default;
13075 titlescreen_initial[i] = titlescreen_initial_default;
13076 titlescreen[i] = titlescreen_default;
13077 titlemessage_initial[i] = titlemessage_initial_default;
13078 titlemessage[i] = titlemessage_default;
13081 // special case: initialize "ARG_DEFAULT" values in static default config
13082 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13083 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13085 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13088 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13089 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13090 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13093 // special case: initialize "ARG_DEFAULT" values in static default config
13094 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13095 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13097 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13098 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13099 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13101 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13104 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13108 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13112 struct XY *dst, *src;
13114 game_buttons_xy[] =
13116 { &game.button.save, &game.button.stop },
13117 { &game.button.pause2, &game.button.pause },
13118 { &game.button.load, &game.button.play },
13119 { &game.button.undo, &game.button.stop },
13120 { &game.button.redo, &game.button.play },
13126 // special case: initialize later added SETUP list size from LEVELS value
13127 if (menu.list_size[GAME_MODE_SETUP] == -1)
13128 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13130 // set default position for snapshot buttons to stop/pause/play buttons
13131 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13132 if ((*game_buttons_xy[i].dst).x == -1 &&
13133 (*game_buttons_xy[i].dst).y == -1)
13134 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13136 // --------------------------------------------------------------------------
13137 // dynamic viewports (including playfield margins, borders and alignments)
13138 // --------------------------------------------------------------------------
13140 // dynamic viewports currently only supported for landscape mode
13141 int display_width = MAX(video.display_width, video.display_height);
13142 int display_height = MIN(video.display_width, video.display_height);
13144 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13146 struct RectWithBorder *vp_window = &viewport.window[i];
13147 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13148 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13149 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13150 boolean dynamic_window_width = (vp_window->min_width != -1);
13151 boolean dynamic_window_height = (vp_window->min_height != -1);
13152 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13153 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13155 // adjust window size if min/max width/height is specified
13157 if (vp_window->min_width != -1)
13159 int window_width = display_width;
13161 // when using static window height, use aspect ratio of display
13162 if (vp_window->min_height == -1)
13163 window_width = vp_window->height * display_width / display_height;
13165 vp_window->width = MAX(vp_window->min_width, window_width);
13168 if (vp_window->min_height != -1)
13170 int window_height = display_height;
13172 // when using static window width, use aspect ratio of display
13173 if (vp_window->min_width == -1)
13174 window_height = vp_window->width * display_height / display_width;
13176 vp_window->height = MAX(vp_window->min_height, window_height);
13179 if (vp_window->max_width != -1)
13180 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13182 if (vp_window->max_height != -1)
13183 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13185 int playfield_width = vp_window->width;
13186 int playfield_height = vp_window->height;
13188 // adjust playfield size and position according to specified margins
13190 playfield_width -= vp_playfield->margin_left;
13191 playfield_width -= vp_playfield->margin_right;
13193 playfield_height -= vp_playfield->margin_top;
13194 playfield_height -= vp_playfield->margin_bottom;
13196 // adjust playfield size if min/max width/height is specified
13198 if (vp_playfield->min_width != -1)
13199 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13201 if (vp_playfield->min_height != -1)
13202 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13204 if (vp_playfield->max_width != -1)
13205 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13207 if (vp_playfield->max_height != -1)
13208 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13210 // adjust playfield position according to specified alignment
13212 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13213 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13214 else if (vp_playfield->align == ALIGN_CENTER)
13215 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13216 else if (vp_playfield->align == ALIGN_RIGHT)
13217 vp_playfield->x += playfield_width - vp_playfield->width;
13219 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13220 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13221 else if (vp_playfield->valign == VALIGN_MIDDLE)
13222 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13223 else if (vp_playfield->valign == VALIGN_BOTTOM)
13224 vp_playfield->y += playfield_height - vp_playfield->height;
13226 vp_playfield->x += vp_playfield->margin_left;
13227 vp_playfield->y += vp_playfield->margin_top;
13229 // adjust individual playfield borders if only default border is specified
13231 if (vp_playfield->border_left == -1)
13232 vp_playfield->border_left = vp_playfield->border_size;
13233 if (vp_playfield->border_right == -1)
13234 vp_playfield->border_right = vp_playfield->border_size;
13235 if (vp_playfield->border_top == -1)
13236 vp_playfield->border_top = vp_playfield->border_size;
13237 if (vp_playfield->border_bottom == -1)
13238 vp_playfield->border_bottom = vp_playfield->border_size;
13240 // set dynamic playfield borders if borders are specified as undefined
13241 // (but only if window size was dynamic and playfield size was static)
13243 if (dynamic_window_width && !dynamic_playfield_width)
13245 if (vp_playfield->border_left == -1)
13247 vp_playfield->border_left = (vp_playfield->x -
13248 vp_playfield->margin_left);
13249 vp_playfield->x -= vp_playfield->border_left;
13250 vp_playfield->width += vp_playfield->border_left;
13253 if (vp_playfield->border_right == -1)
13255 vp_playfield->border_right = (vp_window->width -
13257 vp_playfield->width -
13258 vp_playfield->margin_right);
13259 vp_playfield->width += vp_playfield->border_right;
13263 if (dynamic_window_height && !dynamic_playfield_height)
13265 if (vp_playfield->border_top == -1)
13267 vp_playfield->border_top = (vp_playfield->y -
13268 vp_playfield->margin_top);
13269 vp_playfield->y -= vp_playfield->border_top;
13270 vp_playfield->height += vp_playfield->border_top;
13273 if (vp_playfield->border_bottom == -1)
13275 vp_playfield->border_bottom = (vp_window->height -
13277 vp_playfield->height -
13278 vp_playfield->margin_bottom);
13279 vp_playfield->height += vp_playfield->border_bottom;
13283 // adjust playfield size to be a multiple of a defined alignment tile size
13285 int align_size = vp_playfield->align_size;
13286 int playfield_xtiles = vp_playfield->width / align_size;
13287 int playfield_ytiles = vp_playfield->height / align_size;
13288 int playfield_width_corrected = playfield_xtiles * align_size;
13289 int playfield_height_corrected = playfield_ytiles * align_size;
13290 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13291 i == GFX_SPECIAL_ARG_EDITOR);
13293 if (is_playfield_mode &&
13294 dynamic_playfield_width &&
13295 vp_playfield->width != playfield_width_corrected)
13297 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13299 vp_playfield->width = playfield_width_corrected;
13301 if (vp_playfield->align == ALIGN_LEFT)
13303 vp_playfield->border_left += playfield_xdiff;
13305 else if (vp_playfield->align == ALIGN_RIGHT)
13307 vp_playfield->border_right += playfield_xdiff;
13309 else if (vp_playfield->align == ALIGN_CENTER)
13311 int border_left_diff = playfield_xdiff / 2;
13312 int border_right_diff = playfield_xdiff - border_left_diff;
13314 vp_playfield->border_left += border_left_diff;
13315 vp_playfield->border_right += border_right_diff;
13319 if (is_playfield_mode &&
13320 dynamic_playfield_height &&
13321 vp_playfield->height != playfield_height_corrected)
13323 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13325 vp_playfield->height = playfield_height_corrected;
13327 if (vp_playfield->valign == VALIGN_TOP)
13329 vp_playfield->border_top += playfield_ydiff;
13331 else if (vp_playfield->align == VALIGN_BOTTOM)
13333 vp_playfield->border_right += playfield_ydiff;
13335 else if (vp_playfield->align == VALIGN_MIDDLE)
13337 int border_top_diff = playfield_ydiff / 2;
13338 int border_bottom_diff = playfield_ydiff - border_top_diff;
13340 vp_playfield->border_top += border_top_diff;
13341 vp_playfield->border_bottom += border_bottom_diff;
13345 // adjust door positions according to specified alignment
13347 for (j = 0; j < 2; j++)
13349 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13351 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13352 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13353 else if (vp_door->align == ALIGN_CENTER)
13354 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13355 else if (vp_door->align == ALIGN_RIGHT)
13356 vp_door->x += vp_window->width - vp_door->width;
13358 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13359 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13360 else if (vp_door->valign == VALIGN_MIDDLE)
13361 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13362 else if (vp_door->valign == VALIGN_BOTTOM)
13363 vp_door->y += vp_window->height - vp_door->height;
13368 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13372 struct XYTileSize *dst, *src;
13375 editor_buttons_xy[] =
13378 &editor.button.element_left, &editor.palette.element_left,
13379 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13382 &editor.button.element_middle, &editor.palette.element_middle,
13383 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13386 &editor.button.element_right, &editor.palette.element_right,
13387 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13394 // set default position for element buttons to element graphics
13395 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13397 if ((*editor_buttons_xy[i].dst).x == -1 &&
13398 (*editor_buttons_xy[i].dst).y == -1)
13400 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13402 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13404 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13408 // adjust editor palette rows and columns if specified to be dynamic
13410 if (editor.palette.cols == -1)
13412 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13413 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13414 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13416 editor.palette.cols = (vp_width - sc_width) / bt_width;
13418 if (editor.palette.x == -1)
13420 int palette_width = editor.palette.cols * bt_width + sc_width;
13422 editor.palette.x = (vp_width - palette_width) / 2;
13426 if (editor.palette.rows == -1)
13428 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13429 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13430 int tx_height = getFontHeight(FONT_TEXT_2);
13432 editor.palette.rows = (vp_height - tx_height) / bt_height;
13434 if (editor.palette.y == -1)
13436 int palette_height = editor.palette.rows * bt_height + tx_height;
13438 editor.palette.y = (vp_height - palette_height) / 2;
13443 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13444 boolean initialize)
13446 // special case: check if network and preview player positions are redefined,
13447 // to compare this later against the main menu level preview being redefined
13448 struct TokenIntPtrInfo menu_config_players[] =
13450 { "main.network_players.x", &menu.main.network_players.redefined },
13451 { "main.network_players.y", &menu.main.network_players.redefined },
13452 { "main.preview_players.x", &menu.main.preview_players.redefined },
13453 { "main.preview_players.y", &menu.main.preview_players.redefined },
13454 { "preview.x", &preview.redefined },
13455 { "preview.y", &preview.redefined }
13461 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13462 *menu_config_players[i].value = FALSE;
13466 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13467 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13468 *menu_config_players[i].value = TRUE;
13472 static void InitMenuDesignSettings_PreviewPlayers(void)
13474 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13477 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13479 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13482 static void LoadMenuDesignSettingsFromFilename(char *filename)
13484 static struct TitleFadingInfo tfi;
13485 static struct TitleMessageInfo tmi;
13486 static struct TokenInfo title_tokens[] =
13488 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13489 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13490 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13491 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13492 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13496 static struct TokenInfo titlemessage_tokens[] =
13498 { TYPE_INTEGER, &tmi.x, ".x" },
13499 { TYPE_INTEGER, &tmi.y, ".y" },
13500 { TYPE_INTEGER, &tmi.width, ".width" },
13501 { TYPE_INTEGER, &tmi.height, ".height" },
13502 { TYPE_INTEGER, &tmi.chars, ".chars" },
13503 { TYPE_INTEGER, &tmi.lines, ".lines" },
13504 { TYPE_INTEGER, &tmi.align, ".align" },
13505 { TYPE_INTEGER, &tmi.valign, ".valign" },
13506 { TYPE_INTEGER, &tmi.font, ".font" },
13507 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13508 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13509 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13510 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13511 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13512 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13513 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13514 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13515 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13521 struct TitleFadingInfo *info;
13526 // initialize first titles from "enter screen" definitions, if defined
13527 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13528 { &title_first_default, "menu.enter_screen.TITLE" },
13530 // initialize title screens from "next screen" definitions, if defined
13531 { &title_initial_default, "menu.next_screen.TITLE" },
13532 { &title_default, "menu.next_screen.TITLE" },
13538 struct TitleMessageInfo *array;
13541 titlemessage_arrays[] =
13543 // initialize first titles from "enter screen" definitions, if defined
13544 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13545 { titlescreen_first, "menu.enter_screen.TITLE" },
13546 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13547 { titlemessage_first, "menu.enter_screen.TITLE" },
13549 // initialize titles from "next screen" definitions, if defined
13550 { titlescreen_initial, "menu.next_screen.TITLE" },
13551 { titlescreen, "menu.next_screen.TITLE" },
13552 { titlemessage_initial, "menu.next_screen.TITLE" },
13553 { titlemessage, "menu.next_screen.TITLE" },
13555 // overwrite titles with title definitions, if defined
13556 { titlescreen_initial_first, "[title_initial]" },
13557 { titlescreen_first, "[title]" },
13558 { titlemessage_initial_first, "[title_initial]" },
13559 { titlemessage_first, "[title]" },
13561 { titlescreen_initial, "[title_initial]" },
13562 { titlescreen, "[title]" },
13563 { titlemessage_initial, "[title_initial]" },
13564 { titlemessage, "[title]" },
13566 // overwrite titles with title screen/message definitions, if defined
13567 { titlescreen_initial_first, "[titlescreen_initial]" },
13568 { titlescreen_first, "[titlescreen]" },
13569 { titlemessage_initial_first, "[titlemessage_initial]" },
13570 { titlemessage_first, "[titlemessage]" },
13572 { titlescreen_initial, "[titlescreen_initial]" },
13573 { titlescreen, "[titlescreen]" },
13574 { titlemessage_initial, "[titlemessage_initial]" },
13575 { titlemessage, "[titlemessage]" },
13579 SetupFileHash *setup_file_hash;
13582 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13585 // the following initializes hierarchical values from dynamic configuration
13587 // special case: initialize with default values that may be overwritten
13588 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13589 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13591 struct TokenIntPtrInfo menu_config[] =
13593 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13594 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13595 { "menu.list_size", &menu.list_size[i] }
13598 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13600 char *token = menu_config[j].token;
13601 char *value = getHashEntry(setup_file_hash, token);
13604 *menu_config[j].value = get_integer_from_string(value);
13608 // special case: initialize with default values that may be overwritten
13609 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13610 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13612 struct TokenIntPtrInfo menu_config[] =
13614 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13615 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13616 { "menu.list_size.INFO", &menu.list_size_info[i] },
13617 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13618 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13621 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13623 char *token = menu_config[j].token;
13624 char *value = getHashEntry(setup_file_hash, token);
13627 *menu_config[j].value = get_integer_from_string(value);
13631 // special case: initialize with default values that may be overwritten
13632 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13633 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13635 struct TokenIntPtrInfo menu_config[] =
13637 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13638 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13641 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13643 char *token = menu_config[j].token;
13644 char *value = getHashEntry(setup_file_hash, token);
13647 *menu_config[j].value = get_integer_from_string(value);
13651 // special case: initialize with default values that may be overwritten
13652 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13653 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13655 struct TokenIntPtrInfo menu_config[] =
13657 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13658 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13659 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13660 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13661 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13662 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13663 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13664 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13665 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13666 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13669 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13671 char *token = menu_config[j].token;
13672 char *value = getHashEntry(setup_file_hash, token);
13675 *menu_config[j].value = get_integer_from_string(value);
13679 // special case: initialize with default values that may be overwritten
13680 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13681 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13683 struct TokenIntPtrInfo menu_config[] =
13685 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13686 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13687 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13688 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13689 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13690 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13691 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13692 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13693 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13696 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13698 char *token = menu_config[j].token;
13699 char *value = getHashEntry(setup_file_hash, token);
13702 *menu_config[j].value = get_token_parameter_value(token, value);
13706 // special case: initialize with default values that may be overwritten
13707 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13708 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13712 char *token_prefix;
13713 struct RectWithBorder *struct_ptr;
13717 { "viewport.window", &viewport.window[i] },
13718 { "viewport.playfield", &viewport.playfield[i] },
13719 { "viewport.door_1", &viewport.door_1[i] },
13720 { "viewport.door_2", &viewport.door_2[i] }
13723 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13725 struct TokenIntPtrInfo vp_config[] =
13727 { ".x", &vp_struct[j].struct_ptr->x },
13728 { ".y", &vp_struct[j].struct_ptr->y },
13729 { ".width", &vp_struct[j].struct_ptr->width },
13730 { ".height", &vp_struct[j].struct_ptr->height },
13731 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13732 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13733 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13734 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13735 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13736 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13737 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13738 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13739 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13740 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13741 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13742 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13743 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13744 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13745 { ".align", &vp_struct[j].struct_ptr->align },
13746 { ".valign", &vp_struct[j].struct_ptr->valign }
13749 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13751 char *token = getStringCat2(vp_struct[j].token_prefix,
13752 vp_config[k].token);
13753 char *value = getHashEntry(setup_file_hash, token);
13756 *vp_config[k].value = get_token_parameter_value(token, value);
13763 // special case: initialize with default values that may be overwritten
13764 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13765 for (i = 0; title_info[i].info != NULL; i++)
13767 struct TitleFadingInfo *info = title_info[i].info;
13768 char *base_token = title_info[i].text;
13770 for (j = 0; title_tokens[j].type != -1; j++)
13772 char *token = getStringCat2(base_token, title_tokens[j].text);
13773 char *value = getHashEntry(setup_file_hash, token);
13777 int parameter_value = get_token_parameter_value(token, value);
13781 *(int *)title_tokens[j].value = (int)parameter_value;
13790 // special case: initialize with default values that may be overwritten
13791 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13792 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13794 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13795 char *base_token = titlemessage_arrays[i].text;
13797 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13799 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13800 char *value = getHashEntry(setup_file_hash, token);
13804 int parameter_value = get_token_parameter_value(token, value);
13806 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13810 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13811 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13813 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13823 // read (and overwrite with) values that may be specified in config file
13824 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13826 // special case: check if network and preview player positions are redefined
13827 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13829 freeSetupFileHash(setup_file_hash);
13832 void LoadMenuDesignSettings(void)
13834 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13836 InitMenuDesignSettings_Static();
13837 InitMenuDesignSettings_SpecialPreProcessing();
13838 InitMenuDesignSettings_PreviewPlayers();
13840 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13842 // first look for special settings configured in level series config
13843 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13845 if (fileExists(filename_base))
13846 LoadMenuDesignSettingsFromFilename(filename_base);
13849 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13851 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13852 LoadMenuDesignSettingsFromFilename(filename_local);
13854 InitMenuDesignSettings_SpecialPostProcessing();
13857 void LoadMenuDesignSettings_AfterGraphics(void)
13859 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13862 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13863 boolean ignore_defaults)
13867 for (i = 0; sound_config_vars[i].token != NULL; i++)
13869 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13871 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13872 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13876 *sound_config_vars[i].value =
13877 get_token_parameter_value(sound_config_vars[i].token, value);
13881 void InitSoundSettings_Static(void)
13883 // always start with reliable default values from static default config
13884 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13887 static void LoadSoundSettingsFromFilename(char *filename)
13889 SetupFileHash *setup_file_hash;
13891 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13894 // read (and overwrite with) values that may be specified in config file
13895 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13897 freeSetupFileHash(setup_file_hash);
13900 void LoadSoundSettings(void)
13902 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13904 InitSoundSettings_Static();
13906 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13908 // first look for special settings configured in level series config
13909 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13911 if (fileExists(filename_base))
13912 LoadSoundSettingsFromFilename(filename_base);
13915 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13917 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13918 LoadSoundSettingsFromFilename(filename_local);
13921 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13923 char *filename = getEditorSetupFilename();
13924 SetupFileList *setup_file_list, *list;
13925 SetupFileHash *element_hash;
13926 int num_unknown_tokens = 0;
13929 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13932 element_hash = newSetupFileHash();
13934 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13935 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13937 // determined size may be larger than needed (due to unknown elements)
13939 for (list = setup_file_list; list != NULL; list = list->next)
13942 // add space for up to 3 more elements for padding that may be needed
13943 *num_elements += 3;
13945 // free memory for old list of elements, if needed
13946 checked_free(*elements);
13948 // allocate memory for new list of elements
13949 *elements = checked_malloc(*num_elements * sizeof(int));
13952 for (list = setup_file_list; list != NULL; list = list->next)
13954 char *value = getHashEntry(element_hash, list->token);
13956 if (value == NULL) // try to find obsolete token mapping
13958 char *mapped_token = get_mapped_token(list->token);
13960 if (mapped_token != NULL)
13962 value = getHashEntry(element_hash, mapped_token);
13964 free(mapped_token);
13970 (*elements)[(*num_elements)++] = atoi(value);
13974 if (num_unknown_tokens == 0)
13977 Warn("unknown token(s) found in config file:");
13978 Warn("- config file: '%s'", filename);
13980 num_unknown_tokens++;
13983 Warn("- token: '%s'", list->token);
13987 if (num_unknown_tokens > 0)
13990 while (*num_elements % 4) // pad with empty elements, if needed
13991 (*elements)[(*num_elements)++] = EL_EMPTY;
13993 freeSetupFileList(setup_file_list);
13994 freeSetupFileHash(element_hash);
13997 for (i = 0; i < *num_elements; i++)
13998 Debug("editor", "element '%s' [%d]\n",
13999 element_info[(*elements)[i]].token_name, (*elements)[i]);
14003 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14006 SetupFileHash *setup_file_hash = NULL;
14007 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14008 char *filename_music, *filename_prefix, *filename_info;
14014 token_to_value_ptr[] =
14016 { "title_header", &tmp_music_file_info.title_header },
14017 { "artist_header", &tmp_music_file_info.artist_header },
14018 { "album_header", &tmp_music_file_info.album_header },
14019 { "year_header", &tmp_music_file_info.year_header },
14020 { "played_header", &tmp_music_file_info.played_header },
14022 { "title", &tmp_music_file_info.title },
14023 { "artist", &tmp_music_file_info.artist },
14024 { "album", &tmp_music_file_info.album },
14025 { "year", &tmp_music_file_info.year },
14026 { "played", &tmp_music_file_info.played },
14032 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14033 getCustomMusicFilename(basename));
14035 if (filename_music == NULL)
14038 // ---------- try to replace file extension ----------
14040 filename_prefix = getStringCopy(filename_music);
14041 if (strrchr(filename_prefix, '.') != NULL)
14042 *strrchr(filename_prefix, '.') = '\0';
14043 filename_info = getStringCat2(filename_prefix, ".txt");
14045 if (fileExists(filename_info))
14046 setup_file_hash = loadSetupFileHash(filename_info);
14048 free(filename_prefix);
14049 free(filename_info);
14051 if (setup_file_hash == NULL)
14053 // ---------- try to add file extension ----------
14055 filename_prefix = getStringCopy(filename_music);
14056 filename_info = getStringCat2(filename_prefix, ".txt");
14058 if (fileExists(filename_info))
14059 setup_file_hash = loadSetupFileHash(filename_info);
14061 free(filename_prefix);
14062 free(filename_info);
14065 if (setup_file_hash == NULL)
14068 // ---------- music file info found ----------
14070 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14072 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14074 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14076 *token_to_value_ptr[i].value_ptr =
14077 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14080 tmp_music_file_info.basename = getStringCopy(basename);
14081 tmp_music_file_info.music = music;
14082 tmp_music_file_info.is_sound = is_sound;
14084 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14085 *new_music_file_info = tmp_music_file_info;
14087 return new_music_file_info;
14090 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14092 return get_music_file_info_ext(basename, music, FALSE);
14095 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14097 return get_music_file_info_ext(basename, sound, TRUE);
14100 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14101 char *basename, boolean is_sound)
14103 for (; list != NULL; list = list->next)
14104 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14110 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14112 return music_info_listed_ext(list, basename, FALSE);
14115 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14117 return music_info_listed_ext(list, basename, TRUE);
14120 void LoadMusicInfo(void)
14122 int num_music_noconf = getMusicListSize_NoConf();
14123 int num_music = getMusicListSize();
14124 int num_sounds = getSoundListSize();
14125 struct FileInfo *music, *sound;
14126 struct MusicFileInfo *next, **new;
14130 while (music_file_info != NULL)
14132 next = music_file_info->next;
14134 checked_free(music_file_info->basename);
14136 checked_free(music_file_info->title_header);
14137 checked_free(music_file_info->artist_header);
14138 checked_free(music_file_info->album_header);
14139 checked_free(music_file_info->year_header);
14140 checked_free(music_file_info->played_header);
14142 checked_free(music_file_info->title);
14143 checked_free(music_file_info->artist);
14144 checked_free(music_file_info->album);
14145 checked_free(music_file_info->year);
14146 checked_free(music_file_info->played);
14148 free(music_file_info);
14150 music_file_info = next;
14153 new = &music_file_info;
14155 // get (configured or unconfigured) music file info for all levels
14156 for (i = leveldir_current->first_level;
14157 i <= leveldir_current->last_level; i++)
14161 if (levelset.music[i] != MUS_UNDEFINED)
14163 // get music file info for configured level music
14164 music_nr = levelset.music[i];
14166 else if (num_music_noconf > 0)
14168 // get music file info for unconfigured level music
14169 int level_pos = i - leveldir_current->first_level;
14171 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14178 char *basename = getMusicInfoEntryFilename(music_nr);
14180 if (basename == NULL)
14183 if (!music_info_listed(music_file_info, basename))
14185 *new = get_music_file_info(basename, music_nr);
14188 new = &(*new)->next;
14192 // get music file info for all remaining configured music files
14193 for (i = 0; i < num_music; i++)
14195 music = getMusicListEntry(i);
14197 if (music->filename == NULL)
14200 if (strEqual(music->filename, UNDEFINED_FILENAME))
14203 // a configured file may be not recognized as music
14204 if (!FileIsMusic(music->filename))
14207 if (!music_info_listed(music_file_info, music->filename))
14209 *new = get_music_file_info(music->filename, i);
14212 new = &(*new)->next;
14216 // get sound file info for all configured sound files
14217 for (i = 0; i < num_sounds; i++)
14219 sound = getSoundListEntry(i);
14221 if (sound->filename == NULL)
14224 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14227 // a configured file may be not recognized as sound
14228 if (!FileIsSound(sound->filename))
14231 if (!sound_info_listed(music_file_info, sound->filename))
14233 *new = get_sound_file_info(sound->filename, i);
14235 new = &(*new)->next;
14239 // add pointers to previous list nodes
14241 struct MusicFileInfo *node = music_file_info;
14243 while (node != NULL)
14246 node->next->prev = node;
14252 static void add_helpanim_entry(int element, int action, int direction,
14253 int delay, int *num_list_entries)
14255 struct HelpAnimInfo *new_list_entry;
14256 (*num_list_entries)++;
14259 checked_realloc(helpanim_info,
14260 *num_list_entries * sizeof(struct HelpAnimInfo));
14261 new_list_entry = &helpanim_info[*num_list_entries - 1];
14263 new_list_entry->element = element;
14264 new_list_entry->action = action;
14265 new_list_entry->direction = direction;
14266 new_list_entry->delay = delay;
14269 static void print_unknown_token(char *filename, char *token, int token_nr)
14274 Warn("unknown token(s) found in config file:");
14275 Warn("- config file: '%s'", filename);
14278 Warn("- token: '%s'", token);
14281 static void print_unknown_token_end(int token_nr)
14287 void LoadHelpAnimInfo(void)
14289 char *filename = getHelpAnimFilename();
14290 SetupFileList *setup_file_list = NULL, *list;
14291 SetupFileHash *element_hash, *action_hash, *direction_hash;
14292 int num_list_entries = 0;
14293 int num_unknown_tokens = 0;
14296 if (fileExists(filename))
14297 setup_file_list = loadSetupFileList(filename);
14299 if (setup_file_list == NULL)
14301 // use reliable default values from static configuration
14302 SetupFileList *insert_ptr;
14304 insert_ptr = setup_file_list =
14305 newSetupFileList(helpanim_config[0].token,
14306 helpanim_config[0].value);
14308 for (i = 1; helpanim_config[i].token; i++)
14309 insert_ptr = addListEntry(insert_ptr,
14310 helpanim_config[i].token,
14311 helpanim_config[i].value);
14314 element_hash = newSetupFileHash();
14315 action_hash = newSetupFileHash();
14316 direction_hash = newSetupFileHash();
14318 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14319 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14321 for (i = 0; i < NUM_ACTIONS; i++)
14322 setHashEntry(action_hash, element_action_info[i].suffix,
14323 i_to_a(element_action_info[i].value));
14325 // do not store direction index (bit) here, but direction value!
14326 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14327 setHashEntry(direction_hash, element_direction_info[i].suffix,
14328 i_to_a(1 << element_direction_info[i].value));
14330 for (list = setup_file_list; list != NULL; list = list->next)
14332 char *element_token, *action_token, *direction_token;
14333 char *element_value, *action_value, *direction_value;
14334 int delay = atoi(list->value);
14336 if (strEqual(list->token, "end"))
14338 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14343 /* first try to break element into element/action/direction parts;
14344 if this does not work, also accept combined "element[.act][.dir]"
14345 elements (like "dynamite.active"), which are unique elements */
14347 if (strchr(list->token, '.') == NULL) // token contains no '.'
14349 element_value = getHashEntry(element_hash, list->token);
14350 if (element_value != NULL) // element found
14351 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14352 &num_list_entries);
14355 // no further suffixes found -- this is not an element
14356 print_unknown_token(filename, list->token, num_unknown_tokens++);
14362 // token has format "<prefix>.<something>"
14364 action_token = strchr(list->token, '.'); // suffix may be action ...
14365 direction_token = action_token; // ... or direction
14367 element_token = getStringCopy(list->token);
14368 *strchr(element_token, '.') = '\0';
14370 element_value = getHashEntry(element_hash, element_token);
14372 if (element_value == NULL) // this is no element
14374 element_value = getHashEntry(element_hash, list->token);
14375 if (element_value != NULL) // combined element found
14376 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14377 &num_list_entries);
14379 print_unknown_token(filename, list->token, num_unknown_tokens++);
14381 free(element_token);
14386 action_value = getHashEntry(action_hash, action_token);
14388 if (action_value != NULL) // action found
14390 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14391 &num_list_entries);
14393 free(element_token);
14398 direction_value = getHashEntry(direction_hash, direction_token);
14400 if (direction_value != NULL) // direction found
14402 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14403 &num_list_entries);
14405 free(element_token);
14410 if (strchr(action_token + 1, '.') == NULL)
14412 // no further suffixes found -- this is not an action nor direction
14414 element_value = getHashEntry(element_hash, list->token);
14415 if (element_value != NULL) // combined element found
14416 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14417 &num_list_entries);
14419 print_unknown_token(filename, list->token, num_unknown_tokens++);
14421 free(element_token);
14426 // token has format "<prefix>.<suffix>.<something>"
14428 direction_token = strchr(action_token + 1, '.');
14430 action_token = getStringCopy(action_token);
14431 *strchr(action_token + 1, '.') = '\0';
14433 action_value = getHashEntry(action_hash, action_token);
14435 if (action_value == NULL) // this is no action
14437 element_value = getHashEntry(element_hash, list->token);
14438 if (element_value != NULL) // combined element found
14439 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14440 &num_list_entries);
14442 print_unknown_token(filename, list->token, num_unknown_tokens++);
14444 free(element_token);
14445 free(action_token);
14450 direction_value = getHashEntry(direction_hash, direction_token);
14452 if (direction_value != NULL) // direction found
14454 add_helpanim_entry(atoi(element_value), atoi(action_value),
14455 atoi(direction_value), delay, &num_list_entries);
14457 free(element_token);
14458 free(action_token);
14463 // this is no direction
14465 element_value = getHashEntry(element_hash, list->token);
14466 if (element_value != NULL) // combined element found
14467 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14468 &num_list_entries);
14470 print_unknown_token(filename, list->token, num_unknown_tokens++);
14472 free(element_token);
14473 free(action_token);
14476 print_unknown_token_end(num_unknown_tokens);
14478 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14479 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14481 freeSetupFileList(setup_file_list);
14482 freeSetupFileHash(element_hash);
14483 freeSetupFileHash(action_hash);
14484 freeSetupFileHash(direction_hash);
14487 for (i = 0; i < num_list_entries; i++)
14488 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14489 EL_NAME(helpanim_info[i].element),
14490 helpanim_info[i].element,
14491 helpanim_info[i].action,
14492 helpanim_info[i].direction,
14493 helpanim_info[i].delay);
14497 void LoadHelpTextInfo(void)
14499 char *filename = getHelpTextFilename();
14502 if (helptext_info != NULL)
14504 freeSetupFileHash(helptext_info);
14505 helptext_info = NULL;
14508 if (fileExists(filename))
14509 helptext_info = loadSetupFileHash(filename);
14511 if (helptext_info == NULL)
14513 // use reliable default values from static configuration
14514 helptext_info = newSetupFileHash();
14516 for (i = 0; helptext_config[i].token; i++)
14517 setHashEntry(helptext_info,
14518 helptext_config[i].token,
14519 helptext_config[i].value);
14523 BEGIN_HASH_ITERATION(helptext_info, itr)
14525 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14526 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14528 END_HASH_ITERATION(hash, itr)
14533 // ----------------------------------------------------------------------------
14535 // ----------------------------------------------------------------------------
14537 #define MAX_NUM_CONVERT_LEVELS 1000
14539 void ConvertLevels(void)
14541 static LevelDirTree *convert_leveldir = NULL;
14542 static int convert_level_nr = -1;
14543 static int num_levels_handled = 0;
14544 static int num_levels_converted = 0;
14545 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14548 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14549 global.convert_leveldir);
14551 if (convert_leveldir == NULL)
14552 Fail("no such level identifier: '%s'", global.convert_leveldir);
14554 leveldir_current = convert_leveldir;
14556 if (global.convert_level_nr != -1)
14558 convert_leveldir->first_level = global.convert_level_nr;
14559 convert_leveldir->last_level = global.convert_level_nr;
14562 convert_level_nr = convert_leveldir->first_level;
14564 PrintLine("=", 79);
14565 Print("Converting levels\n");
14566 PrintLine("-", 79);
14567 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14568 Print("Level series name: '%s'\n", convert_leveldir->name);
14569 Print("Level series author: '%s'\n", convert_leveldir->author);
14570 Print("Number of levels: %d\n", convert_leveldir->levels);
14571 PrintLine("=", 79);
14574 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14575 levels_failed[i] = FALSE;
14577 while (convert_level_nr <= convert_leveldir->last_level)
14579 char *level_filename;
14582 level_nr = convert_level_nr++;
14584 Print("Level %03d: ", level_nr);
14586 LoadLevel(level_nr);
14587 if (level.no_level_file || level.no_valid_file)
14589 Print("(no level)\n");
14593 Print("converting level ... ");
14596 // special case: conversion of some EMC levels as requested by ACME
14597 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14600 level_filename = getDefaultLevelFilename(level_nr);
14601 new_level = !fileExists(level_filename);
14605 SaveLevel(level_nr);
14607 num_levels_converted++;
14609 Print("converted.\n");
14613 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14614 levels_failed[level_nr] = TRUE;
14616 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14619 num_levels_handled++;
14623 PrintLine("=", 79);
14624 Print("Number of levels handled: %d\n", num_levels_handled);
14625 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14626 (num_levels_handled ?
14627 num_levels_converted * 100 / num_levels_handled : 0));
14628 PrintLine("-", 79);
14629 Print("Summary (for automatic parsing by scripts):\n");
14630 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14631 convert_leveldir->identifier, num_levels_converted,
14632 num_levels_handled,
14633 (num_levels_handled ?
14634 num_levels_converted * 100 / num_levels_handled : 0));
14636 if (num_levels_handled != num_levels_converted)
14638 Print(", FAILED:");
14639 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14640 if (levels_failed[i])
14645 PrintLine("=", 79);
14647 CloseAllAndExit(0);
14651 // ----------------------------------------------------------------------------
14652 // create and save images for use in level sketches (raw BMP format)
14653 // ----------------------------------------------------------------------------
14655 void CreateLevelSketchImages(void)
14661 InitElementPropertiesGfxElement();
14663 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14664 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14666 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14668 int element = getMappedElement(i);
14669 char basename1[16];
14670 char basename2[16];
14674 sprintf(basename1, "%04d.bmp", i);
14675 sprintf(basename2, "%04ds.bmp", i);
14677 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14678 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14680 DrawSizedElement(0, 0, element, TILESIZE);
14681 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14683 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14684 Fail("cannot save level sketch image file '%s'", filename1);
14686 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14687 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14689 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14690 Fail("cannot save level sketch image file '%s'", filename2);
14695 // create corresponding SQL statements (for normal and small images)
14698 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14699 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14702 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14703 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14705 // optional: create content for forum level sketch demonstration post
14707 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14710 FreeBitmap(bitmap1);
14711 FreeBitmap(bitmap2);
14714 fprintf(stderr, "\n");
14716 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14718 CloseAllAndExit(0);
14722 // ----------------------------------------------------------------------------
14723 // create and save images for element collecting animations (raw BMP format)
14724 // ----------------------------------------------------------------------------
14726 static boolean createCollectImage(int element)
14728 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14731 void CreateCollectElementImages(void)
14735 int anim_frames = num_steps - 1;
14736 int tile_size = TILESIZE;
14737 int anim_width = tile_size * anim_frames;
14738 int anim_height = tile_size;
14739 int num_collect_images = 0;
14740 int pos_collect_images = 0;
14742 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14743 if (createCollectImage(i))
14744 num_collect_images++;
14746 Info("Creating %d element collecting animation images ...",
14747 num_collect_images);
14749 int dst_width = anim_width * 2;
14750 int dst_height = anim_height * num_collect_images / 2;
14751 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14752 char *basename_bmp = "RocksCollect.bmp";
14753 char *basename_png = "RocksCollect.png";
14754 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14755 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14756 int len_filename_bmp = strlen(filename_bmp);
14757 int len_filename_png = strlen(filename_png);
14758 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14759 char cmd_convert[max_command_len];
14761 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14765 // force using RGBA surface for destination bitmap
14766 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14767 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14769 dst_bitmap->surface =
14770 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14772 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14774 if (!createCollectImage(i))
14777 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14778 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14779 int graphic = el2img(i);
14780 char *token_name = element_info[i].token_name;
14781 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14782 Bitmap *src_bitmap;
14785 Info("- creating collecting image for '%s' ...", token_name);
14787 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14789 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14790 tile_size, tile_size, 0, 0);
14792 // force using RGBA surface for temporary bitmap (using transparent black)
14793 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14794 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14796 tmp_bitmap->surface =
14797 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14799 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14801 for (j = 0; j < anim_frames; j++)
14803 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14804 int frame_size = frame_size_final * num_steps;
14805 int offset = (tile_size - frame_size_final) / 2;
14806 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14808 while (frame_size > frame_size_final)
14812 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14814 FreeBitmap(frame_bitmap);
14816 frame_bitmap = half_bitmap;
14819 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14820 frame_size_final, frame_size_final,
14821 dst_x + j * tile_size + offset, dst_y + offset);
14823 FreeBitmap(frame_bitmap);
14826 tmp_bitmap->surface_masked = NULL;
14828 FreeBitmap(tmp_bitmap);
14830 pos_collect_images++;
14833 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14834 Fail("cannot save element collecting image file '%s'", filename_bmp);
14836 FreeBitmap(dst_bitmap);
14838 Info("Converting image file from BMP to PNG ...");
14840 if (system(cmd_convert) != 0)
14841 Fail("converting image file failed");
14843 unlink(filename_bmp);
14847 CloseAllAndExit(0);
14851 // ----------------------------------------------------------------------------
14852 // create and save images for custom and group elements (raw BMP format)
14853 // ----------------------------------------------------------------------------
14855 void CreateCustomElementImages(char *directory)
14857 char *src_basename = "RocksCE-template.ilbm";
14858 char *dst_basename = "RocksCE.bmp";
14859 char *src_filename = getPath2(directory, src_basename);
14860 char *dst_filename = getPath2(directory, dst_basename);
14861 Bitmap *src_bitmap;
14863 int yoffset_ce = 0;
14864 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14867 InitVideoDefaults();
14869 ReCreateBitmap(&backbuffer, video.width, video.height);
14871 src_bitmap = LoadImage(src_filename);
14873 bitmap = CreateBitmap(TILEX * 16 * 2,
14874 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14877 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14884 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14885 TILEX * x, TILEY * y + yoffset_ce);
14887 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14889 TILEX * x + TILEX * 16,
14890 TILEY * y + yoffset_ce);
14892 for (j = 2; j >= 0; j--)
14896 BlitBitmap(src_bitmap, bitmap,
14897 TILEX + c * 7, 0, 6, 10,
14898 TILEX * x + 6 + j * 7,
14899 TILEY * y + 11 + yoffset_ce);
14901 BlitBitmap(src_bitmap, bitmap,
14902 TILEX + c * 8, TILEY, 6, 10,
14903 TILEX * 16 + TILEX * x + 6 + j * 8,
14904 TILEY * y + 10 + yoffset_ce);
14910 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14917 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14918 TILEX * x, TILEY * y + yoffset_ge);
14920 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14922 TILEX * x + TILEX * 16,
14923 TILEY * y + yoffset_ge);
14925 for (j = 1; j >= 0; j--)
14929 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14930 TILEX * x + 6 + j * 10,
14931 TILEY * y + 11 + yoffset_ge);
14933 BlitBitmap(src_bitmap, bitmap,
14934 TILEX + c * 8, TILEY + 12, 6, 10,
14935 TILEX * 16 + TILEX * x + 10 + j * 8,
14936 TILEY * y + 10 + yoffset_ge);
14942 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14943 Fail("cannot save CE graphics file '%s'", dst_filename);
14945 FreeBitmap(bitmap);
14947 CloseAllAndExit(0);