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_DIAMOND_GROWING_5, -1,
708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
709 &li.bd_diamond_birth_turns_to, EL_BD_DIAMOND
713 EL_BD_BOMB_EXPLODING_4, -1,
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_bomb_explosion_turns_to, EL_BD_WALL
719 EL_BD_NITRO_PACK_EXPLODING_4, -1,
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.bd_nitro_explosion_turns_to, EL_EMPTY
725 EL_BD_EXPLODING_5, -1,
726 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
727 &li.bd_explosion_turns_to, EL_EMPTY
731 EL_BD_MAGIC_WALL, -1,
732 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
733 &li.bd_magic_wall_wait_hatching, FALSE
736 EL_BD_MAGIC_WALL, -1,
737 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
738 &li.bd_magic_wall_stops_amoeba, TRUE
741 EL_BD_MAGIC_WALL, -1,
742 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
743 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
746 EL_BD_MAGIC_WALL, -1,
747 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
748 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
751 EL_BD_MAGIC_WALL, -1,
752 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
753 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
756 EL_BD_MAGIC_WALL, -1,
757 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
758 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
761 EL_BD_MAGIC_WALL, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
763 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
766 EL_BD_MAGIC_WALL, -1,
767 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
768 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
771 EL_BD_MAGIC_WALL, -1,
772 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
773 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
778 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
779 &li.bd_clock_extra_time, 30
783 EL_BD_VOODOO_DOLL, -1,
784 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
785 &li.bd_voodoo_collects_diamonds, FALSE
788 EL_BD_VOODOO_DOLL, -1,
789 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
790 &li.bd_voodoo_hurt_kills_player, FALSE
793 EL_BD_VOODOO_DOLL, -1,
794 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
795 &li.bd_voodoo_dies_by_rock, FALSE
798 EL_BD_VOODOO_DOLL, -1,
799 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
800 &li.bd_voodoo_vanish_by_explosion, TRUE
803 EL_BD_VOODOO_DOLL, -1,
804 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
805 &li.bd_voodoo_penalty_time, 30
810 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
811 &li.bd_slime_is_predictable, TRUE
815 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
816 &li.bd_slime_permeability_rate, 100
820 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
821 &li.bd_slime_permeability_bits_c64, 0
825 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
826 &li.bd_slime_random_seed_c64, -1
830 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
831 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
835 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
836 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
840 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
841 &li.bd_slime_eats_element_2, EL_BD_ROCK
845 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
846 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
850 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
851 &li.bd_slime_eats_element_3, EL_BD_NUT
855 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
856 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
861 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
862 &li.bd_acid_eats_element, EL_BD_SAND
866 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
867 &li.bd_acid_spread_rate, 3
871 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
872 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
877 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
878 &li.bd_biter_move_delay, 0
882 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
883 &li.bd_biter_eats_element, EL_BD_DIAMOND
888 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
889 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
893 EL_BD_EXPANDABLE_WALL_ANY, -1,
894 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
895 &li.bd_change_expanding_wall, FALSE
898 EL_BD_EXPANDABLE_WALL_ANY, -1,
899 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
900 &li.bd_expanding_wall_looks_like, EL_BD_WALL
904 EL_BD_REPLICATOR, -1,
905 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
906 &li.bd_replicators_active, TRUE
909 EL_BD_REPLICATOR, -1,
910 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
911 &li.bd_replicator_create_delay, 4
915 EL_BD_CONVEYOR_LEFT, -1,
916 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
917 &li.bd_conveyor_belts_active, TRUE
920 EL_BD_CONVEYOR_LEFT, -1,
921 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
922 &li.bd_conveyor_belts_changed, FALSE
927 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
928 &li.bd_water_cannot_flow_down, FALSE
933 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
934 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
938 EL_BD_PNEUMATIC_HAMMER, -1,
939 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
940 &li.bd_hammer_walls_break_delay, 5
943 EL_BD_PNEUMATIC_HAMMER, -1,
944 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
945 &li.bd_hammer_walls_reappear, FALSE
948 EL_BD_PNEUMATIC_HAMMER, -1,
949 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
950 &li.bd_hammer_walls_reappear_delay, 100
955 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
956 &li.bd_num_skeletons_needed_for_pot, 5
960 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
961 &li.bd_skeleton_worth_num_diamonds, 0
965 EL_BD_CREATURE_SWITCH, -1,
966 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
967 &li.bd_creatures_start_backwards, FALSE
970 EL_BD_CREATURE_SWITCH, -1,
971 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
972 &li.bd_creatures_turn_on_hatching, FALSE
975 EL_BD_CREATURE_SWITCH, -1,
976 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
977 &li.bd_creatures_auto_turn_delay, 0
981 EL_BD_GRAVITY_SWITCH, -1,
982 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
983 &li.bd_gravity_direction, GD_MV_DOWN
986 EL_BD_GRAVITY_SWITCH, -1,
987 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
988 &li.bd_gravity_switch_active, FALSE
991 EL_BD_GRAVITY_SWITCH, -1,
992 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
993 &li.bd_gravity_switch_delay, 10
996 EL_BD_GRAVITY_SWITCH, -1,
997 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
998 &li.bd_gravity_affects_all, TRUE
1001 // (the following values are related to various game elements)
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1006 &li.score[SC_EMERALD], 10
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1012 &li.score[SC_DIAMOND], 10
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1018 &li.score[SC_BUG], 10
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1024 &li.score[SC_SPACESHIP], 10
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1030 &li.score[SC_PACMAN], 10
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1036 &li.score[SC_NUT], 10
1041 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1042 &li.score[SC_DYNAMITE], 10
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1048 &li.score[SC_KEY], 10
1053 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1054 &li.score[SC_PEARL], 10
1059 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1060 &li.score[SC_CRYSTAL], 10
1063 // (amoeba values used by R'n'D game engine only)
1066 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1067 &li.amoeba_content, EL_DIAMOND
1071 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1072 &li.amoeba_speed, 10
1076 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1077 &li.grow_into_diggable, TRUE
1079 // (amoeba values used by BD game engine only)
1082 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1083 &li.bd_amoeba_wait_for_hatching, FALSE
1087 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1088 &li.bd_amoeba_start_immediately, TRUE
1092 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1093 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1098 &li.bd_amoeba_threshold_too_big, 200
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1103 &li.bd_amoeba_slow_growth_time, 200
1107 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1108 &li.bd_amoeba_slow_growth_rate, 3
1112 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1113 &li.bd_amoeba_fast_growth_rate, 25
1117 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1118 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1122 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1123 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1128 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1129 &li.bd_amoeba_2_threshold_too_big, 200
1133 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1134 &li.bd_amoeba_2_slow_growth_time, 200
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1139 &li.bd_amoeba_2_slow_growth_rate, 3
1143 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1144 &li.bd_amoeba_2_fast_growth_rate, 25
1148 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1149 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1153 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1154 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1158 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1159 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1163 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1164 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1169 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1170 &li.yamyam_content, EL_ROCK, NULL,
1171 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1175 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1176 &li.score[SC_YAMYAM], 10
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1182 &li.score[SC_ROBOT], 10
1186 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1192 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1198 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1199 &li.time_magic_wall, 10
1203 EL_GAME_OF_LIFE, -1,
1204 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1205 &li.game_of_life[0], 2
1208 EL_GAME_OF_LIFE, -1,
1209 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1210 &li.game_of_life[1], 3
1213 EL_GAME_OF_LIFE, -1,
1214 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1215 &li.game_of_life[2], 3
1218 EL_GAME_OF_LIFE, -1,
1219 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1220 &li.game_of_life[3], 3
1223 EL_GAME_OF_LIFE, -1,
1224 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1225 &li.use_life_bugs, FALSE
1230 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1235 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1240 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1245 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1250 EL_TIMEGATE_SWITCH, -1,
1251 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1252 &li.time_timegate, 10
1256 EL_LIGHT_SWITCH_ACTIVE, -1,
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1262 EL_SHIELD_NORMAL, -1,
1263 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1264 &li.shield_normal_time, 10
1267 EL_SHIELD_NORMAL, -1,
1268 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1269 &li.score[SC_SHIELD], 10
1273 EL_SHIELD_DEADLY, -1,
1274 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1275 &li.shield_deadly_time, 10
1278 EL_SHIELD_DEADLY, -1,
1279 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1280 &li.score[SC_SHIELD], 10
1285 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1290 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1291 &li.extra_time_score, 10
1295 EL_TIME_ORB_FULL, -1,
1296 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1297 &li.time_orb_time, 10
1300 EL_TIME_ORB_FULL, -1,
1301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1302 &li.use_time_orb_bug, FALSE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1308 &li.use_spring_bug, FALSE
1313 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1314 &li.android_move_time, 10
1318 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1319 &li.android_clone_time, 10
1322 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1323 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1324 &li.android_clone_element[0], EL_EMPTY, NULL,
1325 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1329 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1330 &li.android_clone_element[0], EL_EMPTY, NULL,
1331 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1336 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1337 &li.lenses_score, 10
1341 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1346 EL_EMC_MAGNIFIER, -1,
1347 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1348 &li.magnify_score, 10
1351 EL_EMC_MAGNIFIER, -1,
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1353 &li.magnify_time, 10
1357 EL_EMC_MAGIC_BALL, -1,
1358 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1362 EL_EMC_MAGIC_BALL, -1,
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1364 &li.ball_random, FALSE
1367 EL_EMC_MAGIC_BALL, -1,
1368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1369 &li.ball_active_initial, FALSE
1372 EL_EMC_MAGIC_BALL, -1,
1373 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1374 &li.ball_content, EL_EMPTY, NULL,
1375 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1379 EL_SOKOBAN_FIELD_EMPTY, -1,
1380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1381 &li.sb_fields_needed, TRUE
1385 EL_SOKOBAN_OBJECT, -1,
1386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1387 &li.sb_objects_needed, TRUE
1392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1393 &li.mm_laser_red, FALSE
1397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1398 &li.mm_laser_green, FALSE
1402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1403 &li.mm_laser_blue, TRUE
1408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1409 &li.df_laser_red, TRUE
1413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1414 &li.df_laser_green, TRUE
1418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1419 &li.df_laser_blue, FALSE
1423 EL_MM_FUSE_ACTIVE, -1,
1424 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1425 &li.mm_time_fuse, 25
1429 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1430 &li.mm_time_bomb, 75
1434 EL_MM_GRAY_BALL, -1,
1435 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1436 &li.mm_time_ball, 75
1439 EL_MM_GRAY_BALL, -1,
1440 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1441 &li.mm_ball_choice_mode, ANIM_RANDOM
1444 EL_MM_GRAY_BALL, -1,
1445 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1446 &li.mm_ball_content, EL_EMPTY, NULL,
1447 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1450 EL_MM_GRAY_BALL, -1,
1451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1452 &li.rotate_mm_ball_content, TRUE
1455 EL_MM_GRAY_BALL, -1,
1456 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1457 &li.explode_mm_ball, FALSE
1461 EL_MM_STEEL_BLOCK, -1,
1462 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1463 &li.mm_time_block, 75
1466 EL_MM_LIGHTBALL, -1,
1467 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1468 &li.score[SC_ELEM_BONUS], 10
1478 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1482 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1483 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1487 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1488 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1494 &xx_envelope.autowrap, FALSE
1498 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1499 &xx_envelope.centered, FALSE
1504 TYPE_STRING, CONF_VALUE_BYTES(1),
1505 &xx_envelope.text, -1, NULL,
1506 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1507 &xx_default_string_empty[0]
1517 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1521 TYPE_STRING, CONF_VALUE_BYTES(1),
1522 &xx_ei.description[0], -1,
1523 &yy_ei.description[0],
1524 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1525 &xx_default_description[0]
1530 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1531 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1532 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1534 #if ENABLE_RESERVED_CODE
1535 // (reserved for later use)
1538 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1539 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1540 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1547 &xx_ei.use_gfx_element, FALSE,
1548 &yy_ei.use_gfx_element
1552 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1553 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1554 &yy_ei.gfx_element_initial
1559 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1560 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1561 &yy_ei.access_direction
1566 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1567 &xx_ei.collect_score_initial, 10,
1568 &yy_ei.collect_score_initial
1572 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1573 &xx_ei.collect_count_initial, 1,
1574 &yy_ei.collect_count_initial
1579 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1580 &xx_ei.ce_value_fixed_initial, 0,
1581 &yy_ei.ce_value_fixed_initial
1585 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1586 &xx_ei.ce_value_random_initial, 0,
1587 &yy_ei.ce_value_random_initial
1591 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1592 &xx_ei.use_last_ce_value, FALSE,
1593 &yy_ei.use_last_ce_value
1598 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1599 &xx_ei.push_delay_fixed, 8,
1600 &yy_ei.push_delay_fixed
1604 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1605 &xx_ei.push_delay_random, 8,
1606 &yy_ei.push_delay_random
1610 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1611 &xx_ei.drop_delay_fixed, 0,
1612 &yy_ei.drop_delay_fixed
1616 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1617 &xx_ei.drop_delay_random, 0,
1618 &yy_ei.drop_delay_random
1622 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1623 &xx_ei.move_delay_fixed, 0,
1624 &yy_ei.move_delay_fixed
1628 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1629 &xx_ei.move_delay_random, 0,
1630 &yy_ei.move_delay_random
1634 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1635 &xx_ei.step_delay_fixed, 0,
1636 &yy_ei.step_delay_fixed
1640 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1641 &xx_ei.step_delay_random, 0,
1642 &yy_ei.step_delay_random
1647 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1648 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1653 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1654 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1655 &yy_ei.move_direction_initial
1659 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1660 &xx_ei.move_stepsize, TILEX / 8,
1661 &yy_ei.move_stepsize
1666 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1667 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1668 &yy_ei.move_enter_element
1672 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1673 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1674 &yy_ei.move_leave_element
1678 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1679 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1680 &yy_ei.move_leave_type
1685 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1686 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1687 &yy_ei.slippery_type
1692 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1693 &xx_ei.explosion_type, EXPLODES_3X3,
1694 &yy_ei.explosion_type
1698 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1699 &xx_ei.explosion_delay, 16,
1700 &yy_ei.explosion_delay
1704 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1705 &xx_ei.ignition_delay, 8,
1706 &yy_ei.ignition_delay
1711 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1712 &xx_ei.content, EL_EMPTY_SPACE,
1714 &xx_num_contents, 1, 1
1717 // ---------- "num_change_pages" must be the last entry ---------------------
1720 -1, SAVE_CONF_ALWAYS,
1721 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1722 &xx_ei.num_change_pages, 1,
1723 &yy_ei.num_change_pages
1734 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1736 // ---------- "current_change_page" must be the first entry -----------------
1739 -1, SAVE_CONF_ALWAYS,
1740 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1741 &xx_current_change_page, -1
1744 // ---------- (the remaining entries can be in any order) -------------------
1748 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1749 &xx_change.can_change, FALSE
1754 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1755 &xx_event_bits[0], 0
1759 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1760 &xx_event_bits[1], 0
1765 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1766 &xx_change.trigger_player, CH_PLAYER_ANY
1770 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1771 &xx_change.trigger_side, CH_SIDE_ANY
1775 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1776 &xx_change.trigger_page, CH_PAGE_ANY
1781 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1782 &xx_change.target_element, EL_EMPTY_SPACE
1787 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1788 &xx_change.delay_fixed, 0
1792 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1793 &xx_change.delay_random, 0
1797 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1798 &xx_change.delay_frames, FRAMES_PER_SECOND
1803 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1804 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1809 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1810 &xx_change.explode, FALSE
1814 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1815 &xx_change.use_target_content, FALSE
1819 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1820 &xx_change.only_if_complete, FALSE
1824 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1825 &xx_change.use_random_replace, FALSE
1829 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1830 &xx_change.random_percentage, 100
1834 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1835 &xx_change.replace_when, CP_WHEN_EMPTY
1840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1841 &xx_change.has_action, FALSE
1845 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1846 &xx_change.action_type, CA_NO_ACTION
1850 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1851 &xx_change.action_mode, CA_MODE_UNDEFINED
1855 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1856 &xx_change.action_arg, CA_ARG_UNDEFINED
1861 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1862 &xx_change.action_element, EL_EMPTY_SPACE
1867 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1868 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1869 &xx_num_contents, 1, 1
1879 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1883 TYPE_STRING, CONF_VALUE_BYTES(1),
1884 &xx_ei.description[0], -1, NULL,
1885 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1886 &xx_default_description[0]
1891 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1892 &xx_ei.use_gfx_element, FALSE
1896 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1897 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1902 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1903 &xx_group.choice_mode, ANIM_RANDOM
1908 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1909 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1910 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1920 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1924 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1925 &xx_ei.use_gfx_element, FALSE
1929 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1930 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1940 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1944 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1945 &li.block_snap_field, TRUE
1949 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1950 &li.continuous_snapping, TRUE
1954 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1955 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1959 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1960 &li.use_start_element[0], FALSE
1964 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1965 &li.start_element[0], EL_PLAYER_1
1969 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1970 &li.use_artwork_element[0], FALSE
1974 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1975 &li.artwork_element[0], EL_PLAYER_1
1979 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1980 &li.use_explosion_element[0], FALSE
1984 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1985 &li.explosion_element[0], EL_PLAYER_1
2000 filetype_id_list[] =
2002 { LEVEL_FILE_TYPE_RND, "RND" },
2003 { LEVEL_FILE_TYPE_BD, "BD" },
2004 { LEVEL_FILE_TYPE_EM, "EM" },
2005 { LEVEL_FILE_TYPE_SP, "SP" },
2006 { LEVEL_FILE_TYPE_DX, "DX" },
2007 { LEVEL_FILE_TYPE_SB, "SB" },
2008 { LEVEL_FILE_TYPE_DC, "DC" },
2009 { LEVEL_FILE_TYPE_MM, "MM" },
2010 { LEVEL_FILE_TYPE_MM, "DF" },
2015 // ============================================================================
2016 // level file functions
2017 // ============================================================================
2019 static boolean check_special_flags(char *flag)
2021 if (strEqual(options.special_flags, flag) ||
2022 strEqual(leveldir_current->special_flags, flag))
2028 static struct DateInfo getCurrentDate(void)
2030 time_t epoch_seconds = time(NULL);
2031 struct tm *now = localtime(&epoch_seconds);
2032 struct DateInfo date;
2034 date.year = now->tm_year + 1900;
2035 date.month = now->tm_mon + 1;
2036 date.day = now->tm_mday;
2038 date.src = DATE_SRC_CLOCK;
2043 static void resetEventFlags(struct ElementChangeInfo *change)
2047 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2048 change->has_event[i] = FALSE;
2051 static void resetEventBits(void)
2055 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2056 xx_event_bits[i] = 0;
2059 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2063 /* important: only change event flag if corresponding event bit is set
2064 (this is because all xx_event_bits[] values are loaded separately,
2065 and all xx_event_bits[] values are set back to zero before loading
2066 another value xx_event_bits[x] (each value representing 32 flags)) */
2068 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2069 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2070 change->has_event[i] = TRUE;
2073 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2077 /* in contrast to the above function setEventFlagsFromEventBits(), it
2078 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2079 depending on the corresponding change->has_event[i] values here, as
2080 all xx_event_bits[] values are reset in resetEventBits() before */
2082 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2083 if (change->has_event[i])
2084 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2087 static char *getDefaultElementDescription(struct ElementInfo *ei)
2089 static char description[MAX_ELEMENT_NAME_LEN + 1];
2090 char *default_description = (ei->custom_description != NULL ?
2091 ei->custom_description :
2092 ei->editor_description);
2095 // always start with reliable default values
2096 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2097 description[i] = '\0';
2099 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2100 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2102 return &description[0];
2105 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2107 char *default_description = getDefaultElementDescription(ei);
2110 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2111 ei->description[i] = default_description[i];
2114 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2118 for (i = 0; conf[i].data_type != -1; i++)
2120 int default_value = conf[i].default_value;
2121 int data_type = conf[i].data_type;
2122 int conf_type = conf[i].conf_type;
2123 int byte_mask = conf_type & CONF_MASK_BYTES;
2125 if (byte_mask == CONF_MASK_MULTI_BYTES)
2127 int default_num_entities = conf[i].default_num_entities;
2128 int max_num_entities = conf[i].max_num_entities;
2130 *(int *)(conf[i].num_entities) = default_num_entities;
2132 if (data_type == TYPE_STRING)
2134 char *default_string = conf[i].default_string;
2135 char *string = (char *)(conf[i].value);
2137 strncpy(string, default_string, max_num_entities);
2139 else if (data_type == TYPE_ELEMENT_LIST)
2141 int *element_array = (int *)(conf[i].value);
2144 for (j = 0; j < max_num_entities; j++)
2145 element_array[j] = default_value;
2147 else if (data_type == TYPE_CONTENT_LIST)
2149 struct Content *content = (struct Content *)(conf[i].value);
2152 for (c = 0; c < max_num_entities; c++)
2153 for (y = 0; y < 3; y++)
2154 for (x = 0; x < 3; x++)
2155 content[c].e[x][y] = default_value;
2158 else // constant size configuration data (1, 2 or 4 bytes)
2160 if (data_type == TYPE_BOOLEAN)
2161 *(boolean *)(conf[i].value) = default_value;
2163 *(int *) (conf[i].value) = default_value;
2168 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2172 for (i = 0; conf[i].data_type != -1; i++)
2174 int data_type = conf[i].data_type;
2175 int conf_type = conf[i].conf_type;
2176 int byte_mask = conf_type & CONF_MASK_BYTES;
2178 if (byte_mask == CONF_MASK_MULTI_BYTES)
2180 int max_num_entities = conf[i].max_num_entities;
2182 if (data_type == TYPE_STRING)
2184 char *string = (char *)(conf[i].value);
2185 char *string_copy = (char *)(conf[i].value_copy);
2187 strncpy(string_copy, string, max_num_entities);
2189 else if (data_type == TYPE_ELEMENT_LIST)
2191 int *element_array = (int *)(conf[i].value);
2192 int *element_array_copy = (int *)(conf[i].value_copy);
2195 for (j = 0; j < max_num_entities; j++)
2196 element_array_copy[j] = element_array[j];
2198 else if (data_type == TYPE_CONTENT_LIST)
2200 struct Content *content = (struct Content *)(conf[i].value);
2201 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2204 for (c = 0; c < max_num_entities; c++)
2205 for (y = 0; y < 3; y++)
2206 for (x = 0; x < 3; x++)
2207 content_copy[c].e[x][y] = content[c].e[x][y];
2210 else // constant size configuration data (1, 2 or 4 bytes)
2212 if (data_type == TYPE_BOOLEAN)
2213 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2215 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2220 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2224 xx_ei = *ei_from; // copy element data into temporary buffer
2225 yy_ei = *ei_to; // copy element data into temporary buffer
2227 copyConfigFromConfigList(chunk_config_CUSX_base);
2232 // ---------- reinitialize and copy change pages ----------
2234 ei_to->num_change_pages = ei_from->num_change_pages;
2235 ei_to->current_change_page = ei_from->current_change_page;
2237 setElementChangePages(ei_to, ei_to->num_change_pages);
2239 for (i = 0; i < ei_to->num_change_pages; i++)
2240 ei_to->change_page[i] = ei_from->change_page[i];
2242 // ---------- copy group element info ----------
2243 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2244 *ei_to->group = *ei_from->group;
2246 // mark this custom element as modified
2247 ei_to->modified_settings = TRUE;
2250 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2252 int change_page_size = sizeof(struct ElementChangeInfo);
2254 ei->num_change_pages = MAX(1, change_pages);
2257 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2259 if (ei->current_change_page >= ei->num_change_pages)
2260 ei->current_change_page = ei->num_change_pages - 1;
2262 ei->change = &ei->change_page[ei->current_change_page];
2265 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2267 xx_change = *change; // copy change data into temporary buffer
2269 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2271 *change = xx_change;
2273 resetEventFlags(change);
2275 change->direct_action = 0;
2276 change->other_action = 0;
2278 change->pre_change_function = NULL;
2279 change->change_function = NULL;
2280 change->post_change_function = NULL;
2283 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2287 li = *level; // copy level data into temporary buffer
2288 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2289 *level = li; // copy temporary buffer back to level data
2291 setLevelInfoToDefaults_BD();
2292 setLevelInfoToDefaults_EM();
2293 setLevelInfoToDefaults_SP();
2294 setLevelInfoToDefaults_MM();
2296 level->native_bd_level = &native_bd_level;
2297 level->native_em_level = &native_em_level;
2298 level->native_sp_level = &native_sp_level;
2299 level->native_mm_level = &native_mm_level;
2301 level->file_version = FILE_VERSION_ACTUAL;
2302 level->game_version = GAME_VERSION_ACTUAL;
2304 level->creation_date = getCurrentDate();
2306 level->encoding_16bit_field = TRUE;
2307 level->encoding_16bit_yamyam = TRUE;
2308 level->encoding_16bit_amoeba = TRUE;
2310 // clear level name and level author string buffers
2311 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2312 level->name[i] = '\0';
2313 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2314 level->author[i] = '\0';
2316 // set level name and level author to default values
2317 strcpy(level->name, NAMELESS_LEVEL_NAME);
2318 strcpy(level->author, ANONYMOUS_NAME);
2320 // set level playfield to playable default level with player and exit
2321 for (x = 0; x < MAX_LEV_FIELDX; x++)
2322 for (y = 0; y < MAX_LEV_FIELDY; y++)
2323 level->field[x][y] = EL_SAND;
2325 level->field[0][0] = EL_PLAYER_1;
2326 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2328 BorderElement = EL_STEELWALL;
2330 // detect custom elements when loading them
2331 level->file_has_custom_elements = FALSE;
2333 // set all bug compatibility flags to "false" => do not emulate this bug
2334 level->use_action_after_change_bug = FALSE;
2336 if (leveldir_current)
2338 // try to determine better author name than 'anonymous'
2339 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2341 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2342 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2346 switch (LEVELCLASS(leveldir_current))
2348 case LEVELCLASS_TUTORIAL:
2349 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2352 case LEVELCLASS_CONTRIB:
2353 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2354 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2357 case LEVELCLASS_PRIVATE:
2358 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2359 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2363 // keep default value
2370 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2372 static boolean clipboard_elements_initialized = FALSE;
2375 InitElementPropertiesStatic();
2377 li = *level; // copy level data into temporary buffer
2378 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2379 *level = li; // copy temporary buffer back to level data
2381 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2384 struct ElementInfo *ei = &element_info[element];
2386 if (element == EL_MM_GRAY_BALL)
2388 struct LevelInfo_MM *level_mm = level->native_mm_level;
2391 for (j = 0; j < level->num_mm_ball_contents; j++)
2392 level->mm_ball_content[j] =
2393 map_element_MM_to_RND(level_mm->ball_content[j]);
2396 // never initialize clipboard elements after the very first time
2397 // (to be able to use clipboard elements between several levels)
2398 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2401 if (IS_ENVELOPE(element))
2403 int envelope_nr = element - EL_ENVELOPE_1;
2405 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2407 level->envelope[envelope_nr] = xx_envelope;
2410 if (IS_CUSTOM_ELEMENT(element) ||
2411 IS_GROUP_ELEMENT(element) ||
2412 IS_INTERNAL_ELEMENT(element))
2414 xx_ei = *ei; // copy element data into temporary buffer
2416 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2421 setElementChangePages(ei, 1);
2422 setElementChangeInfoToDefaults(ei->change);
2424 if (IS_CUSTOM_ELEMENT(element) ||
2425 IS_GROUP_ELEMENT(element))
2427 setElementDescriptionToDefault(ei);
2429 ei->modified_settings = FALSE;
2432 if (IS_CUSTOM_ELEMENT(element) ||
2433 IS_INTERNAL_ELEMENT(element))
2435 // internal values used in level editor
2437 ei->access_type = 0;
2438 ei->access_layer = 0;
2439 ei->access_protected = 0;
2440 ei->walk_to_action = 0;
2441 ei->smash_targets = 0;
2444 ei->can_explode_by_fire = FALSE;
2445 ei->can_explode_smashed = FALSE;
2446 ei->can_explode_impact = FALSE;
2448 ei->current_change_page = 0;
2451 if (IS_GROUP_ELEMENT(element) ||
2452 IS_INTERNAL_ELEMENT(element))
2454 struct ElementGroupInfo *group;
2456 // initialize memory for list of elements in group
2457 if (ei->group == NULL)
2458 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2462 xx_group = *group; // copy group data into temporary buffer
2464 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2469 if (IS_EMPTY_ELEMENT(element) ||
2470 IS_INTERNAL_ELEMENT(element))
2472 xx_ei = *ei; // copy element data into temporary buffer
2474 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2480 clipboard_elements_initialized = TRUE;
2483 static void setLevelInfoToDefaults(struct LevelInfo *level,
2484 boolean level_info_only,
2485 boolean reset_file_status)
2487 setLevelInfoToDefaults_Level(level);
2489 if (!level_info_only)
2490 setLevelInfoToDefaults_Elements(level);
2492 if (reset_file_status)
2494 level->no_valid_file = FALSE;
2495 level->no_level_file = FALSE;
2498 level->changed = FALSE;
2501 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2503 level_file_info->nr = 0;
2504 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2505 level_file_info->packed = FALSE;
2507 setString(&level_file_info->basename, NULL);
2508 setString(&level_file_info->filename, NULL);
2511 int getMappedElement_SB(int, boolean);
2513 static void ActivateLevelTemplate(void)
2517 if (check_special_flags("load_xsb_to_ces"))
2519 // fill smaller playfields with padding "beyond border wall" elements
2520 if (level.fieldx < level_template.fieldx ||
2521 level.fieldy < level_template.fieldy)
2523 short field[level.fieldx][level.fieldy];
2524 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2525 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2526 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2527 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2529 // copy old playfield (which is smaller than the visible area)
2530 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2531 field[x][y] = level.field[x][y];
2533 // fill new, larger playfield with "beyond border wall" elements
2534 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2535 level.field[x][y] = getMappedElement_SB('_', TRUE);
2537 // copy the old playfield to the middle of the new playfield
2538 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2539 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2541 level.fieldx = new_fieldx;
2542 level.fieldy = new_fieldy;
2546 // Currently there is no special action needed to activate the template
2547 // data, because 'element_info' property settings overwrite the original
2548 // level data, while all other variables do not change.
2550 // Exception: 'from_level_template' elements in the original level playfield
2551 // are overwritten with the corresponding elements at the same position in
2552 // playfield from the level template.
2554 for (x = 0; x < level.fieldx; x++)
2555 for (y = 0; y < level.fieldy; y++)
2556 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2557 level.field[x][y] = level_template.field[x][y];
2559 if (check_special_flags("load_xsb_to_ces"))
2561 struct LevelInfo level_backup = level;
2563 // overwrite all individual level settings from template level settings
2564 level = level_template;
2566 // restore level file info
2567 level.file_info = level_backup.file_info;
2569 // restore playfield size
2570 level.fieldx = level_backup.fieldx;
2571 level.fieldy = level_backup.fieldy;
2573 // restore playfield content
2574 for (x = 0; x < level.fieldx; x++)
2575 for (y = 0; y < level.fieldy; y++)
2576 level.field[x][y] = level_backup.field[x][y];
2578 // restore name and author from individual level
2579 strcpy(level.name, level_backup.name);
2580 strcpy(level.author, level_backup.author);
2582 // restore flag "use_custom_template"
2583 level.use_custom_template = level_backup.use_custom_template;
2587 static boolean checkForPackageFromBasename_BD(char *basename)
2589 // check for native BD level file extensions
2590 if (!strSuffixLower(basename, ".bd") &&
2591 !strSuffixLower(basename, ".bdr") &&
2592 !strSuffixLower(basename, ".brc") &&
2593 !strSuffixLower(basename, ".gds"))
2596 // check for standard single-level BD files (like "001.bd")
2597 if (strSuffixLower(basename, ".bd") &&
2598 strlen(basename) == 6 &&
2599 basename[0] >= '0' && basename[0] <= '9' &&
2600 basename[1] >= '0' && basename[1] <= '9' &&
2601 basename[2] >= '0' && basename[2] <= '9')
2604 // this is a level package in native BD file format
2608 static char *getLevelFilenameFromBasename(char *basename)
2610 static char *filename = NULL;
2612 checked_free(filename);
2614 filename = getPath2(getCurrentLevelDir(), basename);
2619 static int getFileTypeFromBasename(char *basename)
2621 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2623 static char *filename = NULL;
2624 struct stat file_status;
2626 // ---------- try to determine file type from filename ----------
2628 // check for typical filename of a Supaplex level package file
2629 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2630 return LEVEL_FILE_TYPE_SP;
2632 // check for typical filename of a Diamond Caves II level package file
2633 if (strSuffixLower(basename, ".dc") ||
2634 strSuffixLower(basename, ".dc2"))
2635 return LEVEL_FILE_TYPE_DC;
2637 // check for typical filename of a Sokoban level package file
2638 if (strSuffixLower(basename, ".xsb") &&
2639 strchr(basename, '%') == NULL)
2640 return LEVEL_FILE_TYPE_SB;
2642 // check for typical filename of a Boulder Dash (GDash) level package file
2643 if (checkForPackageFromBasename_BD(basename))
2644 return LEVEL_FILE_TYPE_BD;
2646 // ---------- try to determine file type from filesize ----------
2648 checked_free(filename);
2649 filename = getPath2(getCurrentLevelDir(), basename);
2651 if (stat(filename, &file_status) == 0)
2653 // check for typical filesize of a Supaplex level package file
2654 if (file_status.st_size == 170496)
2655 return LEVEL_FILE_TYPE_SP;
2658 return LEVEL_FILE_TYPE_UNKNOWN;
2661 static int getFileTypeFromMagicBytes(char *filename, int type)
2665 if ((file = openFile(filename, MODE_READ)))
2667 char chunk_name[CHUNK_ID_LEN + 1];
2669 getFileChunkBE(file, chunk_name, NULL);
2671 if (strEqual(chunk_name, "MMII") ||
2672 strEqual(chunk_name, "MIRR"))
2673 type = LEVEL_FILE_TYPE_MM;
2681 static boolean checkForPackageFromBasename(char *basename)
2683 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2684 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2686 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2689 static char *getSingleLevelBasenameExt(int nr, char *extension)
2691 static char basename[MAX_FILENAME_LEN];
2694 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2696 sprintf(basename, "%03d.%s", nr, extension);
2701 static char *getSingleLevelBasename(int nr)
2703 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2706 static char *getPackedLevelBasename(int type)
2708 static char basename[MAX_FILENAME_LEN];
2709 char *directory = getCurrentLevelDir();
2711 DirectoryEntry *dir_entry;
2713 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2715 if ((dir = openDirectory(directory)) == NULL)
2717 Warn("cannot read current level directory '%s'", directory);
2722 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2724 char *entry_basename = dir_entry->basename;
2725 int entry_type = getFileTypeFromBasename(entry_basename);
2727 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2729 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2732 strcpy(basename, entry_basename);
2739 closeDirectory(dir);
2744 static char *getSingleLevelFilename(int nr)
2746 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2749 #if ENABLE_UNUSED_CODE
2750 static char *getPackedLevelFilename(int type)
2752 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2756 char *getDefaultLevelFilename(int nr)
2758 return getSingleLevelFilename(nr);
2761 #if ENABLE_UNUSED_CODE
2762 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2766 lfi->packed = FALSE;
2768 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2769 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2773 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2774 int type, char *format, ...)
2776 static char basename[MAX_FILENAME_LEN];
2779 va_start(ap, format);
2780 vsprintf(basename, format, ap);
2784 lfi->packed = FALSE;
2786 setString(&lfi->basename, basename);
2787 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2790 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2796 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2797 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2800 static int getFiletypeFromID(char *filetype_id)
2802 char *filetype_id_lower;
2803 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2806 if (filetype_id == NULL)
2807 return LEVEL_FILE_TYPE_UNKNOWN;
2809 filetype_id_lower = getStringToLower(filetype_id);
2811 for (i = 0; filetype_id_list[i].id != NULL; i++)
2813 char *id_lower = getStringToLower(filetype_id_list[i].id);
2815 if (strEqual(filetype_id_lower, id_lower))
2816 filetype = filetype_id_list[i].filetype;
2820 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2824 free(filetype_id_lower);
2829 char *getLocalLevelTemplateFilename(void)
2831 return getDefaultLevelFilename(-1);
2834 char *getGlobalLevelTemplateFilename(void)
2836 // global variable "leveldir_current" must be modified in the loop below
2837 LevelDirTree *leveldir_current_last = leveldir_current;
2838 char *filename = NULL;
2840 // check for template level in path from current to topmost tree node
2842 while (leveldir_current != NULL)
2844 filename = getDefaultLevelFilename(-1);
2846 if (fileExists(filename))
2849 leveldir_current = leveldir_current->node_parent;
2852 // restore global variable "leveldir_current" modified in above loop
2853 leveldir_current = leveldir_current_last;
2858 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2862 // special case: level number is negative => check for level template file
2865 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2866 getSingleLevelBasename(-1));
2868 // replace local level template filename with global template filename
2869 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2871 // no fallback if template file not existing
2875 // special case: check for file name/pattern specified in "levelinfo.conf"
2876 if (leveldir_current->level_filename != NULL)
2878 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2880 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2881 leveldir_current->level_filename, nr);
2883 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2885 if (fileExists(lfi->filename))
2888 else if (leveldir_current->level_filetype != NULL)
2890 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2892 // check for specified native level file with standard file name
2893 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2894 "%03d.%s", nr, LEVELFILE_EXTENSION);
2895 if (fileExists(lfi->filename))
2899 // check for native Rocks'n'Diamonds level file
2900 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2901 "%03d.%s", nr, LEVELFILE_EXTENSION);
2902 if (fileExists(lfi->filename))
2905 // check for native Boulder Dash level file
2906 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2907 if (fileExists(lfi->filename))
2910 // check for Emerald Mine level file (V1)
2911 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2912 'a' + (nr / 10) % 26, '0' + nr % 10);
2913 if (fileExists(lfi->filename))
2915 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2916 'A' + (nr / 10) % 26, '0' + nr % 10);
2917 if (fileExists(lfi->filename))
2920 // check for Emerald Mine level file (V2 to V5)
2921 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2922 if (fileExists(lfi->filename))
2925 // check for Emerald Mine level file (V6 / single mode)
2926 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2927 if (fileExists(lfi->filename))
2929 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2930 if (fileExists(lfi->filename))
2933 // check for Emerald Mine level file (V6 / teamwork mode)
2934 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2935 if (fileExists(lfi->filename))
2937 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2938 if (fileExists(lfi->filename))
2941 // check for various packed level file formats
2942 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2943 if (fileExists(lfi->filename))
2946 // no known level file found -- use default values (and fail later)
2947 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2948 "%03d.%s", nr, LEVELFILE_EXTENSION);
2951 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2953 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2954 lfi->type = getFileTypeFromBasename(lfi->basename);
2956 if (lfi->type == LEVEL_FILE_TYPE_RND)
2957 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2960 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2962 // always start with reliable default values
2963 setFileInfoToDefaults(level_file_info);
2965 level_file_info->nr = nr; // set requested level number
2967 determineLevelFileInfo_Filename(level_file_info);
2968 determineLevelFileInfo_Filetype(level_file_info);
2971 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2972 struct LevelFileInfo *lfi_to)
2974 lfi_to->nr = lfi_from->nr;
2975 lfi_to->type = lfi_from->type;
2976 lfi_to->packed = lfi_from->packed;
2978 setString(&lfi_to->basename, lfi_from->basename);
2979 setString(&lfi_to->filename, lfi_from->filename);
2982 // ----------------------------------------------------------------------------
2983 // functions for loading R'n'D level
2984 // ----------------------------------------------------------------------------
2986 int getMappedElement(int element)
2988 // remap some (historic, now obsolete) elements
2992 case EL_PLAYER_OBSOLETE:
2993 element = EL_PLAYER_1;
2996 case EL_KEY_OBSOLETE:
3000 case EL_EM_KEY_1_FILE_OBSOLETE:
3001 element = EL_EM_KEY_1;
3004 case EL_EM_KEY_2_FILE_OBSOLETE:
3005 element = EL_EM_KEY_2;
3008 case EL_EM_KEY_3_FILE_OBSOLETE:
3009 element = EL_EM_KEY_3;
3012 case EL_EM_KEY_4_FILE_OBSOLETE:
3013 element = EL_EM_KEY_4;
3016 case EL_ENVELOPE_OBSOLETE:
3017 element = EL_ENVELOPE_1;
3025 if (element >= NUM_FILE_ELEMENTS)
3027 Warn("invalid level element %d", element);
3029 element = EL_UNKNOWN;
3037 static int getMappedElementByVersion(int element, int game_version)
3039 // remap some elements due to certain game version
3041 if (game_version <= VERSION_IDENT(2,2,0,0))
3043 // map game font elements
3044 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3045 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3046 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3047 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3050 if (game_version < VERSION_IDENT(3,0,0,0))
3052 // map Supaplex gravity tube elements
3053 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3054 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3055 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3056 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3063 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3065 level->file_version = getFileVersion(file);
3066 level->game_version = getFileVersion(file);
3071 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3073 level->creation_date.year = getFile16BitBE(file);
3074 level->creation_date.month = getFile8Bit(file);
3075 level->creation_date.day = getFile8Bit(file);
3077 level->creation_date.src = DATE_SRC_LEVELFILE;
3082 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3084 int initial_player_stepsize;
3085 int initial_player_gravity;
3088 level->fieldx = getFile8Bit(file);
3089 level->fieldy = getFile8Bit(file);
3091 level->time = getFile16BitBE(file);
3092 level->gems_needed = getFile16BitBE(file);
3094 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3095 level->name[i] = getFile8Bit(file);
3096 level->name[MAX_LEVEL_NAME_LEN] = 0;
3098 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3099 level->score[i] = getFile8Bit(file);
3101 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3102 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3103 for (y = 0; y < 3; y++)
3104 for (x = 0; x < 3; x++)
3105 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3107 level->amoeba_speed = getFile8Bit(file);
3108 level->time_magic_wall = getFile8Bit(file);
3109 level->time_wheel = getFile8Bit(file);
3110 level->amoeba_content = getMappedElement(getFile8Bit(file));
3112 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3115 for (i = 0; i < MAX_PLAYERS; i++)
3116 level->initial_player_stepsize[i] = initial_player_stepsize;
3118 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3120 for (i = 0; i < MAX_PLAYERS; i++)
3121 level->initial_player_gravity[i] = initial_player_gravity;
3123 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3124 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3126 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3128 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3129 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3130 level->can_move_into_acid_bits = getFile32BitBE(file);
3131 level->dont_collide_with_bits = getFile8Bit(file);
3133 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3134 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3136 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3137 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3138 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140 level->game_engine_type = getFile8Bit(file);
3142 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3147 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3151 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3152 level->name[i] = getFile8Bit(file);
3153 level->name[MAX_LEVEL_NAME_LEN] = 0;
3158 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3162 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3163 level->author[i] = getFile8Bit(file);
3164 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3169 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3172 int chunk_size_expected = level->fieldx * level->fieldy;
3174 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3175 stored with 16-bit encoding (and should be twice as big then).
3176 Even worse, playfield data was stored 16-bit when only yamyam content
3177 contained 16-bit elements and vice versa. */
3179 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3180 chunk_size_expected *= 2;
3182 if (chunk_size_expected != chunk_size)
3184 ReadUnusedBytesFromFile(file, chunk_size);
3185 return chunk_size_expected;
3188 for (y = 0; y < level->fieldy; y++)
3189 for (x = 0; x < level->fieldx; x++)
3190 level->field[x][y] =
3191 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3196 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3199 int header_size = 4;
3200 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3201 int chunk_size_expected = header_size + content_size;
3203 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3204 stored with 16-bit encoding (and should be twice as big then).
3205 Even worse, playfield data was stored 16-bit when only yamyam content
3206 contained 16-bit elements and vice versa. */
3208 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3209 chunk_size_expected += content_size;
3211 if (chunk_size_expected != chunk_size)
3213 ReadUnusedBytesFromFile(file, chunk_size);
3214 return chunk_size_expected;
3218 level->num_yamyam_contents = getFile8Bit(file);
3222 // correct invalid number of content fields -- should never happen
3223 if (level->num_yamyam_contents < 1 ||
3224 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3225 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3227 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3228 for (y = 0; y < 3; y++)
3229 for (x = 0; x < 3; x++)
3230 level->yamyam_content[i].e[x][y] =
3231 getMappedElement(level->encoding_16bit_field ?
3232 getFile16BitBE(file) : getFile8Bit(file));
3236 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3241 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3243 element = getMappedElement(getFile16BitBE(file));
3244 num_contents = getFile8Bit(file);
3246 getFile8Bit(file); // content x size (unused)
3247 getFile8Bit(file); // content y size (unused)
3249 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3251 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3252 for (y = 0; y < 3; y++)
3253 for (x = 0; x < 3; x++)
3254 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3256 // correct invalid number of content fields -- should never happen
3257 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3258 num_contents = STD_ELEMENT_CONTENTS;
3260 if (element == EL_YAMYAM)
3262 level->num_yamyam_contents = num_contents;
3264 for (i = 0; i < num_contents; i++)
3265 for (y = 0; y < 3; y++)
3266 for (x = 0; x < 3; x++)
3267 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3269 else if (element == EL_BD_AMOEBA)
3271 level->amoeba_content = content_array[0][0][0];
3275 Warn("cannot load content for element '%d'", element);
3281 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3287 int chunk_size_expected;
3289 element = getMappedElement(getFile16BitBE(file));
3290 if (!IS_ENVELOPE(element))
3291 element = EL_ENVELOPE_1;
3293 envelope_nr = element - EL_ENVELOPE_1;
3295 envelope_len = getFile16BitBE(file);
3297 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3298 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3300 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3302 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3303 if (chunk_size_expected != chunk_size)
3305 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3306 return chunk_size_expected;
3309 for (i = 0; i < envelope_len; i++)
3310 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3315 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3317 int num_changed_custom_elements = getFile16BitBE(file);
3318 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3321 if (chunk_size_expected != chunk_size)
3323 ReadUnusedBytesFromFile(file, chunk_size - 2);
3324 return chunk_size_expected;
3327 for (i = 0; i < num_changed_custom_elements; i++)
3329 int element = getMappedElement(getFile16BitBE(file));
3330 int properties = getFile32BitBE(file);
3332 if (IS_CUSTOM_ELEMENT(element))
3333 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3335 Warn("invalid custom element number %d", element);
3337 // older game versions that wrote level files with CUS1 chunks used
3338 // different default push delay values (not yet stored in level file)
3339 element_info[element].push_delay_fixed = 2;
3340 element_info[element].push_delay_random = 8;
3343 level->file_has_custom_elements = TRUE;
3348 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3350 int num_changed_custom_elements = getFile16BitBE(file);
3351 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3354 if (chunk_size_expected != chunk_size)
3356 ReadUnusedBytesFromFile(file, chunk_size - 2);
3357 return chunk_size_expected;
3360 for (i = 0; i < num_changed_custom_elements; i++)
3362 int element = getMappedElement(getFile16BitBE(file));
3363 int custom_target_element = getMappedElement(getFile16BitBE(file));
3365 if (IS_CUSTOM_ELEMENT(element))
3366 element_info[element].change->target_element = custom_target_element;
3368 Warn("invalid custom element number %d", element);
3371 level->file_has_custom_elements = TRUE;
3376 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3378 int num_changed_custom_elements = getFile16BitBE(file);
3379 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3382 if (chunk_size_expected != chunk_size)
3384 ReadUnusedBytesFromFile(file, chunk_size - 2);
3385 return chunk_size_expected;
3388 for (i = 0; i < num_changed_custom_elements; i++)
3390 int element = getMappedElement(getFile16BitBE(file));
3391 struct ElementInfo *ei = &element_info[element];
3392 unsigned int event_bits;
3394 if (!IS_CUSTOM_ELEMENT(element))
3396 Warn("invalid custom element number %d", element);
3398 element = EL_INTERNAL_DUMMY;
3401 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3402 ei->description[j] = getFile8Bit(file);
3403 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3405 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3407 // some free bytes for future properties and padding
3408 ReadUnusedBytesFromFile(file, 7);
3410 ei->use_gfx_element = getFile8Bit(file);
3411 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3413 ei->collect_score_initial = getFile8Bit(file);
3414 ei->collect_count_initial = getFile8Bit(file);
3416 ei->push_delay_fixed = getFile16BitBE(file);
3417 ei->push_delay_random = getFile16BitBE(file);
3418 ei->move_delay_fixed = getFile16BitBE(file);
3419 ei->move_delay_random = getFile16BitBE(file);
3421 ei->move_pattern = getFile16BitBE(file);
3422 ei->move_direction_initial = getFile8Bit(file);
3423 ei->move_stepsize = getFile8Bit(file);
3425 for (y = 0; y < 3; y++)
3426 for (x = 0; x < 3; x++)
3427 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3429 // bits 0 - 31 of "has_event[]"
3430 event_bits = getFile32BitBE(file);
3431 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3432 if (event_bits & (1u << j))
3433 ei->change->has_event[j] = TRUE;
3435 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3437 ei->change->delay_fixed = getFile16BitBE(file);
3438 ei->change->delay_random = getFile16BitBE(file);
3439 ei->change->delay_frames = getFile16BitBE(file);
3441 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3443 ei->change->explode = getFile8Bit(file);
3444 ei->change->use_target_content = getFile8Bit(file);
3445 ei->change->only_if_complete = getFile8Bit(file);
3446 ei->change->use_random_replace = getFile8Bit(file);
3448 ei->change->random_percentage = getFile8Bit(file);
3449 ei->change->replace_when = getFile8Bit(file);
3451 for (y = 0; y < 3; y++)
3452 for (x = 0; x < 3; x++)
3453 ei->change->target_content.e[x][y] =
3454 getMappedElement(getFile16BitBE(file));
3456 ei->slippery_type = getFile8Bit(file);
3458 // some free bytes for future properties and padding
3459 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3461 // mark that this custom element has been modified
3462 ei->modified_settings = TRUE;
3465 level->file_has_custom_elements = TRUE;
3470 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3472 struct ElementInfo *ei;
3473 int chunk_size_expected;
3477 // ---------- custom element base property values (96 bytes) ----------------
3479 element = getMappedElement(getFile16BitBE(file));
3481 if (!IS_CUSTOM_ELEMENT(element))
3483 Warn("invalid custom element number %d", element);
3485 ReadUnusedBytesFromFile(file, chunk_size - 2);
3490 ei = &element_info[element];
3492 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3493 ei->description[i] = getFile8Bit(file);
3494 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3496 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3498 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3500 ei->num_change_pages = getFile8Bit(file);
3502 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3503 if (chunk_size_expected != chunk_size)
3505 ReadUnusedBytesFromFile(file, chunk_size - 43);
3506 return chunk_size_expected;
3509 ei->ce_value_fixed_initial = getFile16BitBE(file);
3510 ei->ce_value_random_initial = getFile16BitBE(file);
3511 ei->use_last_ce_value = getFile8Bit(file);
3513 ei->use_gfx_element = getFile8Bit(file);
3514 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3516 ei->collect_score_initial = getFile8Bit(file);
3517 ei->collect_count_initial = getFile8Bit(file);
3519 ei->drop_delay_fixed = getFile8Bit(file);
3520 ei->push_delay_fixed = getFile8Bit(file);
3521 ei->drop_delay_random = getFile8Bit(file);
3522 ei->push_delay_random = getFile8Bit(file);
3523 ei->move_delay_fixed = getFile16BitBE(file);
3524 ei->move_delay_random = getFile16BitBE(file);
3526 // bits 0 - 15 of "move_pattern" ...
3527 ei->move_pattern = getFile16BitBE(file);
3528 ei->move_direction_initial = getFile8Bit(file);
3529 ei->move_stepsize = getFile8Bit(file);
3531 ei->slippery_type = getFile8Bit(file);
3533 for (y = 0; y < 3; y++)
3534 for (x = 0; x < 3; x++)
3535 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3537 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3538 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3539 ei->move_leave_type = getFile8Bit(file);
3541 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3542 ei->move_pattern |= (getFile16BitBE(file) << 16);
3544 ei->access_direction = getFile8Bit(file);
3546 ei->explosion_delay = getFile8Bit(file);
3547 ei->ignition_delay = getFile8Bit(file);
3548 ei->explosion_type = getFile8Bit(file);
3550 // some free bytes for future custom property values and padding
3551 ReadUnusedBytesFromFile(file, 1);
3553 // ---------- change page property values (48 bytes) ------------------------
3555 setElementChangePages(ei, ei->num_change_pages);
3557 for (i = 0; i < ei->num_change_pages; i++)
3559 struct ElementChangeInfo *change = &ei->change_page[i];
3560 unsigned int event_bits;
3562 // always start with reliable default values
3563 setElementChangeInfoToDefaults(change);
3565 // bits 0 - 31 of "has_event[]" ...
3566 event_bits = getFile32BitBE(file);
3567 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3568 if (event_bits & (1u << j))
3569 change->has_event[j] = TRUE;
3571 change->target_element = getMappedElement(getFile16BitBE(file));
3573 change->delay_fixed = getFile16BitBE(file);
3574 change->delay_random = getFile16BitBE(file);
3575 change->delay_frames = getFile16BitBE(file);
3577 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3579 change->explode = getFile8Bit(file);
3580 change->use_target_content = getFile8Bit(file);
3581 change->only_if_complete = getFile8Bit(file);
3582 change->use_random_replace = getFile8Bit(file);
3584 change->random_percentage = getFile8Bit(file);
3585 change->replace_when = getFile8Bit(file);
3587 for (y = 0; y < 3; y++)
3588 for (x = 0; x < 3; x++)
3589 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3591 change->can_change = getFile8Bit(file);
3593 change->trigger_side = getFile8Bit(file);
3595 change->trigger_player = getFile8Bit(file);
3596 change->trigger_page = getFile8Bit(file);
3598 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3599 CH_PAGE_ANY : (1 << change->trigger_page));
3601 change->has_action = getFile8Bit(file);
3602 change->action_type = getFile8Bit(file);
3603 change->action_mode = getFile8Bit(file);
3604 change->action_arg = getFile16BitBE(file);
3606 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3607 event_bits = getFile8Bit(file);
3608 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3609 if (event_bits & (1u << (j - 32)))
3610 change->has_event[j] = TRUE;
3613 // mark this custom element as modified
3614 ei->modified_settings = TRUE;
3616 level->file_has_custom_elements = TRUE;
3621 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3623 struct ElementInfo *ei;
3624 struct ElementGroupInfo *group;
3628 element = getMappedElement(getFile16BitBE(file));
3630 if (!IS_GROUP_ELEMENT(element))
3632 Warn("invalid group element number %d", element);
3634 ReadUnusedBytesFromFile(file, chunk_size - 2);
3639 ei = &element_info[element];
3641 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3642 ei->description[i] = getFile8Bit(file);
3643 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3645 group = element_info[element].group;
3647 group->num_elements = getFile8Bit(file);
3649 ei->use_gfx_element = getFile8Bit(file);
3650 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3652 group->choice_mode = getFile8Bit(file);
3654 // some free bytes for future values and padding
3655 ReadUnusedBytesFromFile(file, 3);
3657 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3658 group->element[i] = getMappedElement(getFile16BitBE(file));
3660 // mark this group element as modified
3661 element_info[element].modified_settings = TRUE;
3663 level->file_has_custom_elements = TRUE;
3668 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3669 int element, int real_element)
3671 int micro_chunk_size = 0;
3672 int conf_type = getFile8Bit(file);
3673 int byte_mask = conf_type & CONF_MASK_BYTES;
3674 boolean element_found = FALSE;
3677 micro_chunk_size += 1;
3679 if (byte_mask == CONF_MASK_MULTI_BYTES)
3681 int num_bytes = getFile16BitBE(file);
3682 byte *buffer = checked_malloc(num_bytes);
3684 ReadBytesFromFile(file, buffer, num_bytes);
3686 for (i = 0; conf[i].data_type != -1; i++)
3688 if (conf[i].element == element &&
3689 conf[i].conf_type == conf_type)
3691 int data_type = conf[i].data_type;
3692 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3693 int max_num_entities = conf[i].max_num_entities;
3695 if (num_entities > max_num_entities)
3697 Warn("truncating number of entities for element %d from %d to %d",
3698 element, num_entities, max_num_entities);
3700 num_entities = max_num_entities;
3703 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3704 data_type == TYPE_CONTENT_LIST))
3706 // for element and content lists, zero entities are not allowed
3707 Warn("found empty list of entities for element %d", element);
3709 // do not set "num_entities" here to prevent reading behind buffer
3711 *(int *)(conf[i].num_entities) = 1; // at least one is required
3715 *(int *)(conf[i].num_entities) = num_entities;
3718 element_found = TRUE;
3720 if (data_type == TYPE_STRING)
3722 char *string = (char *)(conf[i].value);
3725 for (j = 0; j < max_num_entities; j++)
3726 string[j] = (j < num_entities ? buffer[j] : '\0');
3728 else if (data_type == TYPE_ELEMENT_LIST)
3730 int *element_array = (int *)(conf[i].value);
3733 for (j = 0; j < num_entities; j++)
3735 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3737 else if (data_type == TYPE_CONTENT_LIST)
3739 struct Content *content= (struct Content *)(conf[i].value);
3742 for (c = 0; c < num_entities; c++)
3743 for (y = 0; y < 3; y++)
3744 for (x = 0; x < 3; x++)
3745 content[c].e[x][y] =
3746 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3749 element_found = FALSE;
3755 checked_free(buffer);
3757 micro_chunk_size += 2 + num_bytes;
3759 else // constant size configuration data (1, 2 or 4 bytes)
3761 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3762 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3763 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3765 for (i = 0; conf[i].data_type != -1; i++)
3767 if (conf[i].element == element &&
3768 conf[i].conf_type == conf_type)
3770 int data_type = conf[i].data_type;
3772 if (data_type == TYPE_ELEMENT)
3773 value = getMappedElement(value);
3775 if (data_type == TYPE_BOOLEAN)
3776 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3778 *(int *) (conf[i].value) = value;
3780 element_found = TRUE;
3786 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3791 char *error_conf_chunk_bytes =
3792 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3793 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3794 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3795 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3796 int error_element = real_element;
3798 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3799 error_conf_chunk_bytes, error_conf_chunk_token,
3800 error_element, EL_NAME(error_element));
3803 return micro_chunk_size;
3806 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3808 int real_chunk_size = 0;
3810 li = *level; // copy level data into temporary buffer
3812 while (!checkEndOfFile(file))
3814 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3816 if (real_chunk_size >= chunk_size)
3820 *level = li; // copy temporary buffer back to level data
3822 return real_chunk_size;
3825 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3827 int real_chunk_size = 0;
3829 li = *level; // copy level data into temporary buffer
3831 while (!checkEndOfFile(file))
3833 int element = getMappedElement(getFile16BitBE(file));
3835 real_chunk_size += 2;
3836 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3838 if (real_chunk_size >= chunk_size)
3842 *level = li; // copy temporary buffer back to level data
3844 return real_chunk_size;
3847 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3849 int real_chunk_size = 0;
3851 li = *level; // copy level data into temporary buffer
3853 while (!checkEndOfFile(file))
3855 int element = getMappedElement(getFile16BitBE(file));
3857 real_chunk_size += 2;
3858 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3860 if (real_chunk_size >= chunk_size)
3864 *level = li; // copy temporary buffer back to level data
3866 return real_chunk_size;
3869 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3871 int element = getMappedElement(getFile16BitBE(file));
3872 int envelope_nr = element - EL_ENVELOPE_1;
3873 int real_chunk_size = 2;
3875 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3877 while (!checkEndOfFile(file))
3879 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3882 if (real_chunk_size >= chunk_size)
3886 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3888 return real_chunk_size;
3891 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3893 int element = getMappedElement(getFile16BitBE(file));
3894 int real_chunk_size = 2;
3895 struct ElementInfo *ei = &element_info[element];
3898 xx_ei = *ei; // copy element data into temporary buffer
3900 xx_ei.num_change_pages = -1;
3902 while (!checkEndOfFile(file))
3904 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3906 if (xx_ei.num_change_pages != -1)
3909 if (real_chunk_size >= chunk_size)
3915 if (ei->num_change_pages == -1)
3917 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3920 ei->num_change_pages = 1;
3922 setElementChangePages(ei, 1);
3923 setElementChangeInfoToDefaults(ei->change);
3925 return real_chunk_size;
3928 // initialize number of change pages stored for this custom element
3929 setElementChangePages(ei, ei->num_change_pages);
3930 for (i = 0; i < ei->num_change_pages; i++)
3931 setElementChangeInfoToDefaults(&ei->change_page[i]);
3933 // start with reading properties for the first change page
3934 xx_current_change_page = 0;
3936 while (!checkEndOfFile(file))
3938 // level file might contain invalid change page number
3939 if (xx_current_change_page >= ei->num_change_pages)
3942 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3944 xx_change = *change; // copy change data into temporary buffer
3946 resetEventBits(); // reset bits; change page might have changed
3948 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3951 *change = xx_change;
3953 setEventFlagsFromEventBits(change);
3955 if (real_chunk_size >= chunk_size)
3959 level->file_has_custom_elements = TRUE;
3961 return real_chunk_size;
3964 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3966 int element = getMappedElement(getFile16BitBE(file));
3967 int real_chunk_size = 2;
3968 struct ElementInfo *ei = &element_info[element];
3969 struct ElementGroupInfo *group = ei->group;
3974 xx_ei = *ei; // copy element data into temporary buffer
3975 xx_group = *group; // copy group data into temporary buffer
3977 while (!checkEndOfFile(file))
3979 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3982 if (real_chunk_size >= chunk_size)
3989 level->file_has_custom_elements = TRUE;
3991 return real_chunk_size;
3994 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3996 int element = getMappedElement(getFile16BitBE(file));
3997 int real_chunk_size = 2;
3998 struct ElementInfo *ei = &element_info[element];
4000 xx_ei = *ei; // copy element data into temporary buffer
4002 while (!checkEndOfFile(file))
4004 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4007 if (real_chunk_size >= chunk_size)
4013 level->file_has_custom_elements = TRUE;
4015 return real_chunk_size;
4018 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4019 struct LevelFileInfo *level_file_info,
4020 boolean level_info_only)
4022 char *filename = level_file_info->filename;
4023 char cookie[MAX_LINE_LEN];
4024 char chunk_name[CHUNK_ID_LEN + 1];
4028 if (!(file = openFile(filename, MODE_READ)))
4030 level->no_valid_file = TRUE;
4031 level->no_level_file = TRUE;
4033 if (level_info_only)
4036 Warn("cannot read level '%s' -- using empty level", filename);
4038 if (!setup.editor.use_template_for_new_levels)
4041 // if level file not found, try to initialize level data from template
4042 filename = getGlobalLevelTemplateFilename();
4044 if (!(file = openFile(filename, MODE_READ)))
4047 // default: for empty levels, use level template for custom elements
4048 level->use_custom_template = TRUE;
4050 level->no_valid_file = FALSE;
4053 getFileChunkBE(file, chunk_name, NULL);
4054 if (strEqual(chunk_name, "RND1"))
4056 getFile32BitBE(file); // not used
4058 getFileChunkBE(file, chunk_name, NULL);
4059 if (!strEqual(chunk_name, "CAVE"))
4061 level->no_valid_file = TRUE;
4063 Warn("unknown format of level file '%s'", filename);
4070 else // check for pre-2.0 file format with cookie string
4072 strcpy(cookie, chunk_name);
4073 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4075 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4076 cookie[strlen(cookie) - 1] = '\0';
4078 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4080 level->no_valid_file = TRUE;
4082 Warn("unknown format of level file '%s'", filename);
4089 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4091 level->no_valid_file = TRUE;
4093 Warn("unsupported version of level file '%s'", filename);
4100 // pre-2.0 level files have no game version, so use file version here
4101 level->game_version = level->file_version;
4104 if (level->file_version < FILE_VERSION_1_2)
4106 // level files from versions before 1.2.0 without chunk structure
4107 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4108 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4116 int (*loader)(File *, int, struct LevelInfo *);
4120 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4121 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4122 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4123 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4124 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4125 { "INFO", -1, LoadLevel_INFO },
4126 { "BODY", -1, LoadLevel_BODY },
4127 { "CONT", -1, LoadLevel_CONT },
4128 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4129 { "CNT3", -1, LoadLevel_CNT3 },
4130 { "CUS1", -1, LoadLevel_CUS1 },
4131 { "CUS2", -1, LoadLevel_CUS2 },
4132 { "CUS3", -1, LoadLevel_CUS3 },
4133 { "CUS4", -1, LoadLevel_CUS4 },
4134 { "GRP1", -1, LoadLevel_GRP1 },
4135 { "CONF", -1, LoadLevel_CONF },
4136 { "ELEM", -1, LoadLevel_ELEM },
4137 { "NOTE", -1, LoadLevel_NOTE },
4138 { "CUSX", -1, LoadLevel_CUSX },
4139 { "GRPX", -1, LoadLevel_GRPX },
4140 { "EMPX", -1, LoadLevel_EMPX },
4145 while (getFileChunkBE(file, chunk_name, &chunk_size))
4149 while (chunk_info[i].name != NULL &&
4150 !strEqual(chunk_name, chunk_info[i].name))
4153 if (chunk_info[i].name == NULL)
4155 Warn("unknown chunk '%s' in level file '%s'",
4156 chunk_name, filename);
4158 ReadUnusedBytesFromFile(file, chunk_size);
4160 else if (chunk_info[i].size != -1 &&
4161 chunk_info[i].size != chunk_size)
4163 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4164 chunk_size, chunk_name, filename);
4166 ReadUnusedBytesFromFile(file, chunk_size);
4170 // call function to load this level chunk
4171 int chunk_size_expected =
4172 (chunk_info[i].loader)(file, chunk_size, level);
4174 if (chunk_size_expected < 0)
4176 Warn("error reading chunk '%s' in level file '%s'",
4177 chunk_name, filename);
4182 // the size of some chunks cannot be checked before reading other
4183 // chunks first (like "HEAD" and "BODY") that contain some header
4184 // information, so check them here
4185 if (chunk_size_expected != chunk_size)
4187 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4188 chunk_size, chunk_name, filename);
4200 // ----------------------------------------------------------------------------
4201 // functions for loading BD level
4202 // ----------------------------------------------------------------------------
4204 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4205 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4207 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4209 struct LevelInfo_BD *level_bd = level->native_bd_level;
4210 GdCave *cave = NULL; // will be changed below
4211 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4212 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4215 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4217 // cave and map newly allocated when set to defaults above
4218 cave = level_bd->cave;
4221 cave->intermission = level->bd_intermission;
4224 cave->level_time[0] = level->time;
4225 cave->level_diamonds[0] = level->gems_needed;
4228 cave->scheduling = level->bd_scheduling_type;
4229 cave->pal_timing = level->bd_pal_timing;
4230 cave->level_speed[0] = level->bd_cycle_delay_ms;
4231 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4232 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4233 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4236 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4237 cave->diamond_value = level->score[SC_EMERALD];
4238 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4240 // compatibility settings
4241 cave->lineshift = level->bd_line_shifting_borders;
4242 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4243 cave->short_explosions = level->bd_short_explosions;
4245 // player properties
4246 cave->diagonal_movements = level->bd_diagonal_movements;
4247 cave->active_is_first_found = level->bd_topmost_player_active;
4248 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4249 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4250 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4251 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4253 // element properties
4254 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4255 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4256 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4257 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4258 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4259 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4260 cave->level_magic_wall_time[0] = level->time_magic_wall;
4261 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4262 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4264 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4265 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4266 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4267 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4268 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4269 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4270 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4272 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4273 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4274 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4275 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4276 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4277 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4278 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4279 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4280 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4281 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4282 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4284 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4285 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4286 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4287 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4288 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4289 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4291 cave->slime_predictable = level->bd_slime_is_predictable;
4292 cave->slime_correct_random = level->bd_slime_correct_random;
4293 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4294 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4295 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4296 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4297 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4298 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4299 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4300 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4301 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4302 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4304 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4305 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4306 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4308 cave->biter_delay_frame = level->bd_biter_move_delay;
4309 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4311 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4313 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4315 cave->replicators_active = level->bd_replicators_active;
4316 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4318 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4319 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4321 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4323 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4325 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4326 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4327 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4329 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4330 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4332 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4333 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4335 cave->creatures_backwards = level->bd_creatures_start_backwards;
4336 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4337 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4339 cave->gravity = level->bd_gravity_direction;
4340 cave->gravity_switch_active = level->bd_gravity_switch_active;
4341 cave->gravity_change_time = level->bd_gravity_switch_delay;
4342 cave->gravity_affects_all = level->bd_gravity_affects_all;
4344 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4345 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4346 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4347 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4349 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4350 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4351 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4352 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4353 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4354 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4356 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4357 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4358 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4359 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4362 strncpy(cave->name, level->name, sizeof(GdString));
4363 cave->name[sizeof(GdString) - 1] = '\0';
4365 // playfield elements
4366 for (x = 0; x < cave->w; x++)
4367 for (y = 0; y < cave->h; y++)
4368 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4371 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4373 struct LevelInfo_BD *level_bd = level->native_bd_level;
4374 GdCave *cave = level_bd->cave;
4375 int bd_level_nr = level_bd->level_nr;
4378 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4379 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4382 level->bd_intermission = cave->intermission;
4385 level->time = cave->level_time[bd_level_nr];
4386 level->gems_needed = cave->level_diamonds[bd_level_nr];
4389 level->bd_scheduling_type = cave->scheduling;
4390 level->bd_pal_timing = cave->pal_timing;
4391 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4392 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4393 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4394 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4397 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4398 level->score[SC_EMERALD] = cave->diamond_value;
4399 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4401 // compatibility settings
4402 level->bd_line_shifting_borders = cave->lineshift;
4403 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4404 level->bd_short_explosions = cave->short_explosions;
4406 // player properties
4407 level->bd_diagonal_movements = cave->diagonal_movements;
4408 level->bd_topmost_player_active = cave->active_is_first_found;
4409 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4410 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4411 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4412 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4414 // element properties
4415 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4416 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4417 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4418 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4419 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4420 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4421 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4422 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4423 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4425 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4426 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4427 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4428 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4429 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4430 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4431 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4433 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4434 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4435 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4436 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4437 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4438 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4439 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4440 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4441 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4442 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4443 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4445 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4446 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4447 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4448 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4449 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4450 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4452 level->bd_slime_is_predictable = cave->slime_predictable;
4453 level->bd_slime_correct_random = cave->slime_correct_random;
4454 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4455 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4456 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4457 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4458 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4459 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4460 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4461 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4462 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4463 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4465 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4466 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4467 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4469 level->bd_biter_move_delay = cave->biter_delay_frame;
4470 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4472 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4474 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4476 level->bd_replicators_active = cave->replicators_active;
4477 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4479 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4480 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4482 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4484 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4486 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4487 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4488 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4490 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4491 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4493 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4494 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4496 level->bd_creatures_start_backwards = cave->creatures_backwards;
4497 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4498 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4500 level->bd_gravity_direction = cave->gravity;
4501 level->bd_gravity_switch_active = cave->gravity_switch_active;
4502 level->bd_gravity_switch_delay = cave->gravity_change_time;
4503 level->bd_gravity_affects_all = cave->gravity_affects_all;
4505 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4506 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4507 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4508 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4510 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4511 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4512 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4513 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4514 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4515 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4517 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4518 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4519 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4520 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4523 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4525 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4526 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4528 // playfield elements
4529 for (x = 0; x < level->fieldx; x++)
4530 for (y = 0; y < level->fieldy; y++)
4531 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4533 checked_free(cave_name);
4536 static void setTapeInfoToDefaults(void);
4538 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4540 struct LevelInfo_BD *level_bd = level->native_bd_level;
4541 GdCave *cave = level_bd->cave;
4542 GdReplay *replay = level_bd->replay;
4548 // always start with reliable default values
4549 setTapeInfoToDefaults();
4551 tape.level_nr = level_nr; // (currently not used)
4552 tape.random_seed = replay->seed;
4554 TapeSetDateFromIsoDateString(replay->date);
4557 tape.pos[tape.counter].delay = 0;
4559 tape.bd_replay = TRUE;
4561 // all time calculations only used to display approximate tape time
4562 int cave_speed = cave->speed;
4563 int milliseconds_game = 0;
4564 int milliseconds_elapsed = 20;
4566 for (i = 0; i < replay->movements->len; i++)
4568 int replay_action = replay->movements->data[i];
4569 int tape_action = map_action_BD_to_RND(replay_action);
4570 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4571 boolean success = 0;
4575 success = TapeAddAction(action);
4577 milliseconds_game += milliseconds_elapsed;
4579 if (milliseconds_game >= cave_speed)
4581 milliseconds_game -= cave_speed;
4588 tape.pos[tape.counter].delay = 0;
4589 tape.pos[tape.counter].action[0] = 0;
4593 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4599 TapeHaltRecording();
4603 // ----------------------------------------------------------------------------
4604 // functions for loading EM level
4605 // ----------------------------------------------------------------------------
4607 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4609 static int ball_xy[8][2] =
4620 struct LevelInfo_EM *level_em = level->native_em_level;
4621 struct CAVE *cav = level_em->cav;
4624 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4625 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4627 cav->time_seconds = level->time;
4628 cav->gems_needed = level->gems_needed;
4630 cav->emerald_score = level->score[SC_EMERALD];
4631 cav->diamond_score = level->score[SC_DIAMOND];
4632 cav->alien_score = level->score[SC_ROBOT];
4633 cav->tank_score = level->score[SC_SPACESHIP];
4634 cav->bug_score = level->score[SC_BUG];
4635 cav->eater_score = level->score[SC_YAMYAM];
4636 cav->nut_score = level->score[SC_NUT];
4637 cav->dynamite_score = level->score[SC_DYNAMITE];
4638 cav->key_score = level->score[SC_KEY];
4639 cav->exit_score = level->score[SC_TIME_BONUS];
4641 cav->num_eater_arrays = level->num_yamyam_contents;
4643 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4644 for (y = 0; y < 3; y++)
4645 for (x = 0; x < 3; x++)
4646 cav->eater_array[i][y * 3 + x] =
4647 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4649 cav->amoeba_time = level->amoeba_speed;
4650 cav->wonderwall_time = level->time_magic_wall;
4651 cav->wheel_time = level->time_wheel;
4653 cav->android_move_time = level->android_move_time;
4654 cav->android_clone_time = level->android_clone_time;
4655 cav->ball_random = level->ball_random;
4656 cav->ball_active = level->ball_active_initial;
4657 cav->ball_time = level->ball_time;
4658 cav->num_ball_arrays = level->num_ball_contents;
4660 cav->lenses_score = level->lenses_score;
4661 cav->magnify_score = level->magnify_score;
4662 cav->slurp_score = level->slurp_score;
4664 cav->lenses_time = level->lenses_time;
4665 cav->magnify_time = level->magnify_time;
4667 cav->wind_time = 9999;
4668 cav->wind_direction =
4669 map_direction_RND_to_EM(level->wind_direction_initial);
4671 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4672 for (j = 0; j < 8; j++)
4673 cav->ball_array[i][j] =
4674 map_element_RND_to_EM_cave(level->ball_content[i].
4675 e[ball_xy[j][0]][ball_xy[j][1]]);
4677 map_android_clone_elements_RND_to_EM(level);
4679 // first fill the complete playfield with the empty space element
4680 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4681 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4682 cav->cave[x][y] = Cblank;
4684 // then copy the real level contents from level file into the playfield
4685 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4687 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4689 if (level->field[x][y] == EL_AMOEBA_DEAD)
4690 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4692 cav->cave[x][y] = new_element;
4695 for (i = 0; i < MAX_PLAYERS; i++)
4697 cav->player_x[i] = -1;
4698 cav->player_y[i] = -1;
4701 // initialize player positions and delete players from the playfield
4702 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4704 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4706 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4708 cav->player_x[player_nr] = x;
4709 cav->player_y[player_nr] = y;
4711 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4716 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4718 static int ball_xy[8][2] =
4729 struct LevelInfo_EM *level_em = level->native_em_level;
4730 struct CAVE *cav = level_em->cav;
4733 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4734 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4736 level->time = cav->time_seconds;
4737 level->gems_needed = cav->gems_needed;
4739 sprintf(level->name, "Level %d", level->file_info.nr);
4741 level->score[SC_EMERALD] = cav->emerald_score;
4742 level->score[SC_DIAMOND] = cav->diamond_score;
4743 level->score[SC_ROBOT] = cav->alien_score;
4744 level->score[SC_SPACESHIP] = cav->tank_score;
4745 level->score[SC_BUG] = cav->bug_score;
4746 level->score[SC_YAMYAM] = cav->eater_score;
4747 level->score[SC_NUT] = cav->nut_score;
4748 level->score[SC_DYNAMITE] = cav->dynamite_score;
4749 level->score[SC_KEY] = cav->key_score;
4750 level->score[SC_TIME_BONUS] = cav->exit_score;
4752 level->num_yamyam_contents = cav->num_eater_arrays;
4754 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4755 for (y = 0; y < 3; y++)
4756 for (x = 0; x < 3; x++)
4757 level->yamyam_content[i].e[x][y] =
4758 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4760 level->amoeba_speed = cav->amoeba_time;
4761 level->time_magic_wall = cav->wonderwall_time;
4762 level->time_wheel = cav->wheel_time;
4764 level->android_move_time = cav->android_move_time;
4765 level->android_clone_time = cav->android_clone_time;
4766 level->ball_random = cav->ball_random;
4767 level->ball_active_initial = cav->ball_active;
4768 level->ball_time = cav->ball_time;
4769 level->num_ball_contents = cav->num_ball_arrays;
4771 level->lenses_score = cav->lenses_score;
4772 level->magnify_score = cav->magnify_score;
4773 level->slurp_score = cav->slurp_score;
4775 level->lenses_time = cav->lenses_time;
4776 level->magnify_time = cav->magnify_time;
4778 level->wind_direction_initial =
4779 map_direction_EM_to_RND(cav->wind_direction);
4781 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4782 for (j = 0; j < 8; j++)
4783 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4784 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4786 map_android_clone_elements_EM_to_RND(level);
4788 // convert the playfield (some elements need special treatment)
4789 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4791 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4793 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4794 new_element = EL_AMOEBA_DEAD;
4796 level->field[x][y] = new_element;
4799 for (i = 0; i < MAX_PLAYERS; i++)
4801 // in case of all players set to the same field, use the first player
4802 int nr = MAX_PLAYERS - i - 1;
4803 int jx = cav->player_x[nr];
4804 int jy = cav->player_y[nr];
4806 if (jx != -1 && jy != -1)
4807 level->field[jx][jy] = EL_PLAYER_1 + nr;
4810 // time score is counted for each 10 seconds left in Emerald Mine levels
4811 level->time_score_base = 10;
4815 // ----------------------------------------------------------------------------
4816 // functions for loading SP level
4817 // ----------------------------------------------------------------------------
4819 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4821 struct LevelInfo_SP *level_sp = level->native_sp_level;
4822 LevelInfoType *header = &level_sp->header;
4825 level_sp->width = level->fieldx;
4826 level_sp->height = level->fieldy;
4828 for (x = 0; x < level->fieldx; x++)
4829 for (y = 0; y < level->fieldy; y++)
4830 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4832 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4834 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4835 header->LevelTitle[i] = level->name[i];
4836 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4838 header->InfotronsNeeded = level->gems_needed;
4840 header->SpecialPortCount = 0;
4842 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4844 boolean gravity_port_found = FALSE;
4845 boolean gravity_port_valid = FALSE;
4846 int gravity_port_flag;
4847 int gravity_port_base_element;
4848 int element = level->field[x][y];
4850 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4851 element <= EL_SP_GRAVITY_ON_PORT_UP)
4853 gravity_port_found = TRUE;
4854 gravity_port_valid = TRUE;
4855 gravity_port_flag = 1;
4856 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4858 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4859 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4861 gravity_port_found = TRUE;
4862 gravity_port_valid = TRUE;
4863 gravity_port_flag = 0;
4864 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4866 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4867 element <= EL_SP_GRAVITY_PORT_UP)
4869 // change R'n'D style gravity inverting special port to normal port
4870 // (there are no gravity inverting ports in native Supaplex engine)
4872 gravity_port_found = TRUE;
4873 gravity_port_valid = FALSE;
4874 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4877 if (gravity_port_found)
4879 if (gravity_port_valid &&
4880 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4882 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4884 port->PortLocation = (y * level->fieldx + x) * 2;
4885 port->Gravity = gravity_port_flag;
4887 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4889 header->SpecialPortCount++;
4893 // change special gravity port to normal port
4895 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4898 level_sp->playfield[x][y] = element - EL_SP_START;
4903 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4905 struct LevelInfo_SP *level_sp = level->native_sp_level;
4906 LevelInfoType *header = &level_sp->header;
4907 boolean num_invalid_elements = 0;
4910 level->fieldx = level_sp->width;
4911 level->fieldy = level_sp->height;
4913 for (x = 0; x < level->fieldx; x++)
4915 for (y = 0; y < level->fieldy; y++)
4917 int element_old = level_sp->playfield[x][y];
4918 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4920 if (element_new == EL_UNKNOWN)
4922 num_invalid_elements++;
4924 Debug("level:native:SP", "invalid element %d at position %d, %d",
4928 level->field[x][y] = element_new;
4932 if (num_invalid_elements > 0)
4933 Warn("found %d invalid elements%s", num_invalid_elements,
4934 (!options.debug ? " (use '--debug' for more details)" : ""));
4936 for (i = 0; i < MAX_PLAYERS; i++)
4937 level->initial_player_gravity[i] =
4938 (header->InitialGravity == 1 ? TRUE : FALSE);
4940 // skip leading spaces
4941 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4942 if (header->LevelTitle[i] != ' ')
4946 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4947 level->name[j] = header->LevelTitle[i];
4948 level->name[j] = '\0';
4950 // cut trailing spaces
4952 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4953 level->name[j - 1] = '\0';
4955 level->gems_needed = header->InfotronsNeeded;
4957 for (i = 0; i < header->SpecialPortCount; i++)
4959 SpecialPortType *port = &header->SpecialPort[i];
4960 int port_location = port->PortLocation;
4961 int gravity = port->Gravity;
4962 int port_x, port_y, port_element;
4964 port_x = (port_location / 2) % level->fieldx;
4965 port_y = (port_location / 2) / level->fieldx;
4967 if (port_x < 0 || port_x >= level->fieldx ||
4968 port_y < 0 || port_y >= level->fieldy)
4970 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4975 port_element = level->field[port_x][port_y];
4977 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4978 port_element > EL_SP_GRAVITY_PORT_UP)
4980 Warn("no special port at position (%d, %d)", port_x, port_y);
4985 // change previous (wrong) gravity inverting special port to either
4986 // gravity enabling special port or gravity disabling special port
4987 level->field[port_x][port_y] +=
4988 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4989 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4992 // change special gravity ports without database entries to normal ports
4993 for (x = 0; x < level->fieldx; x++)
4994 for (y = 0; y < level->fieldy; y++)
4995 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4996 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4997 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4999 level->time = 0; // no time limit
5000 level->amoeba_speed = 0;
5001 level->time_magic_wall = 0;
5002 level->time_wheel = 0;
5003 level->amoeba_content = EL_EMPTY;
5005 // original Supaplex does not use score values -- rate by playing time
5006 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5007 level->score[i] = 0;
5009 level->rate_time_over_score = TRUE;
5011 // there are no yamyams in supaplex levels
5012 for (i = 0; i < level->num_yamyam_contents; i++)
5013 for (x = 0; x < 3; x++)
5014 for (y = 0; y < 3; y++)
5015 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5018 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5020 struct LevelInfo_SP *level_sp = level->native_sp_level;
5021 struct DemoInfo_SP *demo = &level_sp->demo;
5024 // always start with reliable default values
5025 demo->is_available = FALSE;
5028 if (TAPE_IS_EMPTY(tape))
5031 demo->level_nr = tape.level_nr; // (currently not used)
5033 level_sp->header.DemoRandomSeed = tape.random_seed;
5037 for (i = 0; i < tape.length; i++)
5039 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5040 int demo_repeat = tape.pos[i].delay;
5041 int demo_entries = (demo_repeat + 15) / 16;
5043 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5045 Warn("tape truncated: size exceeds maximum SP demo size %d",
5051 for (j = 0; j < demo_repeat / 16; j++)
5052 demo->data[demo->length++] = 0xf0 | demo_action;
5054 if (demo_repeat % 16)
5055 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5058 demo->is_available = TRUE;
5061 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5063 struct LevelInfo_SP *level_sp = level->native_sp_level;
5064 struct DemoInfo_SP *demo = &level_sp->demo;
5065 char *filename = level->file_info.filename;
5068 // always start with reliable default values
5069 setTapeInfoToDefaults();
5071 if (!demo->is_available)
5074 tape.level_nr = demo->level_nr; // (currently not used)
5075 tape.random_seed = level_sp->header.DemoRandomSeed;
5077 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5080 tape.pos[tape.counter].delay = 0;
5082 for (i = 0; i < demo->length; i++)
5084 int demo_action = demo->data[i] & 0x0f;
5085 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5086 int tape_action = map_key_SP_to_RND(demo_action);
5087 int tape_repeat = demo_repeat + 1;
5088 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5089 boolean success = 0;
5092 for (j = 0; j < tape_repeat; j++)
5093 success = TapeAddAction(action);
5097 Warn("SP demo truncated: size exceeds maximum tape size %d",
5104 TapeHaltRecording();
5108 // ----------------------------------------------------------------------------
5109 // functions for loading MM level
5110 // ----------------------------------------------------------------------------
5112 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5114 struct LevelInfo_MM *level_mm = level->native_mm_level;
5117 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5118 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5120 level_mm->time = level->time;
5121 level_mm->kettles_needed = level->gems_needed;
5122 level_mm->auto_count_kettles = level->auto_count_gems;
5124 level_mm->mm_laser_red = level->mm_laser_red;
5125 level_mm->mm_laser_green = level->mm_laser_green;
5126 level_mm->mm_laser_blue = level->mm_laser_blue;
5128 level_mm->df_laser_red = level->df_laser_red;
5129 level_mm->df_laser_green = level->df_laser_green;
5130 level_mm->df_laser_blue = level->df_laser_blue;
5132 strcpy(level_mm->name, level->name);
5133 strcpy(level_mm->author, level->author);
5135 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5136 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5137 level_mm->score[SC_KEY] = level->score[SC_KEY];
5138 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5139 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5141 level_mm->amoeba_speed = level->amoeba_speed;
5142 level_mm->time_fuse = level->mm_time_fuse;
5143 level_mm->time_bomb = level->mm_time_bomb;
5144 level_mm->time_ball = level->mm_time_ball;
5145 level_mm->time_block = level->mm_time_block;
5147 level_mm->num_ball_contents = level->num_mm_ball_contents;
5148 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5149 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5150 level_mm->explode_ball = level->explode_mm_ball;
5152 for (i = 0; i < level->num_mm_ball_contents; i++)
5153 level_mm->ball_content[i] =
5154 map_element_RND_to_MM(level->mm_ball_content[i]);
5156 for (x = 0; x < level->fieldx; x++)
5157 for (y = 0; y < level->fieldy; y++)
5159 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5162 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5164 struct LevelInfo_MM *level_mm = level->native_mm_level;
5167 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5168 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5170 level->time = level_mm->time;
5171 level->gems_needed = level_mm->kettles_needed;
5172 level->auto_count_gems = level_mm->auto_count_kettles;
5174 level->mm_laser_red = level_mm->mm_laser_red;
5175 level->mm_laser_green = level_mm->mm_laser_green;
5176 level->mm_laser_blue = level_mm->mm_laser_blue;
5178 level->df_laser_red = level_mm->df_laser_red;
5179 level->df_laser_green = level_mm->df_laser_green;
5180 level->df_laser_blue = level_mm->df_laser_blue;
5182 strcpy(level->name, level_mm->name);
5184 // only overwrite author from 'levelinfo.conf' if author defined in level
5185 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5186 strcpy(level->author, level_mm->author);
5188 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5189 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5190 level->score[SC_KEY] = level_mm->score[SC_KEY];
5191 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5192 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5194 level->amoeba_speed = level_mm->amoeba_speed;
5195 level->mm_time_fuse = level_mm->time_fuse;
5196 level->mm_time_bomb = level_mm->time_bomb;
5197 level->mm_time_ball = level_mm->time_ball;
5198 level->mm_time_block = level_mm->time_block;
5200 level->num_mm_ball_contents = level_mm->num_ball_contents;
5201 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5202 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5203 level->explode_mm_ball = level_mm->explode_ball;
5205 for (i = 0; i < level->num_mm_ball_contents; i++)
5206 level->mm_ball_content[i] =
5207 map_element_MM_to_RND(level_mm->ball_content[i]);
5209 for (x = 0; x < level->fieldx; x++)
5210 for (y = 0; y < level->fieldy; y++)
5211 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5215 // ----------------------------------------------------------------------------
5216 // functions for loading DC level
5217 // ----------------------------------------------------------------------------
5219 #define DC_LEVEL_HEADER_SIZE 344
5221 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5224 static int last_data_encoded;
5228 int diff_hi, diff_lo;
5229 int data_hi, data_lo;
5230 unsigned short data_decoded;
5234 last_data_encoded = 0;
5241 diff = data_encoded - last_data_encoded;
5242 diff_hi = diff & ~0xff;
5243 diff_lo = diff & 0xff;
5247 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5248 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5249 data_hi = data_hi & 0xff00;
5251 data_decoded = data_hi | data_lo;
5253 last_data_encoded = data_encoded;
5255 offset1 = (offset1 + 1) % 31;
5256 offset2 = offset2 & 0xff;
5258 return data_decoded;
5261 static int getMappedElement_DC(int element)
5269 // 0x0117 - 0x036e: (?)
5272 // 0x042d - 0x0684: (?)
5288 element = EL_CRYSTAL;
5291 case 0x0e77: // quicksand (boulder)
5292 element = EL_QUICKSAND_FAST_FULL;
5295 case 0x0e99: // slow quicksand (boulder)
5296 element = EL_QUICKSAND_FULL;
5300 element = EL_EM_EXIT_OPEN;
5304 element = EL_EM_EXIT_CLOSED;
5308 element = EL_EM_STEEL_EXIT_OPEN;
5312 element = EL_EM_STEEL_EXIT_CLOSED;
5315 case 0x0f4f: // dynamite (lit 1)
5316 element = EL_EM_DYNAMITE_ACTIVE;
5319 case 0x0f57: // dynamite (lit 2)
5320 element = EL_EM_DYNAMITE_ACTIVE;
5323 case 0x0f5f: // dynamite (lit 3)
5324 element = EL_EM_DYNAMITE_ACTIVE;
5327 case 0x0f67: // dynamite (lit 4)
5328 element = EL_EM_DYNAMITE_ACTIVE;
5335 element = EL_AMOEBA_WET;
5339 element = EL_AMOEBA_DROP;
5343 element = EL_DC_MAGIC_WALL;
5347 element = EL_SPACESHIP_UP;
5351 element = EL_SPACESHIP_DOWN;
5355 element = EL_SPACESHIP_LEFT;
5359 element = EL_SPACESHIP_RIGHT;
5363 element = EL_BUG_UP;
5367 element = EL_BUG_DOWN;
5371 element = EL_BUG_LEFT;
5375 element = EL_BUG_RIGHT;
5379 element = EL_MOLE_UP;
5383 element = EL_MOLE_DOWN;
5387 element = EL_MOLE_LEFT;
5391 element = EL_MOLE_RIGHT;
5399 element = EL_YAMYAM_UP;
5403 element = EL_SWITCHGATE_OPEN;
5407 element = EL_SWITCHGATE_CLOSED;
5411 element = EL_DC_SWITCHGATE_SWITCH_UP;
5415 element = EL_TIMEGATE_CLOSED;
5418 case 0x144c: // conveyor belt switch (green)
5419 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5422 case 0x144f: // conveyor belt switch (red)
5423 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5426 case 0x1452: // conveyor belt switch (blue)
5427 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5431 element = EL_CONVEYOR_BELT_3_MIDDLE;
5435 element = EL_CONVEYOR_BELT_3_LEFT;
5439 element = EL_CONVEYOR_BELT_3_RIGHT;
5443 element = EL_CONVEYOR_BELT_1_MIDDLE;
5447 element = EL_CONVEYOR_BELT_1_LEFT;
5451 element = EL_CONVEYOR_BELT_1_RIGHT;
5455 element = EL_CONVEYOR_BELT_4_MIDDLE;
5459 element = EL_CONVEYOR_BELT_4_LEFT;
5463 element = EL_CONVEYOR_BELT_4_RIGHT;
5467 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5471 element = EL_EXPANDABLE_WALL_VERTICAL;
5475 element = EL_EXPANDABLE_WALL_ANY;
5478 case 0x14ce: // growing steel wall (left/right)
5479 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5482 case 0x14df: // growing steel wall (up/down)
5483 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5486 case 0x14e8: // growing steel wall (up/down/left/right)
5487 element = EL_EXPANDABLE_STEELWALL_ANY;
5491 element = EL_SHIELD_DEADLY;
5495 element = EL_EXTRA_TIME;
5503 element = EL_EMPTY_SPACE;
5506 case 0x1578: // quicksand (empty)
5507 element = EL_QUICKSAND_FAST_EMPTY;
5510 case 0x1579: // slow quicksand (empty)
5511 element = EL_QUICKSAND_EMPTY;
5521 element = EL_EM_DYNAMITE;
5524 case 0x15a1: // key (red)
5525 element = EL_EM_KEY_1;
5528 case 0x15a2: // key (yellow)
5529 element = EL_EM_KEY_2;
5532 case 0x15a3: // key (blue)
5533 element = EL_EM_KEY_4;
5536 case 0x15a4: // key (green)
5537 element = EL_EM_KEY_3;
5540 case 0x15a5: // key (white)
5541 element = EL_DC_KEY_WHITE;
5545 element = EL_WALL_SLIPPERY;
5552 case 0x15a8: // wall (not round)
5556 case 0x15a9: // (blue)
5557 element = EL_CHAR_A;
5560 case 0x15aa: // (blue)
5561 element = EL_CHAR_B;
5564 case 0x15ab: // (blue)
5565 element = EL_CHAR_C;
5568 case 0x15ac: // (blue)
5569 element = EL_CHAR_D;
5572 case 0x15ad: // (blue)
5573 element = EL_CHAR_E;
5576 case 0x15ae: // (blue)
5577 element = EL_CHAR_F;
5580 case 0x15af: // (blue)
5581 element = EL_CHAR_G;
5584 case 0x15b0: // (blue)
5585 element = EL_CHAR_H;
5588 case 0x15b1: // (blue)
5589 element = EL_CHAR_I;
5592 case 0x15b2: // (blue)
5593 element = EL_CHAR_J;
5596 case 0x15b3: // (blue)
5597 element = EL_CHAR_K;
5600 case 0x15b4: // (blue)
5601 element = EL_CHAR_L;
5604 case 0x15b5: // (blue)
5605 element = EL_CHAR_M;
5608 case 0x15b6: // (blue)
5609 element = EL_CHAR_N;
5612 case 0x15b7: // (blue)
5613 element = EL_CHAR_O;
5616 case 0x15b8: // (blue)
5617 element = EL_CHAR_P;
5620 case 0x15b9: // (blue)
5621 element = EL_CHAR_Q;
5624 case 0x15ba: // (blue)
5625 element = EL_CHAR_R;
5628 case 0x15bb: // (blue)
5629 element = EL_CHAR_S;
5632 case 0x15bc: // (blue)
5633 element = EL_CHAR_T;
5636 case 0x15bd: // (blue)
5637 element = EL_CHAR_U;
5640 case 0x15be: // (blue)
5641 element = EL_CHAR_V;
5644 case 0x15bf: // (blue)
5645 element = EL_CHAR_W;
5648 case 0x15c0: // (blue)
5649 element = EL_CHAR_X;
5652 case 0x15c1: // (blue)
5653 element = EL_CHAR_Y;
5656 case 0x15c2: // (blue)
5657 element = EL_CHAR_Z;
5660 case 0x15c3: // (blue)
5661 element = EL_CHAR_AUMLAUT;
5664 case 0x15c4: // (blue)
5665 element = EL_CHAR_OUMLAUT;
5668 case 0x15c5: // (blue)
5669 element = EL_CHAR_UUMLAUT;
5672 case 0x15c6: // (blue)
5673 element = EL_CHAR_0;
5676 case 0x15c7: // (blue)
5677 element = EL_CHAR_1;
5680 case 0x15c8: // (blue)
5681 element = EL_CHAR_2;
5684 case 0x15c9: // (blue)
5685 element = EL_CHAR_3;
5688 case 0x15ca: // (blue)
5689 element = EL_CHAR_4;
5692 case 0x15cb: // (blue)
5693 element = EL_CHAR_5;
5696 case 0x15cc: // (blue)
5697 element = EL_CHAR_6;
5700 case 0x15cd: // (blue)
5701 element = EL_CHAR_7;
5704 case 0x15ce: // (blue)
5705 element = EL_CHAR_8;
5708 case 0x15cf: // (blue)
5709 element = EL_CHAR_9;
5712 case 0x15d0: // (blue)
5713 element = EL_CHAR_PERIOD;
5716 case 0x15d1: // (blue)
5717 element = EL_CHAR_EXCLAM;
5720 case 0x15d2: // (blue)
5721 element = EL_CHAR_COLON;
5724 case 0x15d3: // (blue)
5725 element = EL_CHAR_LESS;
5728 case 0x15d4: // (blue)
5729 element = EL_CHAR_GREATER;
5732 case 0x15d5: // (blue)
5733 element = EL_CHAR_QUESTION;
5736 case 0x15d6: // (blue)
5737 element = EL_CHAR_COPYRIGHT;
5740 case 0x15d7: // (blue)
5741 element = EL_CHAR_UP;
5744 case 0x15d8: // (blue)
5745 element = EL_CHAR_DOWN;
5748 case 0x15d9: // (blue)
5749 element = EL_CHAR_BUTTON;
5752 case 0x15da: // (blue)
5753 element = EL_CHAR_PLUS;
5756 case 0x15db: // (blue)
5757 element = EL_CHAR_MINUS;
5760 case 0x15dc: // (blue)
5761 element = EL_CHAR_APOSTROPHE;
5764 case 0x15dd: // (blue)
5765 element = EL_CHAR_PARENLEFT;
5768 case 0x15de: // (blue)
5769 element = EL_CHAR_PARENRIGHT;
5772 case 0x15df: // (green)
5773 element = EL_CHAR_A;
5776 case 0x15e0: // (green)
5777 element = EL_CHAR_B;
5780 case 0x15e1: // (green)
5781 element = EL_CHAR_C;
5784 case 0x15e2: // (green)
5785 element = EL_CHAR_D;
5788 case 0x15e3: // (green)
5789 element = EL_CHAR_E;
5792 case 0x15e4: // (green)
5793 element = EL_CHAR_F;
5796 case 0x15e5: // (green)
5797 element = EL_CHAR_G;
5800 case 0x15e6: // (green)
5801 element = EL_CHAR_H;
5804 case 0x15e7: // (green)
5805 element = EL_CHAR_I;
5808 case 0x15e8: // (green)
5809 element = EL_CHAR_J;
5812 case 0x15e9: // (green)
5813 element = EL_CHAR_K;
5816 case 0x15ea: // (green)
5817 element = EL_CHAR_L;
5820 case 0x15eb: // (green)
5821 element = EL_CHAR_M;
5824 case 0x15ec: // (green)
5825 element = EL_CHAR_N;
5828 case 0x15ed: // (green)
5829 element = EL_CHAR_O;
5832 case 0x15ee: // (green)
5833 element = EL_CHAR_P;
5836 case 0x15ef: // (green)
5837 element = EL_CHAR_Q;
5840 case 0x15f0: // (green)
5841 element = EL_CHAR_R;
5844 case 0x15f1: // (green)
5845 element = EL_CHAR_S;
5848 case 0x15f2: // (green)
5849 element = EL_CHAR_T;
5852 case 0x15f3: // (green)
5853 element = EL_CHAR_U;
5856 case 0x15f4: // (green)
5857 element = EL_CHAR_V;
5860 case 0x15f5: // (green)
5861 element = EL_CHAR_W;
5864 case 0x15f6: // (green)
5865 element = EL_CHAR_X;
5868 case 0x15f7: // (green)
5869 element = EL_CHAR_Y;
5872 case 0x15f8: // (green)
5873 element = EL_CHAR_Z;
5876 case 0x15f9: // (green)
5877 element = EL_CHAR_AUMLAUT;
5880 case 0x15fa: // (green)
5881 element = EL_CHAR_OUMLAUT;
5884 case 0x15fb: // (green)
5885 element = EL_CHAR_UUMLAUT;
5888 case 0x15fc: // (green)
5889 element = EL_CHAR_0;
5892 case 0x15fd: // (green)
5893 element = EL_CHAR_1;
5896 case 0x15fe: // (green)
5897 element = EL_CHAR_2;
5900 case 0x15ff: // (green)
5901 element = EL_CHAR_3;
5904 case 0x1600: // (green)
5905 element = EL_CHAR_4;
5908 case 0x1601: // (green)
5909 element = EL_CHAR_5;
5912 case 0x1602: // (green)
5913 element = EL_CHAR_6;
5916 case 0x1603: // (green)
5917 element = EL_CHAR_7;
5920 case 0x1604: // (green)
5921 element = EL_CHAR_8;
5924 case 0x1605: // (green)
5925 element = EL_CHAR_9;
5928 case 0x1606: // (green)
5929 element = EL_CHAR_PERIOD;
5932 case 0x1607: // (green)
5933 element = EL_CHAR_EXCLAM;
5936 case 0x1608: // (green)
5937 element = EL_CHAR_COLON;
5940 case 0x1609: // (green)
5941 element = EL_CHAR_LESS;
5944 case 0x160a: // (green)
5945 element = EL_CHAR_GREATER;
5948 case 0x160b: // (green)
5949 element = EL_CHAR_QUESTION;
5952 case 0x160c: // (green)
5953 element = EL_CHAR_COPYRIGHT;
5956 case 0x160d: // (green)
5957 element = EL_CHAR_UP;
5960 case 0x160e: // (green)
5961 element = EL_CHAR_DOWN;
5964 case 0x160f: // (green)
5965 element = EL_CHAR_BUTTON;
5968 case 0x1610: // (green)
5969 element = EL_CHAR_PLUS;
5972 case 0x1611: // (green)
5973 element = EL_CHAR_MINUS;
5976 case 0x1612: // (green)
5977 element = EL_CHAR_APOSTROPHE;
5980 case 0x1613: // (green)
5981 element = EL_CHAR_PARENLEFT;
5984 case 0x1614: // (green)
5985 element = EL_CHAR_PARENRIGHT;
5988 case 0x1615: // (blue steel)
5989 element = EL_STEEL_CHAR_A;
5992 case 0x1616: // (blue steel)
5993 element = EL_STEEL_CHAR_B;
5996 case 0x1617: // (blue steel)
5997 element = EL_STEEL_CHAR_C;
6000 case 0x1618: // (blue steel)
6001 element = EL_STEEL_CHAR_D;
6004 case 0x1619: // (blue steel)
6005 element = EL_STEEL_CHAR_E;
6008 case 0x161a: // (blue steel)
6009 element = EL_STEEL_CHAR_F;
6012 case 0x161b: // (blue steel)
6013 element = EL_STEEL_CHAR_G;
6016 case 0x161c: // (blue steel)
6017 element = EL_STEEL_CHAR_H;
6020 case 0x161d: // (blue steel)
6021 element = EL_STEEL_CHAR_I;
6024 case 0x161e: // (blue steel)
6025 element = EL_STEEL_CHAR_J;
6028 case 0x161f: // (blue steel)
6029 element = EL_STEEL_CHAR_K;
6032 case 0x1620: // (blue steel)
6033 element = EL_STEEL_CHAR_L;
6036 case 0x1621: // (blue steel)
6037 element = EL_STEEL_CHAR_M;
6040 case 0x1622: // (blue steel)
6041 element = EL_STEEL_CHAR_N;
6044 case 0x1623: // (blue steel)
6045 element = EL_STEEL_CHAR_O;
6048 case 0x1624: // (blue steel)
6049 element = EL_STEEL_CHAR_P;
6052 case 0x1625: // (blue steel)
6053 element = EL_STEEL_CHAR_Q;
6056 case 0x1626: // (blue steel)
6057 element = EL_STEEL_CHAR_R;
6060 case 0x1627: // (blue steel)
6061 element = EL_STEEL_CHAR_S;
6064 case 0x1628: // (blue steel)
6065 element = EL_STEEL_CHAR_T;
6068 case 0x1629: // (blue steel)
6069 element = EL_STEEL_CHAR_U;
6072 case 0x162a: // (blue steel)
6073 element = EL_STEEL_CHAR_V;
6076 case 0x162b: // (blue steel)
6077 element = EL_STEEL_CHAR_W;
6080 case 0x162c: // (blue steel)
6081 element = EL_STEEL_CHAR_X;
6084 case 0x162d: // (blue steel)
6085 element = EL_STEEL_CHAR_Y;
6088 case 0x162e: // (blue steel)
6089 element = EL_STEEL_CHAR_Z;
6092 case 0x162f: // (blue steel)
6093 element = EL_STEEL_CHAR_AUMLAUT;
6096 case 0x1630: // (blue steel)
6097 element = EL_STEEL_CHAR_OUMLAUT;
6100 case 0x1631: // (blue steel)
6101 element = EL_STEEL_CHAR_UUMLAUT;
6104 case 0x1632: // (blue steel)
6105 element = EL_STEEL_CHAR_0;
6108 case 0x1633: // (blue steel)
6109 element = EL_STEEL_CHAR_1;
6112 case 0x1634: // (blue steel)
6113 element = EL_STEEL_CHAR_2;
6116 case 0x1635: // (blue steel)
6117 element = EL_STEEL_CHAR_3;
6120 case 0x1636: // (blue steel)
6121 element = EL_STEEL_CHAR_4;
6124 case 0x1637: // (blue steel)
6125 element = EL_STEEL_CHAR_5;
6128 case 0x1638: // (blue steel)
6129 element = EL_STEEL_CHAR_6;
6132 case 0x1639: // (blue steel)
6133 element = EL_STEEL_CHAR_7;
6136 case 0x163a: // (blue steel)
6137 element = EL_STEEL_CHAR_8;
6140 case 0x163b: // (blue steel)
6141 element = EL_STEEL_CHAR_9;
6144 case 0x163c: // (blue steel)
6145 element = EL_STEEL_CHAR_PERIOD;
6148 case 0x163d: // (blue steel)
6149 element = EL_STEEL_CHAR_EXCLAM;
6152 case 0x163e: // (blue steel)
6153 element = EL_STEEL_CHAR_COLON;
6156 case 0x163f: // (blue steel)
6157 element = EL_STEEL_CHAR_LESS;
6160 case 0x1640: // (blue steel)
6161 element = EL_STEEL_CHAR_GREATER;
6164 case 0x1641: // (blue steel)
6165 element = EL_STEEL_CHAR_QUESTION;
6168 case 0x1642: // (blue steel)
6169 element = EL_STEEL_CHAR_COPYRIGHT;
6172 case 0x1643: // (blue steel)
6173 element = EL_STEEL_CHAR_UP;
6176 case 0x1644: // (blue steel)
6177 element = EL_STEEL_CHAR_DOWN;
6180 case 0x1645: // (blue steel)
6181 element = EL_STEEL_CHAR_BUTTON;
6184 case 0x1646: // (blue steel)
6185 element = EL_STEEL_CHAR_PLUS;
6188 case 0x1647: // (blue steel)
6189 element = EL_STEEL_CHAR_MINUS;
6192 case 0x1648: // (blue steel)
6193 element = EL_STEEL_CHAR_APOSTROPHE;
6196 case 0x1649: // (blue steel)
6197 element = EL_STEEL_CHAR_PARENLEFT;
6200 case 0x164a: // (blue steel)
6201 element = EL_STEEL_CHAR_PARENRIGHT;
6204 case 0x164b: // (green steel)
6205 element = EL_STEEL_CHAR_A;
6208 case 0x164c: // (green steel)
6209 element = EL_STEEL_CHAR_B;
6212 case 0x164d: // (green steel)
6213 element = EL_STEEL_CHAR_C;
6216 case 0x164e: // (green steel)
6217 element = EL_STEEL_CHAR_D;
6220 case 0x164f: // (green steel)
6221 element = EL_STEEL_CHAR_E;
6224 case 0x1650: // (green steel)
6225 element = EL_STEEL_CHAR_F;
6228 case 0x1651: // (green steel)
6229 element = EL_STEEL_CHAR_G;
6232 case 0x1652: // (green steel)
6233 element = EL_STEEL_CHAR_H;
6236 case 0x1653: // (green steel)
6237 element = EL_STEEL_CHAR_I;
6240 case 0x1654: // (green steel)
6241 element = EL_STEEL_CHAR_J;
6244 case 0x1655: // (green steel)
6245 element = EL_STEEL_CHAR_K;
6248 case 0x1656: // (green steel)
6249 element = EL_STEEL_CHAR_L;
6252 case 0x1657: // (green steel)
6253 element = EL_STEEL_CHAR_M;
6256 case 0x1658: // (green steel)
6257 element = EL_STEEL_CHAR_N;
6260 case 0x1659: // (green steel)
6261 element = EL_STEEL_CHAR_O;
6264 case 0x165a: // (green steel)
6265 element = EL_STEEL_CHAR_P;
6268 case 0x165b: // (green steel)
6269 element = EL_STEEL_CHAR_Q;
6272 case 0x165c: // (green steel)
6273 element = EL_STEEL_CHAR_R;
6276 case 0x165d: // (green steel)
6277 element = EL_STEEL_CHAR_S;
6280 case 0x165e: // (green steel)
6281 element = EL_STEEL_CHAR_T;
6284 case 0x165f: // (green steel)
6285 element = EL_STEEL_CHAR_U;
6288 case 0x1660: // (green steel)
6289 element = EL_STEEL_CHAR_V;
6292 case 0x1661: // (green steel)
6293 element = EL_STEEL_CHAR_W;
6296 case 0x1662: // (green steel)
6297 element = EL_STEEL_CHAR_X;
6300 case 0x1663: // (green steel)
6301 element = EL_STEEL_CHAR_Y;
6304 case 0x1664: // (green steel)
6305 element = EL_STEEL_CHAR_Z;
6308 case 0x1665: // (green steel)
6309 element = EL_STEEL_CHAR_AUMLAUT;
6312 case 0x1666: // (green steel)
6313 element = EL_STEEL_CHAR_OUMLAUT;
6316 case 0x1667: // (green steel)
6317 element = EL_STEEL_CHAR_UUMLAUT;
6320 case 0x1668: // (green steel)
6321 element = EL_STEEL_CHAR_0;
6324 case 0x1669: // (green steel)
6325 element = EL_STEEL_CHAR_1;
6328 case 0x166a: // (green steel)
6329 element = EL_STEEL_CHAR_2;
6332 case 0x166b: // (green steel)
6333 element = EL_STEEL_CHAR_3;
6336 case 0x166c: // (green steel)
6337 element = EL_STEEL_CHAR_4;
6340 case 0x166d: // (green steel)
6341 element = EL_STEEL_CHAR_5;
6344 case 0x166e: // (green steel)
6345 element = EL_STEEL_CHAR_6;
6348 case 0x166f: // (green steel)
6349 element = EL_STEEL_CHAR_7;
6352 case 0x1670: // (green steel)
6353 element = EL_STEEL_CHAR_8;
6356 case 0x1671: // (green steel)
6357 element = EL_STEEL_CHAR_9;
6360 case 0x1672: // (green steel)
6361 element = EL_STEEL_CHAR_PERIOD;
6364 case 0x1673: // (green steel)
6365 element = EL_STEEL_CHAR_EXCLAM;
6368 case 0x1674: // (green steel)
6369 element = EL_STEEL_CHAR_COLON;
6372 case 0x1675: // (green steel)
6373 element = EL_STEEL_CHAR_LESS;
6376 case 0x1676: // (green steel)
6377 element = EL_STEEL_CHAR_GREATER;
6380 case 0x1677: // (green steel)
6381 element = EL_STEEL_CHAR_QUESTION;
6384 case 0x1678: // (green steel)
6385 element = EL_STEEL_CHAR_COPYRIGHT;
6388 case 0x1679: // (green steel)
6389 element = EL_STEEL_CHAR_UP;
6392 case 0x167a: // (green steel)
6393 element = EL_STEEL_CHAR_DOWN;
6396 case 0x167b: // (green steel)
6397 element = EL_STEEL_CHAR_BUTTON;
6400 case 0x167c: // (green steel)
6401 element = EL_STEEL_CHAR_PLUS;
6404 case 0x167d: // (green steel)
6405 element = EL_STEEL_CHAR_MINUS;
6408 case 0x167e: // (green steel)
6409 element = EL_STEEL_CHAR_APOSTROPHE;
6412 case 0x167f: // (green steel)
6413 element = EL_STEEL_CHAR_PARENLEFT;
6416 case 0x1680: // (green steel)
6417 element = EL_STEEL_CHAR_PARENRIGHT;
6420 case 0x1681: // gate (red)
6421 element = EL_EM_GATE_1;
6424 case 0x1682: // secret gate (red)
6425 element = EL_EM_GATE_1_GRAY;
6428 case 0x1683: // gate (yellow)
6429 element = EL_EM_GATE_2;
6432 case 0x1684: // secret gate (yellow)
6433 element = EL_EM_GATE_2_GRAY;
6436 case 0x1685: // gate (blue)
6437 element = EL_EM_GATE_4;
6440 case 0x1686: // secret gate (blue)
6441 element = EL_EM_GATE_4_GRAY;
6444 case 0x1687: // gate (green)
6445 element = EL_EM_GATE_3;
6448 case 0x1688: // secret gate (green)
6449 element = EL_EM_GATE_3_GRAY;
6452 case 0x1689: // gate (white)
6453 element = EL_DC_GATE_WHITE;
6456 case 0x168a: // secret gate (white)
6457 element = EL_DC_GATE_WHITE_GRAY;
6460 case 0x168b: // secret gate (no key)
6461 element = EL_DC_GATE_FAKE_GRAY;
6465 element = EL_ROBOT_WHEEL;
6469 element = EL_DC_TIMEGATE_SWITCH;
6473 element = EL_ACID_POOL_BOTTOM;
6477 element = EL_ACID_POOL_TOPLEFT;
6481 element = EL_ACID_POOL_TOPRIGHT;
6485 element = EL_ACID_POOL_BOTTOMLEFT;
6489 element = EL_ACID_POOL_BOTTOMRIGHT;
6493 element = EL_STEELWALL;
6497 element = EL_STEELWALL_SLIPPERY;
6500 case 0x1695: // steel wall (not round)
6501 element = EL_STEELWALL;
6504 case 0x1696: // steel wall (left)
6505 element = EL_DC_STEELWALL_1_LEFT;
6508 case 0x1697: // steel wall (bottom)
6509 element = EL_DC_STEELWALL_1_BOTTOM;
6512 case 0x1698: // steel wall (right)
6513 element = EL_DC_STEELWALL_1_RIGHT;
6516 case 0x1699: // steel wall (top)
6517 element = EL_DC_STEELWALL_1_TOP;
6520 case 0x169a: // steel wall (left/bottom)
6521 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6524 case 0x169b: // steel wall (right/bottom)
6525 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6528 case 0x169c: // steel wall (right/top)
6529 element = EL_DC_STEELWALL_1_TOPRIGHT;
6532 case 0x169d: // steel wall (left/top)
6533 element = EL_DC_STEELWALL_1_TOPLEFT;
6536 case 0x169e: // steel wall (right/bottom small)
6537 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6540 case 0x169f: // steel wall (left/bottom small)
6541 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6544 case 0x16a0: // steel wall (right/top small)
6545 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6548 case 0x16a1: // steel wall (left/top small)
6549 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6552 case 0x16a2: // steel wall (left/right)
6553 element = EL_DC_STEELWALL_1_VERTICAL;
6556 case 0x16a3: // steel wall (top/bottom)
6557 element = EL_DC_STEELWALL_1_HORIZONTAL;
6560 case 0x16a4: // steel wall 2 (left end)
6561 element = EL_DC_STEELWALL_2_LEFT;
6564 case 0x16a5: // steel wall 2 (right end)
6565 element = EL_DC_STEELWALL_2_RIGHT;
6568 case 0x16a6: // steel wall 2 (top end)
6569 element = EL_DC_STEELWALL_2_TOP;
6572 case 0x16a7: // steel wall 2 (bottom end)
6573 element = EL_DC_STEELWALL_2_BOTTOM;
6576 case 0x16a8: // steel wall 2 (left/right)
6577 element = EL_DC_STEELWALL_2_HORIZONTAL;
6580 case 0x16a9: // steel wall 2 (up/down)
6581 element = EL_DC_STEELWALL_2_VERTICAL;
6584 case 0x16aa: // steel wall 2 (mid)
6585 element = EL_DC_STEELWALL_2_MIDDLE;
6589 element = EL_SIGN_EXCLAMATION;
6593 element = EL_SIGN_RADIOACTIVITY;
6597 element = EL_SIGN_STOP;
6601 element = EL_SIGN_WHEELCHAIR;
6605 element = EL_SIGN_PARKING;
6609 element = EL_SIGN_NO_ENTRY;
6613 element = EL_SIGN_HEART;
6617 element = EL_SIGN_GIVE_WAY;
6621 element = EL_SIGN_ENTRY_FORBIDDEN;
6625 element = EL_SIGN_EMERGENCY_EXIT;
6629 element = EL_SIGN_YIN_YANG;
6633 element = EL_WALL_EMERALD;
6637 element = EL_WALL_DIAMOND;
6641 element = EL_WALL_PEARL;
6645 element = EL_WALL_CRYSTAL;
6649 element = EL_INVISIBLE_WALL;
6653 element = EL_INVISIBLE_STEELWALL;
6657 // EL_INVISIBLE_SAND
6660 element = EL_LIGHT_SWITCH;
6664 element = EL_ENVELOPE_1;
6668 if (element >= 0x0117 && element <= 0x036e) // (?)
6669 element = EL_DIAMOND;
6670 else if (element >= 0x042d && element <= 0x0684) // (?)
6671 element = EL_EMERALD;
6672 else if (element >= 0x157c && element <= 0x158b)
6674 else if (element >= 0x1590 && element <= 0x159f)
6675 element = EL_DC_LANDMINE;
6676 else if (element >= 0x16bc && element <= 0x16cb)
6677 element = EL_INVISIBLE_SAND;
6680 Warn("unknown Diamond Caves element 0x%04x", element);
6682 element = EL_UNKNOWN;
6687 return getMappedElement(element);
6690 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6692 byte header[DC_LEVEL_HEADER_SIZE];
6694 int envelope_header_pos = 62;
6695 int envelope_content_pos = 94;
6696 int level_name_pos = 251;
6697 int level_author_pos = 292;
6698 int envelope_header_len;
6699 int envelope_content_len;
6701 int level_author_len;
6703 int num_yamyam_contents;
6706 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6708 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6710 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6712 header[i * 2 + 0] = header_word >> 8;
6713 header[i * 2 + 1] = header_word & 0xff;
6716 // read some values from level header to check level decoding integrity
6717 fieldx = header[6] | (header[7] << 8);
6718 fieldy = header[8] | (header[9] << 8);
6719 num_yamyam_contents = header[60] | (header[61] << 8);
6721 // do some simple sanity checks to ensure that level was correctly decoded
6722 if (fieldx < 1 || fieldx > 256 ||
6723 fieldy < 1 || fieldy > 256 ||
6724 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6726 level->no_valid_file = TRUE;
6728 Warn("cannot decode level from stream -- using empty level");
6733 // maximum envelope header size is 31 bytes
6734 envelope_header_len = header[envelope_header_pos];
6735 // maximum envelope content size is 110 (156?) bytes
6736 envelope_content_len = header[envelope_content_pos];
6738 // maximum level title size is 40 bytes
6739 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6740 // maximum level author size is 30 (51?) bytes
6741 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6745 for (i = 0; i < envelope_header_len; i++)
6746 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6747 level->envelope[0].text[envelope_size++] =
6748 header[envelope_header_pos + 1 + i];
6750 if (envelope_header_len > 0 && envelope_content_len > 0)
6752 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6753 level->envelope[0].text[envelope_size++] = '\n';
6754 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6755 level->envelope[0].text[envelope_size++] = '\n';
6758 for (i = 0; i < envelope_content_len; i++)
6759 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6760 level->envelope[0].text[envelope_size++] =
6761 header[envelope_content_pos + 1 + i];
6763 level->envelope[0].text[envelope_size] = '\0';
6765 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6766 level->envelope[0].ysize = 10;
6767 level->envelope[0].autowrap = TRUE;
6768 level->envelope[0].centered = TRUE;
6770 for (i = 0; i < level_name_len; i++)
6771 level->name[i] = header[level_name_pos + 1 + i];
6772 level->name[level_name_len] = '\0';
6774 for (i = 0; i < level_author_len; i++)
6775 level->author[i] = header[level_author_pos + 1 + i];
6776 level->author[level_author_len] = '\0';
6778 num_yamyam_contents = header[60] | (header[61] << 8);
6779 level->num_yamyam_contents =
6780 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6782 for (i = 0; i < num_yamyam_contents; i++)
6784 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6786 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6787 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6789 if (i < MAX_ELEMENT_CONTENTS)
6790 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6794 fieldx = header[6] | (header[7] << 8);
6795 fieldy = header[8] | (header[9] << 8);
6796 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6797 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6799 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6801 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6802 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6804 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6805 level->field[x][y] = getMappedElement_DC(element_dc);
6808 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6809 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6810 level->field[x][y] = EL_PLAYER_1;
6812 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6813 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6814 level->field[x][y] = EL_PLAYER_2;
6816 level->gems_needed = header[18] | (header[19] << 8);
6818 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6819 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6820 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6821 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6822 level->score[SC_NUT] = header[28] | (header[29] << 8);
6823 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6824 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6825 level->score[SC_BUG] = header[34] | (header[35] << 8);
6826 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6827 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6828 level->score[SC_KEY] = header[40] | (header[41] << 8);
6829 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6831 level->time = header[44] | (header[45] << 8);
6833 level->amoeba_speed = header[46] | (header[47] << 8);
6834 level->time_light = header[48] | (header[49] << 8);
6835 level->time_timegate = header[50] | (header[51] << 8);
6836 level->time_wheel = header[52] | (header[53] << 8);
6837 level->time_magic_wall = header[54] | (header[55] << 8);
6838 level->extra_time = header[56] | (header[57] << 8);
6839 level->shield_normal_time = header[58] | (header[59] << 8);
6841 // shield and extra time elements do not have a score
6842 level->score[SC_SHIELD] = 0;
6843 level->extra_time_score = 0;
6845 // set time for normal and deadly shields to the same value
6846 level->shield_deadly_time = level->shield_normal_time;
6848 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6849 // can slip down from flat walls, like normal walls and steel walls
6850 level->em_slippery_gems = TRUE;
6852 // time score is counted for each 10 seconds left in Diamond Caves levels
6853 level->time_score_base = 10;
6856 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6857 struct LevelFileInfo *level_file_info,
6858 boolean level_info_only)
6860 char *filename = level_file_info->filename;
6862 int num_magic_bytes = 8;
6863 char magic_bytes[num_magic_bytes + 1];
6864 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6866 if (!(file = openFile(filename, MODE_READ)))
6868 level->no_valid_file = TRUE;
6870 if (!level_info_only)
6871 Warn("cannot read level '%s' -- using empty level", filename);
6876 // fseek(file, 0x0000, SEEK_SET);
6878 if (level_file_info->packed)
6880 // read "magic bytes" from start of file
6881 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6882 magic_bytes[0] = '\0';
6884 // check "magic bytes" for correct file format
6885 if (!strPrefix(magic_bytes, "DC2"))
6887 level->no_valid_file = TRUE;
6889 Warn("unknown DC level file '%s' -- using empty level", filename);
6894 if (strPrefix(magic_bytes, "DC2Win95") ||
6895 strPrefix(magic_bytes, "DC2Win98"))
6897 int position_first_level = 0x00fa;
6898 int extra_bytes = 4;
6901 // advance file stream to first level inside the level package
6902 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6904 // each block of level data is followed by block of non-level data
6905 num_levels_to_skip *= 2;
6907 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6908 while (num_levels_to_skip >= 0)
6910 // advance file stream to next level inside the level package
6911 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6913 level->no_valid_file = TRUE;
6915 Warn("cannot fseek in file '%s' -- using empty level", filename);
6920 // skip apparently unused extra bytes following each level
6921 ReadUnusedBytesFromFile(file, extra_bytes);
6923 // read size of next level in level package
6924 skip_bytes = getFile32BitLE(file);
6926 num_levels_to_skip--;
6931 level->no_valid_file = TRUE;
6933 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6939 LoadLevelFromFileStream_DC(file, level);
6945 // ----------------------------------------------------------------------------
6946 // functions for loading SB level
6947 // ----------------------------------------------------------------------------
6949 int getMappedElement_SB(int element_ascii, boolean use_ces)
6957 sb_element_mapping[] =
6959 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6960 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6961 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6962 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6963 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6964 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6965 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6966 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6973 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6974 if (element_ascii == sb_element_mapping[i].ascii)
6975 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6977 return EL_UNDEFINED;
6980 static void SetLevelSettings_SB(struct LevelInfo *level)
6984 level->use_step_counter = TRUE;
6987 level->score[SC_TIME_BONUS] = 0;
6988 level->time_score_base = 1;
6989 level->rate_time_over_score = TRUE;
6992 level->auto_exit_sokoban = TRUE;
6995 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6996 struct LevelFileInfo *level_file_info,
6997 boolean level_info_only)
6999 char *filename = level_file_info->filename;
7000 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7001 char last_comment[MAX_LINE_LEN];
7002 char level_name[MAX_LINE_LEN];
7005 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7006 boolean read_continued_line = FALSE;
7007 boolean reading_playfield = FALSE;
7008 boolean got_valid_playfield_line = FALSE;
7009 boolean invalid_playfield_char = FALSE;
7010 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7011 int file_level_nr = 0;
7012 int x = 0, y = 0; // initialized to make compilers happy
7014 last_comment[0] = '\0';
7015 level_name[0] = '\0';
7017 if (!(file = openFile(filename, MODE_READ)))
7019 level->no_valid_file = TRUE;
7021 if (!level_info_only)
7022 Warn("cannot read level '%s' -- using empty level", filename);
7027 while (!checkEndOfFile(file))
7029 // level successfully read, but next level may follow here
7030 if (!got_valid_playfield_line && reading_playfield)
7032 // read playfield from single level file -- skip remaining file
7033 if (!level_file_info->packed)
7036 if (file_level_nr >= num_levels_to_skip)
7041 last_comment[0] = '\0';
7042 level_name[0] = '\0';
7044 reading_playfield = FALSE;
7047 got_valid_playfield_line = FALSE;
7049 // read next line of input file
7050 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7053 // cut trailing line break (this can be newline and/or carriage return)
7054 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7055 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7058 // copy raw input line for later use (mainly debugging output)
7059 strcpy(line_raw, line);
7061 if (read_continued_line)
7063 // append new line to existing line, if there is enough space
7064 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7065 strcat(previous_line, line_ptr);
7067 strcpy(line, previous_line); // copy storage buffer to line
7069 read_continued_line = FALSE;
7072 // if the last character is '\', continue at next line
7073 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7075 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7076 strcpy(previous_line, line); // copy line to storage buffer
7078 read_continued_line = TRUE;
7084 if (line[0] == '\0')
7087 // extract comment text from comment line
7090 for (line_ptr = line; *line_ptr; line_ptr++)
7091 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7094 strcpy(last_comment, line_ptr);
7099 // extract level title text from line containing level title
7100 if (line[0] == '\'')
7102 strcpy(level_name, &line[1]);
7104 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7105 level_name[strlen(level_name) - 1] = '\0';
7110 // skip lines containing only spaces (or empty lines)
7111 for (line_ptr = line; *line_ptr; line_ptr++)
7112 if (*line_ptr != ' ')
7114 if (*line_ptr == '\0')
7117 // at this point, we have found a line containing part of a playfield
7119 got_valid_playfield_line = TRUE;
7121 if (!reading_playfield)
7123 reading_playfield = TRUE;
7124 invalid_playfield_char = FALSE;
7126 for (x = 0; x < MAX_LEV_FIELDX; x++)
7127 for (y = 0; y < MAX_LEV_FIELDY; y++)
7128 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7133 // start with topmost tile row
7137 // skip playfield line if larger row than allowed
7138 if (y >= MAX_LEV_FIELDY)
7141 // start with leftmost tile column
7144 // read playfield elements from line
7145 for (line_ptr = line; *line_ptr; line_ptr++)
7147 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7149 // stop parsing playfield line if larger column than allowed
7150 if (x >= MAX_LEV_FIELDX)
7153 if (mapped_sb_element == EL_UNDEFINED)
7155 invalid_playfield_char = TRUE;
7160 level->field[x][y] = mapped_sb_element;
7162 // continue with next tile column
7165 level->fieldx = MAX(x, level->fieldx);
7168 if (invalid_playfield_char)
7170 // if first playfield line, treat invalid lines as comment lines
7172 reading_playfield = FALSE;
7177 // continue with next tile row
7185 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7186 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7188 if (!reading_playfield)
7190 level->no_valid_file = TRUE;
7192 Warn("cannot read level '%s' -- using empty level", filename);
7197 if (*level_name != '\0')
7199 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7200 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7202 else if (*last_comment != '\0')
7204 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7205 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7209 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7212 // set all empty fields beyond the border walls to invisible steel wall
7213 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7215 if ((x == 0 || x == level->fieldx - 1 ||
7216 y == 0 || y == level->fieldy - 1) &&
7217 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7218 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7219 level->field, level->fieldx, level->fieldy);
7222 // set special level settings for Sokoban levels
7223 SetLevelSettings_SB(level);
7225 if (load_xsb_to_ces)
7227 // special global settings can now be set in level template
7228 level->use_custom_template = TRUE;
7233 // -------------------------------------------------------------------------
7234 // functions for handling native levels
7235 // -------------------------------------------------------------------------
7237 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7238 struct LevelFileInfo *level_file_info,
7239 boolean level_info_only)
7243 // determine position of requested level inside level package
7244 if (level_file_info->packed)
7245 pos = level_file_info->nr - leveldir_current->first_level;
7247 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7248 level->no_valid_file = TRUE;
7251 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7252 struct LevelFileInfo *level_file_info,
7253 boolean level_info_only)
7255 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7256 level->no_valid_file = TRUE;
7259 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7260 struct LevelFileInfo *level_file_info,
7261 boolean level_info_only)
7265 // determine position of requested level inside level package
7266 if (level_file_info->packed)
7267 pos = level_file_info->nr - leveldir_current->first_level;
7269 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7270 level->no_valid_file = TRUE;
7273 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7274 struct LevelFileInfo *level_file_info,
7275 boolean level_info_only)
7277 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7278 level->no_valid_file = TRUE;
7281 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7283 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7284 CopyNativeLevel_RND_to_BD(level);
7285 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7286 CopyNativeLevel_RND_to_EM(level);
7287 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7288 CopyNativeLevel_RND_to_SP(level);
7289 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7290 CopyNativeLevel_RND_to_MM(level);
7293 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7295 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7296 CopyNativeLevel_BD_to_RND(level);
7297 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7298 CopyNativeLevel_EM_to_RND(level);
7299 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7300 CopyNativeLevel_SP_to_RND(level);
7301 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7302 CopyNativeLevel_MM_to_RND(level);
7305 void SaveNativeLevel(struct LevelInfo *level)
7307 // saving native level files only supported for some game engines
7308 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7309 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7312 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7313 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7314 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7315 char *filename = getLevelFilenameFromBasename(basename);
7317 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7320 boolean success = FALSE;
7322 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7324 CopyNativeLevel_RND_to_BD(level);
7325 // CopyNativeTape_RND_to_BD(level);
7327 success = SaveNativeLevel_BD(filename);
7329 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7331 CopyNativeLevel_RND_to_SP(level);
7332 CopyNativeTape_RND_to_SP(level);
7334 success = SaveNativeLevel_SP(filename);
7338 Request("Native level file saved!", REQ_CONFIRM);
7340 Request("Failed to save native level file!", REQ_CONFIRM);
7344 // ----------------------------------------------------------------------------
7345 // functions for loading generic level
7346 // ----------------------------------------------------------------------------
7348 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7349 struct LevelFileInfo *level_file_info,
7350 boolean level_info_only)
7352 // always start with reliable default values
7353 setLevelInfoToDefaults(level, level_info_only, TRUE);
7355 switch (level_file_info->type)
7357 case LEVEL_FILE_TYPE_RND:
7358 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7361 case LEVEL_FILE_TYPE_BD:
7362 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7363 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7366 case LEVEL_FILE_TYPE_EM:
7367 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7368 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7371 case LEVEL_FILE_TYPE_SP:
7372 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7373 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7376 case LEVEL_FILE_TYPE_MM:
7377 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7378 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7381 case LEVEL_FILE_TYPE_DC:
7382 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7385 case LEVEL_FILE_TYPE_SB:
7386 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7390 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7394 // if level file is invalid, restore level structure to default values
7395 if (level->no_valid_file)
7396 setLevelInfoToDefaults(level, level_info_only, FALSE);
7398 if (check_special_flags("use_native_bd_game_engine"))
7399 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7401 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7402 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7404 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7405 CopyNativeLevel_Native_to_RND(level);
7408 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7410 static struct LevelFileInfo level_file_info;
7412 // always start with reliable default values
7413 setFileInfoToDefaults(&level_file_info);
7415 level_file_info.nr = 0; // unknown level number
7416 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7418 setString(&level_file_info.filename, filename);
7420 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7423 static void LoadLevel_InitVersion(struct LevelInfo *level)
7427 if (leveldir_current == NULL) // only when dumping level
7430 // all engine modifications also valid for levels which use latest engine
7431 if (level->game_version < VERSION_IDENT(3,2,0,5))
7433 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7434 level->time_score_base = 10;
7437 if (leveldir_current->latest_engine)
7439 // ---------- use latest game engine --------------------------------------
7441 /* For all levels which are forced to use the latest game engine version
7442 (normally all but user contributed, private and undefined levels), set
7443 the game engine version to the actual version; this allows for actual
7444 corrections in the game engine to take effect for existing, converted
7445 levels (from "classic" or other existing games) to make the emulation
7446 of the corresponding game more accurate, while (hopefully) not breaking
7447 existing levels created from other players. */
7449 level->game_version = GAME_VERSION_ACTUAL;
7451 /* Set special EM style gems behaviour: EM style gems slip down from
7452 normal, steel and growing wall. As this is a more fundamental change,
7453 it seems better to set the default behaviour to "off" (as it is more
7454 natural) and make it configurable in the level editor (as a property
7455 of gem style elements). Already existing converted levels (neither
7456 private nor contributed levels) are changed to the new behaviour. */
7458 if (level->file_version < FILE_VERSION_2_0)
7459 level->em_slippery_gems = TRUE;
7464 // ---------- use game engine the level was created with --------------------
7466 /* For all levels which are not forced to use the latest game engine
7467 version (normally user contributed, private and undefined levels),
7468 use the version of the game engine the levels were created for.
7470 Since 2.0.1, the game engine version is now directly stored
7471 in the level file (chunk "VERS"), so there is no need anymore
7472 to set the game version from the file version (except for old,
7473 pre-2.0 levels, where the game version is still taken from the
7474 file format version used to store the level -- see above). */
7476 // player was faster than enemies in 1.0.0 and before
7477 if (level->file_version == FILE_VERSION_1_0)
7478 for (i = 0; i < MAX_PLAYERS; i++)
7479 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7481 // default behaviour for EM style gems was "slippery" only in 2.0.1
7482 if (level->game_version == VERSION_IDENT(2,0,1,0))
7483 level->em_slippery_gems = TRUE;
7485 // springs could be pushed over pits before (pre-release version) 2.2.0
7486 if (level->game_version < VERSION_IDENT(2,2,0,0))
7487 level->use_spring_bug = TRUE;
7489 if (level->game_version < VERSION_IDENT(3,2,0,5))
7491 // time orb caused limited time in endless time levels before 3.2.0-5
7492 level->use_time_orb_bug = TRUE;
7494 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7495 level->block_snap_field = FALSE;
7497 // extra time score was same value as time left score before 3.2.0-5
7498 level->extra_time_score = level->score[SC_TIME_BONUS];
7501 if (level->game_version < VERSION_IDENT(3,2,0,7))
7503 // default behaviour for snapping was "not continuous" before 3.2.0-7
7504 level->continuous_snapping = FALSE;
7507 // only few elements were able to actively move into acid before 3.1.0
7508 // trigger settings did not exist before 3.1.0; set to default "any"
7509 if (level->game_version < VERSION_IDENT(3,1,0,0))
7511 // correct "can move into acid" settings (all zero in old levels)
7513 level->can_move_into_acid_bits = 0; // nothing can move into acid
7514 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7516 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7517 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7518 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7519 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7521 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7522 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7524 // correct trigger settings (stored as zero == "none" in old levels)
7526 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7528 int element = EL_CUSTOM_START + i;
7529 struct ElementInfo *ei = &element_info[element];
7531 for (j = 0; j < ei->num_change_pages; j++)
7533 struct ElementChangeInfo *change = &ei->change_page[j];
7535 change->trigger_player = CH_PLAYER_ANY;
7536 change->trigger_page = CH_PAGE_ANY;
7541 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7543 int element = EL_CUSTOM_256;
7544 struct ElementInfo *ei = &element_info[element];
7545 struct ElementChangeInfo *change = &ei->change_page[0];
7547 /* This is needed to fix a problem that was caused by a bugfix in function
7548 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7549 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7550 not replace walkable elements, but instead just placed the player on it,
7551 without placing the Sokoban field under the player). Unfortunately, this
7552 breaks "Snake Bite" style levels when the snake is halfway through a door
7553 that just closes (the snake head is still alive and can be moved in this
7554 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7555 player (without Sokoban element) which then gets killed as designed). */
7557 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7558 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7559 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7560 change->target_element = EL_PLAYER_1;
7563 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7564 if (level->game_version < VERSION_IDENT(3,2,5,0))
7566 /* This is needed to fix a problem that was caused by a bugfix in function
7567 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7568 corrects the behaviour when a custom element changes to another custom
7569 element with a higher element number that has change actions defined.
7570 Normally, only one change per frame is allowed for custom elements.
7571 Therefore, it is checked if a custom element already changed in the
7572 current frame; if it did, subsequent changes are suppressed.
7573 Unfortunately, this is only checked for element changes, but not for
7574 change actions, which are still executed. As the function above loops
7575 through all custom elements from lower to higher, an element change
7576 resulting in a lower CE number won't be checked again, while a target
7577 element with a higher number will also be checked, and potential change
7578 actions will get executed for this CE, too (which is wrong), while
7579 further changes are ignored (which is correct). As this bugfix breaks
7580 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7581 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7582 behaviour for existing levels and tapes that make use of this bug */
7584 level->use_action_after_change_bug = TRUE;
7587 // not centering level after relocating player was default only in 3.2.3
7588 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7589 level->shifted_relocation = TRUE;
7591 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7592 if (level->game_version < VERSION_IDENT(3,2,6,0))
7593 level->em_explodes_by_fire = TRUE;
7595 // levels were solved by the first player entering an exit up to 4.1.0.0
7596 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7597 level->solved_by_one_player = TRUE;
7599 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7600 if (level->game_version < VERSION_IDENT(4,1,1,1))
7601 level->use_life_bugs = TRUE;
7603 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7604 if (level->game_version < VERSION_IDENT(4,1,1,1))
7605 level->sb_objects_needed = FALSE;
7607 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7608 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7609 level->finish_dig_collect = FALSE;
7611 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7612 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7613 level->keep_walkable_ce = TRUE;
7616 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7618 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7621 // check if this level is (not) a Sokoban level
7622 for (y = 0; y < level->fieldy; y++)
7623 for (x = 0; x < level->fieldx; x++)
7624 if (!IS_SB_ELEMENT(Tile[x][y]))
7625 is_sokoban_level = FALSE;
7627 if (is_sokoban_level)
7629 // set special level settings for Sokoban levels
7630 SetLevelSettings_SB(level);
7634 static void LoadLevel_InitSettings(struct LevelInfo *level)
7636 // adjust level settings for (non-native) Sokoban-style levels
7637 LoadLevel_InitSettings_SB(level);
7639 // rename levels with title "nameless level" or if renaming is forced
7640 if (leveldir_current->empty_level_name != NULL &&
7641 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7642 leveldir_current->force_level_name))
7643 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7644 leveldir_current->empty_level_name, level_nr);
7647 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7651 // map elements that have changed in newer versions
7652 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7653 level->game_version);
7654 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7655 for (x = 0; x < 3; x++)
7656 for (y = 0; y < 3; y++)
7657 level->yamyam_content[i].e[x][y] =
7658 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7659 level->game_version);
7663 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7667 // map custom element change events that have changed in newer versions
7668 // (these following values were accidentally changed in version 3.0.1)
7669 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7670 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7672 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7674 int element = EL_CUSTOM_START + i;
7676 // order of checking and copying events to be mapped is important
7677 // (do not change the start and end value -- they are constant)
7678 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7680 if (HAS_CHANGE_EVENT(element, j - 2))
7682 SET_CHANGE_EVENT(element, j - 2, FALSE);
7683 SET_CHANGE_EVENT(element, j, TRUE);
7687 // order of checking and copying events to be mapped is important
7688 // (do not change the start and end value -- they are constant)
7689 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7691 if (HAS_CHANGE_EVENT(element, j - 1))
7693 SET_CHANGE_EVENT(element, j - 1, FALSE);
7694 SET_CHANGE_EVENT(element, j, TRUE);
7700 // initialize "can_change" field for old levels with only one change page
7701 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7703 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7705 int element = EL_CUSTOM_START + i;
7707 if (CAN_CHANGE(element))
7708 element_info[element].change->can_change = TRUE;
7712 // correct custom element values (for old levels without these options)
7713 if (level->game_version < VERSION_IDENT(3,1,1,0))
7715 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7717 int element = EL_CUSTOM_START + i;
7718 struct ElementInfo *ei = &element_info[element];
7720 if (ei->access_direction == MV_NO_DIRECTION)
7721 ei->access_direction = MV_ALL_DIRECTIONS;
7725 // correct custom element values (fix invalid values for all versions)
7728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7730 int element = EL_CUSTOM_START + i;
7731 struct ElementInfo *ei = &element_info[element];
7733 for (j = 0; j < ei->num_change_pages; j++)
7735 struct ElementChangeInfo *change = &ei->change_page[j];
7737 if (change->trigger_player == CH_PLAYER_NONE)
7738 change->trigger_player = CH_PLAYER_ANY;
7740 if (change->trigger_side == CH_SIDE_NONE)
7741 change->trigger_side = CH_SIDE_ANY;
7746 // initialize "can_explode" field for old levels which did not store this
7747 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7748 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7750 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7752 int element = EL_CUSTOM_START + i;
7754 if (EXPLODES_1X1_OLD(element))
7755 element_info[element].explosion_type = EXPLODES_1X1;
7757 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7758 EXPLODES_SMASHED(element) ||
7759 EXPLODES_IMPACT(element)));
7763 // correct previously hard-coded move delay values for maze runner style
7764 if (level->game_version < VERSION_IDENT(3,1,1,0))
7766 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7768 int element = EL_CUSTOM_START + i;
7770 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7772 // previously hard-coded and therefore ignored
7773 element_info[element].move_delay_fixed = 9;
7774 element_info[element].move_delay_random = 0;
7779 // set some other uninitialized values of custom elements in older levels
7780 if (level->game_version < VERSION_IDENT(3,1,0,0))
7782 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7784 int element = EL_CUSTOM_START + i;
7786 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7788 element_info[element].explosion_delay = 17;
7789 element_info[element].ignition_delay = 8;
7793 // set mouse click change events to work for left/middle/right mouse button
7794 if (level->game_version < VERSION_IDENT(4,2,3,0))
7796 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7798 int element = EL_CUSTOM_START + i;
7799 struct ElementInfo *ei = &element_info[element];
7801 for (j = 0; j < ei->num_change_pages; j++)
7803 struct ElementChangeInfo *change = &ei->change_page[j];
7805 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7806 change->has_event[CE_PRESSED_BY_MOUSE] ||
7807 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7808 change->has_event[CE_MOUSE_PRESSED_ON_X])
7809 change->trigger_side = CH_SIDE_ANY;
7815 static void LoadLevel_InitElements(struct LevelInfo *level)
7817 LoadLevel_InitStandardElements(level);
7819 if (level->file_has_custom_elements)
7820 LoadLevel_InitCustomElements(level);
7822 // initialize element properties for level editor etc.
7823 InitElementPropertiesEngine(level->game_version);
7824 InitElementPropertiesGfxElement();
7827 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7831 // map elements that have changed in newer versions
7832 for (y = 0; y < level->fieldy; y++)
7833 for (x = 0; x < level->fieldx; x++)
7834 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7835 level->game_version);
7837 // clear unused playfield data (nicer if level gets resized in editor)
7838 for (x = 0; x < MAX_LEV_FIELDX; x++)
7839 for (y = 0; y < MAX_LEV_FIELDY; y++)
7840 if (x >= level->fieldx || y >= level->fieldy)
7841 level->field[x][y] = EL_EMPTY;
7843 // copy elements to runtime playfield array
7844 for (x = 0; x < MAX_LEV_FIELDX; x++)
7845 for (y = 0; y < MAX_LEV_FIELDY; y++)
7846 Tile[x][y] = level->field[x][y];
7848 // initialize level size variables for faster access
7849 lev_fieldx = level->fieldx;
7850 lev_fieldy = level->fieldy;
7852 // determine border element for this level
7853 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7854 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7859 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7861 struct LevelFileInfo *level_file_info = &level->file_info;
7863 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7864 CopyNativeLevel_RND_to_Native(level);
7867 static void LoadLevelTemplate_LoadAndInit(void)
7869 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7871 LoadLevel_InitVersion(&level_template);
7872 LoadLevel_InitElements(&level_template);
7873 LoadLevel_InitSettings(&level_template);
7875 ActivateLevelTemplate();
7878 void LoadLevelTemplate(int nr)
7880 if (!fileExists(getGlobalLevelTemplateFilename()))
7882 Warn("no level template found for this level");
7887 setLevelFileInfo(&level_template.file_info, nr);
7889 LoadLevelTemplate_LoadAndInit();
7892 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7894 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7896 LoadLevelTemplate_LoadAndInit();
7899 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7901 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7903 if (level.use_custom_template)
7905 if (network_level != NULL)
7906 LoadNetworkLevelTemplate(network_level);
7908 LoadLevelTemplate(-1);
7911 LoadLevel_InitVersion(&level);
7912 LoadLevel_InitElements(&level);
7913 LoadLevel_InitPlayfield(&level);
7914 LoadLevel_InitSettings(&level);
7916 LoadLevel_InitNativeEngines(&level);
7919 void LoadLevel(int nr)
7921 SetLevelSetInfo(leveldir_current->identifier, nr);
7923 setLevelFileInfo(&level.file_info, nr);
7925 LoadLevel_LoadAndInit(NULL);
7928 void LoadLevelInfoOnly(int nr)
7930 setLevelFileInfo(&level.file_info, nr);
7932 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7935 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7937 SetLevelSetInfo(network_level->leveldir_identifier,
7938 network_level->file_info.nr);
7940 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7942 LoadLevel_LoadAndInit(network_level);
7945 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7949 chunk_size += putFileVersion(file, level->file_version);
7950 chunk_size += putFileVersion(file, level->game_version);
7955 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7959 chunk_size += putFile16BitBE(file, level->creation_date.year);
7960 chunk_size += putFile8Bit(file, level->creation_date.month);
7961 chunk_size += putFile8Bit(file, level->creation_date.day);
7966 #if ENABLE_HISTORIC_CHUNKS
7967 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7971 putFile8Bit(file, level->fieldx);
7972 putFile8Bit(file, level->fieldy);
7974 putFile16BitBE(file, level->time);
7975 putFile16BitBE(file, level->gems_needed);
7977 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7978 putFile8Bit(file, level->name[i]);
7980 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7981 putFile8Bit(file, level->score[i]);
7983 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7984 for (y = 0; y < 3; y++)
7985 for (x = 0; x < 3; x++)
7986 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7987 level->yamyam_content[i].e[x][y]));
7988 putFile8Bit(file, level->amoeba_speed);
7989 putFile8Bit(file, level->time_magic_wall);
7990 putFile8Bit(file, level->time_wheel);
7991 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7992 level->amoeba_content));
7993 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7994 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7995 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7996 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7998 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8000 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8001 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8002 putFile32BitBE(file, level->can_move_into_acid_bits);
8003 putFile8Bit(file, level->dont_collide_with_bits);
8005 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8006 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8008 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8009 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8010 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8012 putFile8Bit(file, level->game_engine_type);
8014 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8018 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8023 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8024 chunk_size += putFile8Bit(file, level->name[i]);
8029 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8034 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8035 chunk_size += putFile8Bit(file, level->author[i]);
8040 #if ENABLE_HISTORIC_CHUNKS
8041 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8046 for (y = 0; y < level->fieldy; y++)
8047 for (x = 0; x < level->fieldx; x++)
8048 if (level->encoding_16bit_field)
8049 chunk_size += putFile16BitBE(file, level->field[x][y]);
8051 chunk_size += putFile8Bit(file, level->field[x][y]);
8057 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8062 for (y = 0; y < level->fieldy; y++)
8063 for (x = 0; x < level->fieldx; x++)
8064 chunk_size += putFile16BitBE(file, level->field[x][y]);
8069 #if ENABLE_HISTORIC_CHUNKS
8070 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8074 putFile8Bit(file, EL_YAMYAM);
8075 putFile8Bit(file, level->num_yamyam_contents);
8076 putFile8Bit(file, 0);
8077 putFile8Bit(file, 0);
8079 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8080 for (y = 0; y < 3; y++)
8081 for (x = 0; x < 3; x++)
8082 if (level->encoding_16bit_field)
8083 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8085 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8089 #if ENABLE_HISTORIC_CHUNKS
8090 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8093 int num_contents, content_xsize, content_ysize;
8094 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8096 if (element == EL_YAMYAM)
8098 num_contents = level->num_yamyam_contents;
8102 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8103 for (y = 0; y < 3; y++)
8104 for (x = 0; x < 3; x++)
8105 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8107 else if (element == EL_BD_AMOEBA)
8113 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8114 for (y = 0; y < 3; y++)
8115 for (x = 0; x < 3; x++)
8116 content_array[i][x][y] = EL_EMPTY;
8117 content_array[0][0][0] = level->amoeba_content;
8121 // chunk header already written -- write empty chunk data
8122 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8124 Warn("cannot save content for element '%d'", element);
8129 putFile16BitBE(file, element);
8130 putFile8Bit(file, num_contents);
8131 putFile8Bit(file, content_xsize);
8132 putFile8Bit(file, content_ysize);
8134 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8136 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8137 for (y = 0; y < 3; y++)
8138 for (x = 0; x < 3; x++)
8139 putFile16BitBE(file, content_array[i][x][y]);
8143 #if ENABLE_HISTORIC_CHUNKS
8144 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8146 int envelope_nr = element - EL_ENVELOPE_1;
8147 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8151 chunk_size += putFile16BitBE(file, element);
8152 chunk_size += putFile16BitBE(file, envelope_len);
8153 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8154 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8156 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8157 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8159 for (i = 0; i < envelope_len; i++)
8160 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8166 #if ENABLE_HISTORIC_CHUNKS
8167 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8168 int num_changed_custom_elements)
8172 putFile16BitBE(file, num_changed_custom_elements);
8174 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8176 int element = EL_CUSTOM_START + i;
8178 struct ElementInfo *ei = &element_info[element];
8180 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8182 if (check < num_changed_custom_elements)
8184 putFile16BitBE(file, element);
8185 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8192 if (check != num_changed_custom_elements) // should not happen
8193 Warn("inconsistent number of custom element properties");
8197 #if ENABLE_HISTORIC_CHUNKS
8198 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8199 int num_changed_custom_elements)
8203 putFile16BitBE(file, num_changed_custom_elements);
8205 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8207 int element = EL_CUSTOM_START + i;
8209 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8211 if (check < num_changed_custom_elements)
8213 putFile16BitBE(file, element);
8214 putFile16BitBE(file, element_info[element].change->target_element);
8221 if (check != num_changed_custom_elements) // should not happen
8222 Warn("inconsistent number of custom target elements");
8226 #if ENABLE_HISTORIC_CHUNKS
8227 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8228 int num_changed_custom_elements)
8230 int i, j, x, y, check = 0;
8232 putFile16BitBE(file, num_changed_custom_elements);
8234 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8236 int element = EL_CUSTOM_START + i;
8237 struct ElementInfo *ei = &element_info[element];
8239 if (ei->modified_settings)
8241 if (check < num_changed_custom_elements)
8243 putFile16BitBE(file, element);
8245 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8246 putFile8Bit(file, ei->description[j]);
8248 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8250 // some free bytes for future properties and padding
8251 WriteUnusedBytesToFile(file, 7);
8253 putFile8Bit(file, ei->use_gfx_element);
8254 putFile16BitBE(file, ei->gfx_element_initial);
8256 putFile8Bit(file, ei->collect_score_initial);
8257 putFile8Bit(file, ei->collect_count_initial);
8259 putFile16BitBE(file, ei->push_delay_fixed);
8260 putFile16BitBE(file, ei->push_delay_random);
8261 putFile16BitBE(file, ei->move_delay_fixed);
8262 putFile16BitBE(file, ei->move_delay_random);
8264 putFile16BitBE(file, ei->move_pattern);
8265 putFile8Bit(file, ei->move_direction_initial);
8266 putFile8Bit(file, ei->move_stepsize);
8268 for (y = 0; y < 3; y++)
8269 for (x = 0; x < 3; x++)
8270 putFile16BitBE(file, ei->content.e[x][y]);
8272 putFile32BitBE(file, ei->change->events);
8274 putFile16BitBE(file, ei->change->target_element);
8276 putFile16BitBE(file, ei->change->delay_fixed);
8277 putFile16BitBE(file, ei->change->delay_random);
8278 putFile16BitBE(file, ei->change->delay_frames);
8280 putFile16BitBE(file, ei->change->initial_trigger_element);
8282 putFile8Bit(file, ei->change->explode);
8283 putFile8Bit(file, ei->change->use_target_content);
8284 putFile8Bit(file, ei->change->only_if_complete);
8285 putFile8Bit(file, ei->change->use_random_replace);
8287 putFile8Bit(file, ei->change->random_percentage);
8288 putFile8Bit(file, ei->change->replace_when);
8290 for (y = 0; y < 3; y++)
8291 for (x = 0; x < 3; x++)
8292 putFile16BitBE(file, ei->change->content.e[x][y]);
8294 putFile8Bit(file, ei->slippery_type);
8296 // some free bytes for future properties and padding
8297 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8304 if (check != num_changed_custom_elements) // should not happen
8305 Warn("inconsistent number of custom element properties");
8309 #if ENABLE_HISTORIC_CHUNKS
8310 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8312 struct ElementInfo *ei = &element_info[element];
8315 // ---------- custom element base property values (96 bytes) ----------------
8317 putFile16BitBE(file, element);
8319 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8320 putFile8Bit(file, ei->description[i]);
8322 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8324 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8326 putFile8Bit(file, ei->num_change_pages);
8328 putFile16BitBE(file, ei->ce_value_fixed_initial);
8329 putFile16BitBE(file, ei->ce_value_random_initial);
8330 putFile8Bit(file, ei->use_last_ce_value);
8332 putFile8Bit(file, ei->use_gfx_element);
8333 putFile16BitBE(file, ei->gfx_element_initial);
8335 putFile8Bit(file, ei->collect_score_initial);
8336 putFile8Bit(file, ei->collect_count_initial);
8338 putFile8Bit(file, ei->drop_delay_fixed);
8339 putFile8Bit(file, ei->push_delay_fixed);
8340 putFile8Bit(file, ei->drop_delay_random);
8341 putFile8Bit(file, ei->push_delay_random);
8342 putFile16BitBE(file, ei->move_delay_fixed);
8343 putFile16BitBE(file, ei->move_delay_random);
8345 // bits 0 - 15 of "move_pattern" ...
8346 putFile16BitBE(file, ei->move_pattern & 0xffff);
8347 putFile8Bit(file, ei->move_direction_initial);
8348 putFile8Bit(file, ei->move_stepsize);
8350 putFile8Bit(file, ei->slippery_type);
8352 for (y = 0; y < 3; y++)
8353 for (x = 0; x < 3; x++)
8354 putFile16BitBE(file, ei->content.e[x][y]);
8356 putFile16BitBE(file, ei->move_enter_element);
8357 putFile16BitBE(file, ei->move_leave_element);
8358 putFile8Bit(file, ei->move_leave_type);
8360 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8361 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8363 putFile8Bit(file, ei->access_direction);
8365 putFile8Bit(file, ei->explosion_delay);
8366 putFile8Bit(file, ei->ignition_delay);
8367 putFile8Bit(file, ei->explosion_type);
8369 // some free bytes for future custom property values and padding
8370 WriteUnusedBytesToFile(file, 1);
8372 // ---------- change page property values (48 bytes) ------------------------
8374 for (i = 0; i < ei->num_change_pages; i++)
8376 struct ElementChangeInfo *change = &ei->change_page[i];
8377 unsigned int event_bits;
8379 // bits 0 - 31 of "has_event[]" ...
8381 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8382 if (change->has_event[j])
8383 event_bits |= (1u << j);
8384 putFile32BitBE(file, event_bits);
8386 putFile16BitBE(file, change->target_element);
8388 putFile16BitBE(file, change->delay_fixed);
8389 putFile16BitBE(file, change->delay_random);
8390 putFile16BitBE(file, change->delay_frames);
8392 putFile16BitBE(file, change->initial_trigger_element);
8394 putFile8Bit(file, change->explode);
8395 putFile8Bit(file, change->use_target_content);
8396 putFile8Bit(file, change->only_if_complete);
8397 putFile8Bit(file, change->use_random_replace);
8399 putFile8Bit(file, change->random_percentage);
8400 putFile8Bit(file, change->replace_when);
8402 for (y = 0; y < 3; y++)
8403 for (x = 0; x < 3; x++)
8404 putFile16BitBE(file, change->target_content.e[x][y]);
8406 putFile8Bit(file, change->can_change);
8408 putFile8Bit(file, change->trigger_side);
8410 putFile8Bit(file, change->trigger_player);
8411 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8412 log_2(change->trigger_page)));
8414 putFile8Bit(file, change->has_action);
8415 putFile8Bit(file, change->action_type);
8416 putFile8Bit(file, change->action_mode);
8417 putFile16BitBE(file, change->action_arg);
8419 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8421 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8422 if (change->has_event[j])
8423 event_bits |= (1u << (j - 32));
8424 putFile8Bit(file, event_bits);
8429 #if ENABLE_HISTORIC_CHUNKS
8430 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8432 struct ElementInfo *ei = &element_info[element];
8433 struct ElementGroupInfo *group = ei->group;
8436 putFile16BitBE(file, element);
8438 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8439 putFile8Bit(file, ei->description[i]);
8441 putFile8Bit(file, group->num_elements);
8443 putFile8Bit(file, ei->use_gfx_element);
8444 putFile16BitBE(file, ei->gfx_element_initial);
8446 putFile8Bit(file, group->choice_mode);
8448 // some free bytes for future values and padding
8449 WriteUnusedBytesToFile(file, 3);
8451 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8452 putFile16BitBE(file, group->element[i]);
8456 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8457 boolean write_element)
8459 int save_type = entry->save_type;
8460 int data_type = entry->data_type;
8461 int conf_type = entry->conf_type;
8462 int byte_mask = conf_type & CONF_MASK_BYTES;
8463 int element = entry->element;
8464 int default_value = entry->default_value;
8466 boolean modified = FALSE;
8468 if (byte_mask != CONF_MASK_MULTI_BYTES)
8470 void *value_ptr = entry->value;
8471 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8474 // check if any settings have been modified before saving them
8475 if (value != default_value)
8478 // do not save if explicitly told or if unmodified default settings
8479 if ((save_type == SAVE_CONF_NEVER) ||
8480 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8484 num_bytes += putFile16BitBE(file, element);
8486 num_bytes += putFile8Bit(file, conf_type);
8487 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8488 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8489 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8492 else if (data_type == TYPE_STRING)
8494 char *default_string = entry->default_string;
8495 char *string = (char *)(entry->value);
8496 int string_length = strlen(string);
8499 // check if any settings have been modified before saving them
8500 if (!strEqual(string, default_string))
8503 // do not save if explicitly told or if unmodified default settings
8504 if ((save_type == SAVE_CONF_NEVER) ||
8505 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8509 num_bytes += putFile16BitBE(file, element);
8511 num_bytes += putFile8Bit(file, conf_type);
8512 num_bytes += putFile16BitBE(file, string_length);
8514 for (i = 0; i < string_length; i++)
8515 num_bytes += putFile8Bit(file, string[i]);
8517 else if (data_type == TYPE_ELEMENT_LIST)
8519 int *element_array = (int *)(entry->value);
8520 int num_elements = *(int *)(entry->num_entities);
8523 // check if any settings have been modified before saving them
8524 for (i = 0; i < num_elements; i++)
8525 if (element_array[i] != default_value)
8528 // do not save if explicitly told or if unmodified default settings
8529 if ((save_type == SAVE_CONF_NEVER) ||
8530 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8534 num_bytes += putFile16BitBE(file, element);
8536 num_bytes += putFile8Bit(file, conf_type);
8537 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8539 for (i = 0; i < num_elements; i++)
8540 num_bytes += putFile16BitBE(file, element_array[i]);
8542 else if (data_type == TYPE_CONTENT_LIST)
8544 struct Content *content = (struct Content *)(entry->value);
8545 int num_contents = *(int *)(entry->num_entities);
8548 // check if any settings have been modified before saving them
8549 for (i = 0; i < num_contents; i++)
8550 for (y = 0; y < 3; y++)
8551 for (x = 0; x < 3; x++)
8552 if (content[i].e[x][y] != default_value)
8555 // do not save if explicitly told or if unmodified default settings
8556 if ((save_type == SAVE_CONF_NEVER) ||
8557 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8561 num_bytes += putFile16BitBE(file, element);
8563 num_bytes += putFile8Bit(file, conf_type);
8564 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8566 for (i = 0; i < num_contents; i++)
8567 for (y = 0; y < 3; y++)
8568 for (x = 0; x < 3; x++)
8569 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8575 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8580 li = *level; // copy level data into temporary buffer
8582 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8583 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8588 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8593 li = *level; // copy level data into temporary buffer
8595 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8596 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8601 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8603 int envelope_nr = element - EL_ENVELOPE_1;
8607 chunk_size += putFile16BitBE(file, element);
8609 // copy envelope data into temporary buffer
8610 xx_envelope = level->envelope[envelope_nr];
8612 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8613 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8618 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8620 struct ElementInfo *ei = &element_info[element];
8624 chunk_size += putFile16BitBE(file, element);
8626 xx_ei = *ei; // copy element data into temporary buffer
8628 // set default description string for this specific element
8629 strcpy(xx_default_description, getDefaultElementDescription(ei));
8631 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8632 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8634 for (i = 0; i < ei->num_change_pages; i++)
8636 struct ElementChangeInfo *change = &ei->change_page[i];
8638 xx_current_change_page = i;
8640 xx_change = *change; // copy change data into temporary buffer
8643 setEventBitsFromEventFlags(change);
8645 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8646 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8653 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8655 struct ElementInfo *ei = &element_info[element];
8656 struct ElementGroupInfo *group = ei->group;
8660 chunk_size += putFile16BitBE(file, element);
8662 xx_ei = *ei; // copy element data into temporary buffer
8663 xx_group = *group; // copy group data into temporary buffer
8665 // set default description string for this specific element
8666 strcpy(xx_default_description, getDefaultElementDescription(ei));
8668 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8669 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8674 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8676 struct ElementInfo *ei = &element_info[element];
8680 chunk_size += putFile16BitBE(file, element);
8682 xx_ei = *ei; // copy element data into temporary buffer
8684 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8685 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8690 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8691 boolean save_as_template)
8697 if (!(file = fopen(filename, MODE_WRITE)))
8699 Warn("cannot save level file '%s'", filename);
8704 level->file_version = FILE_VERSION_ACTUAL;
8705 level->game_version = GAME_VERSION_ACTUAL;
8707 level->creation_date = getCurrentDate();
8709 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8710 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8712 chunk_size = SaveLevel_VERS(NULL, level);
8713 putFileChunkBE(file, "VERS", chunk_size);
8714 SaveLevel_VERS(file, level);
8716 chunk_size = SaveLevel_DATE(NULL, level);
8717 putFileChunkBE(file, "DATE", chunk_size);
8718 SaveLevel_DATE(file, level);
8720 chunk_size = SaveLevel_NAME(NULL, level);
8721 putFileChunkBE(file, "NAME", chunk_size);
8722 SaveLevel_NAME(file, level);
8724 chunk_size = SaveLevel_AUTH(NULL, level);
8725 putFileChunkBE(file, "AUTH", chunk_size);
8726 SaveLevel_AUTH(file, level);
8728 chunk_size = SaveLevel_INFO(NULL, level);
8729 putFileChunkBE(file, "INFO", chunk_size);
8730 SaveLevel_INFO(file, level);
8732 chunk_size = SaveLevel_BODY(NULL, level);
8733 putFileChunkBE(file, "BODY", chunk_size);
8734 SaveLevel_BODY(file, level);
8736 chunk_size = SaveLevel_ELEM(NULL, level);
8737 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8739 putFileChunkBE(file, "ELEM", chunk_size);
8740 SaveLevel_ELEM(file, level);
8743 for (i = 0; i < NUM_ENVELOPES; i++)
8745 int element = EL_ENVELOPE_1 + i;
8747 chunk_size = SaveLevel_NOTE(NULL, level, element);
8748 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8750 putFileChunkBE(file, "NOTE", chunk_size);
8751 SaveLevel_NOTE(file, level, element);
8755 // if not using template level, check for non-default custom/group elements
8756 if (!level->use_custom_template || save_as_template)
8758 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8760 int element = EL_CUSTOM_START + i;
8762 chunk_size = SaveLevel_CUSX(NULL, level, element);
8763 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8765 putFileChunkBE(file, "CUSX", chunk_size);
8766 SaveLevel_CUSX(file, level, element);
8770 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8772 int element = EL_GROUP_START + i;
8774 chunk_size = SaveLevel_GRPX(NULL, level, element);
8775 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8777 putFileChunkBE(file, "GRPX", chunk_size);
8778 SaveLevel_GRPX(file, level, element);
8782 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8784 int element = GET_EMPTY_ELEMENT(i);
8786 chunk_size = SaveLevel_EMPX(NULL, level, element);
8787 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8789 putFileChunkBE(file, "EMPX", chunk_size);
8790 SaveLevel_EMPX(file, level, element);
8797 SetFilePermissions(filename, PERMS_PRIVATE);
8800 void SaveLevel(int nr)
8802 char *filename = getDefaultLevelFilename(nr);
8804 SaveLevelFromFilename(&level, filename, FALSE);
8807 void SaveLevelTemplate(void)
8809 char *filename = getLocalLevelTemplateFilename();
8811 SaveLevelFromFilename(&level, filename, TRUE);
8814 boolean SaveLevelChecked(int nr)
8816 char *filename = getDefaultLevelFilename(nr);
8817 boolean new_level = !fileExists(filename);
8818 boolean level_saved = FALSE;
8820 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8825 Request("Level saved!", REQ_CONFIRM);
8833 void DumpLevel(struct LevelInfo *level)
8835 if (level->no_level_file || level->no_valid_file)
8837 Warn("cannot dump -- no valid level file found");
8843 Print("Level xxx (file version %08d, game version %08d)\n",
8844 level->file_version, level->game_version);
8847 Print("Level author: '%s'\n", level->author);
8848 Print("Level title: '%s'\n", level->name);
8850 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8852 Print("Level time: %d seconds\n", level->time);
8853 Print("Gems needed: %d\n", level->gems_needed);
8855 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8856 Print("Time for wheel: %d seconds\n", level->time_wheel);
8857 Print("Time for light: %d seconds\n", level->time_light);
8858 Print("Time for timegate: %d seconds\n", level->time_timegate);
8860 Print("Amoeba speed: %d\n", level->amoeba_speed);
8863 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8864 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8865 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8866 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8867 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8868 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8874 for (i = 0; i < NUM_ENVELOPES; i++)
8876 char *text = level->envelope[i].text;
8877 int text_len = strlen(text);
8878 boolean has_text = FALSE;
8880 for (j = 0; j < text_len; j++)
8881 if (text[j] != ' ' && text[j] != '\n')
8887 Print("Envelope %d:\n'%s'\n", i + 1, text);
8895 void DumpLevels(void)
8897 static LevelDirTree *dumplevel_leveldir = NULL;
8899 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8900 global.dumplevel_leveldir);
8902 if (dumplevel_leveldir == NULL)
8903 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8905 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8906 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8907 Fail("no such level number: %d", global.dumplevel_level_nr);
8909 leveldir_current = dumplevel_leveldir;
8911 LoadLevel(global.dumplevel_level_nr);
8918 // ============================================================================
8919 // tape file functions
8920 // ============================================================================
8922 static void setTapeInfoToDefaults(void)
8926 // always start with reliable default values (empty tape)
8929 // default values (also for pre-1.2 tapes) with only the first player
8930 tape.player_participates[0] = TRUE;
8931 for (i = 1; i < MAX_PLAYERS; i++)
8932 tape.player_participates[i] = FALSE;
8934 // at least one (default: the first) player participates in every tape
8935 tape.num_participating_players = 1;
8937 tape.property_bits = TAPE_PROPERTY_NONE;
8939 tape.level_nr = level_nr;
8941 tape.changed = FALSE;
8942 tape.solved = FALSE;
8944 tape.recording = FALSE;
8945 tape.playing = FALSE;
8946 tape.pausing = FALSE;
8948 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8949 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8951 tape.no_info_chunk = TRUE;
8952 tape.no_valid_file = FALSE;
8955 static int getTapePosSize(struct TapeInfo *tape)
8957 int tape_pos_size = 0;
8959 if (tape->use_key_actions)
8960 tape_pos_size += tape->num_participating_players;
8962 if (tape->use_mouse_actions)
8963 tape_pos_size += 3; // x and y position and mouse button mask
8965 tape_pos_size += 1; // tape action delay value
8967 return tape_pos_size;
8970 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8972 tape->use_key_actions = FALSE;
8973 tape->use_mouse_actions = FALSE;
8975 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8976 tape->use_key_actions = TRUE;
8978 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8979 tape->use_mouse_actions = TRUE;
8982 static int getTapeActionValue(struct TapeInfo *tape)
8984 return (tape->use_key_actions &&
8985 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8986 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8987 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8988 TAPE_ACTIONS_DEFAULT);
8991 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8993 tape->file_version = getFileVersion(file);
8994 tape->game_version = getFileVersion(file);
8999 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9003 tape->random_seed = getFile32BitBE(file);
9004 tape->date = getFile32BitBE(file);
9005 tape->length = getFile32BitBE(file);
9007 // read header fields that are new since version 1.2
9008 if (tape->file_version >= FILE_VERSION_1_2)
9010 byte store_participating_players = getFile8Bit(file);
9013 // since version 1.2, tapes store which players participate in the tape
9014 tape->num_participating_players = 0;
9015 for (i = 0; i < MAX_PLAYERS; i++)
9017 tape->player_participates[i] = FALSE;
9019 if (store_participating_players & (1 << i))
9021 tape->player_participates[i] = TRUE;
9022 tape->num_participating_players++;
9026 setTapeActionFlags(tape, getFile8Bit(file));
9028 tape->property_bits = getFile8Bit(file);
9029 tape->solved = getFile8Bit(file);
9031 engine_version = getFileVersion(file);
9032 if (engine_version > 0)
9033 tape->engine_version = engine_version;
9035 tape->engine_version = tape->game_version;
9041 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9043 tape->scr_fieldx = getFile8Bit(file);
9044 tape->scr_fieldy = getFile8Bit(file);
9049 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9051 char *level_identifier = NULL;
9052 int level_identifier_size;
9055 tape->no_info_chunk = FALSE;
9057 level_identifier_size = getFile16BitBE(file);
9059 level_identifier = checked_malloc(level_identifier_size);
9061 for (i = 0; i < level_identifier_size; i++)
9062 level_identifier[i] = getFile8Bit(file);
9064 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9065 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9067 checked_free(level_identifier);
9069 tape->level_nr = getFile16BitBE(file);
9071 chunk_size = 2 + level_identifier_size + 2;
9076 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9079 int tape_pos_size = getTapePosSize(tape);
9080 int chunk_size_expected = tape_pos_size * tape->length;
9082 if (chunk_size_expected != chunk_size)
9084 ReadUnusedBytesFromFile(file, chunk_size);
9085 return chunk_size_expected;
9088 for (i = 0; i < tape->length; i++)
9090 if (i >= MAX_TAPE_LEN)
9092 Warn("tape truncated -- size exceeds maximum tape size %d",
9095 // tape too large; read and ignore remaining tape data from this chunk
9096 for (;i < tape->length; i++)
9097 ReadUnusedBytesFromFile(file, tape_pos_size);
9102 if (tape->use_key_actions)
9104 for (j = 0; j < MAX_PLAYERS; j++)
9106 tape->pos[i].action[j] = MV_NONE;
9108 if (tape->player_participates[j])
9109 tape->pos[i].action[j] = getFile8Bit(file);
9113 if (tape->use_mouse_actions)
9115 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9116 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9117 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9120 tape->pos[i].delay = getFile8Bit(file);
9122 if (tape->file_version == FILE_VERSION_1_0)
9124 // eliminate possible diagonal moves in old tapes
9125 // this is only for backward compatibility
9127 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9128 byte action = tape->pos[i].action[0];
9129 int k, num_moves = 0;
9131 for (k = 0; k < 4; k++)
9133 if (action & joy_dir[k])
9135 tape->pos[i + num_moves].action[0] = joy_dir[k];
9137 tape->pos[i + num_moves].delay = 0;
9146 tape->length += num_moves;
9149 else if (tape->file_version < FILE_VERSION_2_0)
9151 // convert pre-2.0 tapes to new tape format
9153 if (tape->pos[i].delay > 1)
9156 tape->pos[i + 1] = tape->pos[i];
9157 tape->pos[i + 1].delay = 1;
9160 for (j = 0; j < MAX_PLAYERS; j++)
9161 tape->pos[i].action[j] = MV_NONE;
9162 tape->pos[i].delay--;
9169 if (checkEndOfFile(file))
9173 if (i != tape->length)
9174 chunk_size = tape_pos_size * i;
9179 static void LoadTape_SokobanSolution(char *filename)
9182 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9184 if (!(file = openFile(filename, MODE_READ)))
9186 tape.no_valid_file = TRUE;
9191 while (!checkEndOfFile(file))
9193 unsigned char c = getByteFromFile(file);
9195 if (checkEndOfFile(file))
9202 tape.pos[tape.length].action[0] = MV_UP;
9203 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9209 tape.pos[tape.length].action[0] = MV_DOWN;
9210 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9216 tape.pos[tape.length].action[0] = MV_LEFT;
9217 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9223 tape.pos[tape.length].action[0] = MV_RIGHT;
9224 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9232 // ignore white-space characters
9236 tape.no_valid_file = TRUE;
9238 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9246 if (tape.no_valid_file)
9249 tape.length_frames = GetTapeLengthFrames();
9250 tape.length_seconds = GetTapeLengthSeconds();
9253 void LoadTapeFromFilename(char *filename)
9255 char cookie[MAX_LINE_LEN];
9256 char chunk_name[CHUNK_ID_LEN + 1];
9260 // always start with reliable default values
9261 setTapeInfoToDefaults();
9263 if (strSuffix(filename, ".sln"))
9265 LoadTape_SokobanSolution(filename);
9270 if (!(file = openFile(filename, MODE_READ)))
9272 tape.no_valid_file = TRUE;
9277 getFileChunkBE(file, chunk_name, NULL);
9278 if (strEqual(chunk_name, "RND1"))
9280 getFile32BitBE(file); // not used
9282 getFileChunkBE(file, chunk_name, NULL);
9283 if (!strEqual(chunk_name, "TAPE"))
9285 tape.no_valid_file = TRUE;
9287 Warn("unknown format of tape file '%s'", filename);
9294 else // check for pre-2.0 file format with cookie string
9296 strcpy(cookie, chunk_name);
9297 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9299 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9300 cookie[strlen(cookie) - 1] = '\0';
9302 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9304 tape.no_valid_file = TRUE;
9306 Warn("unknown format of tape file '%s'", filename);
9313 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9315 tape.no_valid_file = TRUE;
9317 Warn("unsupported version of tape file '%s'", filename);
9324 // pre-2.0 tape files have no game version, so use file version here
9325 tape.game_version = tape.file_version;
9328 if (tape.file_version < FILE_VERSION_1_2)
9330 // tape files from versions before 1.2.0 without chunk structure
9331 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9332 LoadTape_BODY(file, 2 * tape.length, &tape);
9340 int (*loader)(File *, int, struct TapeInfo *);
9344 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9345 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9346 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9347 { "INFO", -1, LoadTape_INFO },
9348 { "BODY", -1, LoadTape_BODY },
9352 while (getFileChunkBE(file, chunk_name, &chunk_size))
9356 while (chunk_info[i].name != NULL &&
9357 !strEqual(chunk_name, chunk_info[i].name))
9360 if (chunk_info[i].name == NULL)
9362 Warn("unknown chunk '%s' in tape file '%s'",
9363 chunk_name, filename);
9365 ReadUnusedBytesFromFile(file, chunk_size);
9367 else if (chunk_info[i].size != -1 &&
9368 chunk_info[i].size != chunk_size)
9370 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9371 chunk_size, chunk_name, filename);
9373 ReadUnusedBytesFromFile(file, chunk_size);
9377 // call function to load this tape chunk
9378 int chunk_size_expected =
9379 (chunk_info[i].loader)(file, chunk_size, &tape);
9381 // the size of some chunks cannot be checked before reading other
9382 // chunks first (like "HEAD" and "BODY") that contain some header
9383 // information, so check them here
9384 if (chunk_size_expected != chunk_size)
9386 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9387 chunk_size, chunk_name, filename);
9395 tape.length_frames = GetTapeLengthFrames();
9396 tape.length_seconds = GetTapeLengthSeconds();
9399 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9401 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9403 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9404 tape.engine_version);
9408 void LoadTape(int nr)
9410 char *filename = getTapeFilename(nr);
9412 LoadTapeFromFilename(filename);
9415 void LoadSolutionTape(int nr)
9417 char *filename = getSolutionTapeFilename(nr);
9419 LoadTapeFromFilename(filename);
9421 if (TAPE_IS_EMPTY(tape))
9423 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9424 level.native_bd_level->replay != NULL)
9425 CopyNativeTape_BD_to_RND(&level);
9426 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9427 level.native_sp_level->demo.is_available)
9428 CopyNativeTape_SP_to_RND(&level);
9432 void LoadScoreTape(char *score_tape_basename, int nr)
9434 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9436 LoadTapeFromFilename(filename);
9439 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9441 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9443 LoadTapeFromFilename(filename);
9446 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9448 // chunk required for team mode tapes with non-default screen size
9449 return (tape->num_participating_players > 1 &&
9450 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9451 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9454 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9456 putFileVersion(file, tape->file_version);
9457 putFileVersion(file, tape->game_version);
9460 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9463 byte store_participating_players = 0;
9465 // set bits for participating players for compact storage
9466 for (i = 0; i < MAX_PLAYERS; i++)
9467 if (tape->player_participates[i])
9468 store_participating_players |= (1 << i);
9470 putFile32BitBE(file, tape->random_seed);
9471 putFile32BitBE(file, tape->date);
9472 putFile32BitBE(file, tape->length);
9474 putFile8Bit(file, store_participating_players);
9476 putFile8Bit(file, getTapeActionValue(tape));
9478 putFile8Bit(file, tape->property_bits);
9479 putFile8Bit(file, tape->solved);
9481 putFileVersion(file, tape->engine_version);
9484 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9486 putFile8Bit(file, tape->scr_fieldx);
9487 putFile8Bit(file, tape->scr_fieldy);
9490 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9492 int level_identifier_size = strlen(tape->level_identifier) + 1;
9495 putFile16BitBE(file, level_identifier_size);
9497 for (i = 0; i < level_identifier_size; i++)
9498 putFile8Bit(file, tape->level_identifier[i]);
9500 putFile16BitBE(file, tape->level_nr);
9503 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9507 for (i = 0; i < tape->length; i++)
9509 if (tape->use_key_actions)
9511 for (j = 0; j < MAX_PLAYERS; j++)
9512 if (tape->player_participates[j])
9513 putFile8Bit(file, tape->pos[i].action[j]);
9516 if (tape->use_mouse_actions)
9518 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9519 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9520 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9523 putFile8Bit(file, tape->pos[i].delay);
9527 void SaveTapeToFilename(char *filename)
9531 int info_chunk_size;
9532 int body_chunk_size;
9534 if (!(file = fopen(filename, MODE_WRITE)))
9536 Warn("cannot save level recording file '%s'", filename);
9541 tape_pos_size = getTapePosSize(&tape);
9543 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9544 body_chunk_size = tape_pos_size * tape.length;
9546 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9547 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9549 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9550 SaveTape_VERS(file, &tape);
9552 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9553 SaveTape_HEAD(file, &tape);
9555 if (checkSaveTape_SCRN(&tape))
9557 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9558 SaveTape_SCRN(file, &tape);
9561 putFileChunkBE(file, "INFO", info_chunk_size);
9562 SaveTape_INFO(file, &tape);
9564 putFileChunkBE(file, "BODY", body_chunk_size);
9565 SaveTape_BODY(file, &tape);
9569 SetFilePermissions(filename, PERMS_PRIVATE);
9572 static void SaveTapeExt(char *filename)
9576 tape.file_version = FILE_VERSION_ACTUAL;
9577 tape.game_version = GAME_VERSION_ACTUAL;
9579 tape.num_participating_players = 0;
9581 // count number of participating players
9582 for (i = 0; i < MAX_PLAYERS; i++)
9583 if (tape.player_participates[i])
9584 tape.num_participating_players++;
9586 SaveTapeToFilename(filename);
9588 tape.changed = FALSE;
9591 void SaveTape(int nr)
9593 char *filename = getTapeFilename(nr);
9595 InitTapeDirectory(leveldir_current->subdir);
9597 SaveTapeExt(filename);
9600 void SaveScoreTape(int nr)
9602 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9604 // used instead of "leveldir_current->subdir" (for network games)
9605 InitScoreTapeDirectory(levelset.identifier, nr);
9607 SaveTapeExt(filename);
9610 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9611 unsigned int req_state_added)
9613 char *filename = getTapeFilename(nr);
9614 boolean new_tape = !fileExists(filename);
9615 boolean tape_saved = FALSE;
9617 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9622 Request(msg_saved, REQ_CONFIRM | req_state_added);
9630 boolean SaveTapeChecked(int nr)
9632 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9635 boolean SaveTapeChecked_LevelSolved(int nr)
9637 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9638 "Level solved! Tape saved!", REQ_STAY_OPEN);
9641 void DumpTape(struct TapeInfo *tape)
9643 int tape_frame_counter;
9646 if (tape->no_valid_file)
9648 Warn("cannot dump -- no valid tape file found");
9655 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9656 tape->level_nr, tape->file_version, tape->game_version);
9657 Print(" (effective engine version %08d)\n",
9658 tape->engine_version);
9659 Print("Level series identifier: '%s'\n", tape->level_identifier);
9661 Print("Solution tape: %s\n",
9662 tape->solved ? "yes" :
9663 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9665 Print("Special tape properties: ");
9666 if (tape->property_bits == TAPE_PROPERTY_NONE)
9668 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9669 Print("[em_random_bug]");
9670 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9671 Print("[game_speed]");
9672 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9674 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9675 Print("[single_step]");
9676 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9677 Print("[snapshot]");
9678 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9679 Print("[replayed]");
9680 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9681 Print("[tas_keys]");
9682 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9683 Print("[small_graphics]");
9686 int year2 = tape->date / 10000;
9687 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9688 int month_index_raw = (tape->date / 100) % 100;
9689 int month_index = month_index_raw % 12; // prevent invalid index
9690 int month = month_index + 1;
9691 int day = tape->date % 100;
9693 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9697 tape_frame_counter = 0;
9699 for (i = 0; i < tape->length; i++)
9701 if (i >= MAX_TAPE_LEN)
9706 for (j = 0; j < MAX_PLAYERS; j++)
9708 if (tape->player_participates[j])
9710 int action = tape->pos[i].action[j];
9712 Print("%d:%02x ", j, action);
9713 Print("[%c%c%c%c|%c%c] - ",
9714 (action & JOY_LEFT ? '<' : ' '),
9715 (action & JOY_RIGHT ? '>' : ' '),
9716 (action & JOY_UP ? '^' : ' '),
9717 (action & JOY_DOWN ? 'v' : ' '),
9718 (action & JOY_BUTTON_1 ? '1' : ' '),
9719 (action & JOY_BUTTON_2 ? '2' : ' '));
9723 Print("(%03d) ", tape->pos[i].delay);
9724 Print("[%05d]\n", tape_frame_counter);
9726 tape_frame_counter += tape->pos[i].delay;
9732 void DumpTapes(void)
9734 static LevelDirTree *dumptape_leveldir = NULL;
9736 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9737 global.dumptape_leveldir);
9739 if (dumptape_leveldir == NULL)
9740 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9742 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9743 global.dumptape_level_nr > dumptape_leveldir->last_level)
9744 Fail("no such level number: %d", global.dumptape_level_nr);
9746 leveldir_current = dumptape_leveldir;
9748 if (options.mytapes)
9749 LoadTape(global.dumptape_level_nr);
9751 LoadSolutionTape(global.dumptape_level_nr);
9759 // ============================================================================
9760 // score file functions
9761 // ============================================================================
9763 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9767 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9769 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9770 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9771 scores->entry[i].score = 0;
9772 scores->entry[i].time = 0;
9774 scores->entry[i].id = -1;
9775 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9776 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9777 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9778 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9779 strcpy(scores->entry[i].country_code, "??");
9782 scores->num_entries = 0;
9783 scores->last_added = -1;
9784 scores->last_added_local = -1;
9786 scores->updated = FALSE;
9787 scores->uploaded = FALSE;
9788 scores->tape_downloaded = FALSE;
9789 scores->force_last_added = FALSE;
9791 // The following values are intentionally not reset here:
9795 // - continue_playing
9796 // - continue_on_return
9799 static void setScoreInfoToDefaults(void)
9801 setScoreInfoToDefaultsExt(&scores);
9804 static void setServerScoreInfoToDefaults(void)
9806 setScoreInfoToDefaultsExt(&server_scores);
9809 static void LoadScore_OLD(int nr)
9812 char *filename = getScoreFilename(nr);
9813 char cookie[MAX_LINE_LEN];
9814 char line[MAX_LINE_LEN];
9818 if (!(file = fopen(filename, MODE_READ)))
9821 // check file identifier
9822 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9824 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9825 cookie[strlen(cookie) - 1] = '\0';
9827 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9829 Warn("unknown format of score file '%s'", filename);
9836 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9838 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9839 Warn("fscanf() failed; %s", strerror(errno));
9841 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9844 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9845 line[strlen(line) - 1] = '\0';
9847 for (line_ptr = line; *line_ptr; line_ptr++)
9849 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9851 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9852 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9861 static void ConvertScore_OLD(void)
9863 // only convert score to time for levels that rate playing time over score
9864 if (!level.rate_time_over_score)
9867 // convert old score to playing time for score-less levels (like Supaplex)
9868 int time_final_max = 999;
9871 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9873 int score = scores.entry[i].score;
9875 if (score > 0 && score < time_final_max)
9876 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9880 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9882 scores->file_version = getFileVersion(file);
9883 scores->game_version = getFileVersion(file);
9888 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9890 char *level_identifier = NULL;
9891 int level_identifier_size;
9894 level_identifier_size = getFile16BitBE(file);
9896 level_identifier = checked_malloc(level_identifier_size);
9898 for (i = 0; i < level_identifier_size; i++)
9899 level_identifier[i] = getFile8Bit(file);
9901 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9902 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9904 checked_free(level_identifier);
9906 scores->level_nr = getFile16BitBE(file);
9907 scores->num_entries = getFile16BitBE(file);
9909 chunk_size = 2 + level_identifier_size + 2 + 2;
9914 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9918 for (i = 0; i < scores->num_entries; i++)
9920 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9921 scores->entry[i].name[j] = getFile8Bit(file);
9923 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9926 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9931 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9935 for (i = 0; i < scores->num_entries; i++)
9936 scores->entry[i].score = getFile16BitBE(file);
9938 chunk_size = scores->num_entries * 2;
9943 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9947 for (i = 0; i < scores->num_entries; i++)
9948 scores->entry[i].score = getFile32BitBE(file);
9950 chunk_size = scores->num_entries * 4;
9955 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9959 for (i = 0; i < scores->num_entries; i++)
9960 scores->entry[i].time = getFile32BitBE(file);
9962 chunk_size = scores->num_entries * 4;
9967 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9971 for (i = 0; i < scores->num_entries; i++)
9973 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9974 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9976 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9979 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9984 void LoadScore(int nr)
9986 char *filename = getScoreFilename(nr);
9987 char cookie[MAX_LINE_LEN];
9988 char chunk_name[CHUNK_ID_LEN + 1];
9990 boolean old_score_file_format = FALSE;
9993 // always start with reliable default values
9994 setScoreInfoToDefaults();
9996 if (!(file = openFile(filename, MODE_READ)))
9999 getFileChunkBE(file, chunk_name, NULL);
10000 if (strEqual(chunk_name, "RND1"))
10002 getFile32BitBE(file); // not used
10004 getFileChunkBE(file, chunk_name, NULL);
10005 if (!strEqual(chunk_name, "SCOR"))
10007 Warn("unknown format of score file '%s'", filename);
10014 else // check for old file format with cookie string
10016 strcpy(cookie, chunk_name);
10017 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10019 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10020 cookie[strlen(cookie) - 1] = '\0';
10022 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10024 Warn("unknown format of score file '%s'", filename);
10031 old_score_file_format = TRUE;
10034 if (old_score_file_format)
10036 // score files from versions before 4.2.4.0 without chunk structure
10039 // convert score to time, if possible (mainly for Supaplex levels)
10040 ConvertScore_OLD();
10048 int (*loader)(File *, int, struct ScoreInfo *);
10052 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10053 { "INFO", -1, LoadScore_INFO },
10054 { "NAME", -1, LoadScore_NAME },
10055 { "SCOR", -1, LoadScore_SCOR },
10056 { "SC4R", -1, LoadScore_SC4R },
10057 { "TIME", -1, LoadScore_TIME },
10058 { "TAPE", -1, LoadScore_TAPE },
10063 while (getFileChunkBE(file, chunk_name, &chunk_size))
10067 while (chunk_info[i].name != NULL &&
10068 !strEqual(chunk_name, chunk_info[i].name))
10071 if (chunk_info[i].name == NULL)
10073 Warn("unknown chunk '%s' in score file '%s'",
10074 chunk_name, filename);
10076 ReadUnusedBytesFromFile(file, chunk_size);
10078 else if (chunk_info[i].size != -1 &&
10079 chunk_info[i].size != chunk_size)
10081 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10082 chunk_size, chunk_name, filename);
10084 ReadUnusedBytesFromFile(file, chunk_size);
10088 // call function to load this score chunk
10089 int chunk_size_expected =
10090 (chunk_info[i].loader)(file, chunk_size, &scores);
10092 // the size of some chunks cannot be checked before reading other
10093 // chunks first (like "HEAD" and "BODY") that contain some header
10094 // information, so check them here
10095 if (chunk_size_expected != chunk_size)
10097 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10098 chunk_size, chunk_name, filename);
10107 #if ENABLE_HISTORIC_CHUNKS
10108 void SaveScore_OLD(int nr)
10111 char *filename = getScoreFilename(nr);
10114 // used instead of "leveldir_current->subdir" (for network games)
10115 InitScoreDirectory(levelset.identifier);
10117 if (!(file = fopen(filename, MODE_WRITE)))
10119 Warn("cannot save score for level %d", nr);
10124 fprintf(file, "%s\n\n", SCORE_COOKIE);
10126 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10127 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10131 SetFilePermissions(filename, PERMS_PRIVATE);
10135 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10137 putFileVersion(file, scores->file_version);
10138 putFileVersion(file, scores->game_version);
10141 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10143 int level_identifier_size = strlen(scores->level_identifier) + 1;
10146 putFile16BitBE(file, level_identifier_size);
10148 for (i = 0; i < level_identifier_size; i++)
10149 putFile8Bit(file, scores->level_identifier[i]);
10151 putFile16BitBE(file, scores->level_nr);
10152 putFile16BitBE(file, scores->num_entries);
10155 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10159 for (i = 0; i < scores->num_entries; i++)
10161 int name_size = strlen(scores->entry[i].name);
10163 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10164 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10168 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10172 for (i = 0; i < scores->num_entries; i++)
10173 putFile16BitBE(file, scores->entry[i].score);
10176 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10180 for (i = 0; i < scores->num_entries; i++)
10181 putFile32BitBE(file, scores->entry[i].score);
10184 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10188 for (i = 0; i < scores->num_entries; i++)
10189 putFile32BitBE(file, scores->entry[i].time);
10192 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10196 for (i = 0; i < scores->num_entries; i++)
10198 int size = strlen(scores->entry[i].tape_basename);
10200 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10201 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10205 static void SaveScoreToFilename(char *filename)
10208 int info_chunk_size;
10209 int name_chunk_size;
10210 int scor_chunk_size;
10211 int sc4r_chunk_size;
10212 int time_chunk_size;
10213 int tape_chunk_size;
10214 boolean has_large_score_values;
10217 if (!(file = fopen(filename, MODE_WRITE)))
10219 Warn("cannot save score file '%s'", filename);
10224 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10225 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10226 scor_chunk_size = scores.num_entries * 2;
10227 sc4r_chunk_size = scores.num_entries * 4;
10228 time_chunk_size = scores.num_entries * 4;
10229 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10231 has_large_score_values = FALSE;
10232 for (i = 0; i < scores.num_entries; i++)
10233 if (scores.entry[i].score > 0xffff)
10234 has_large_score_values = TRUE;
10236 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10237 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10239 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10240 SaveScore_VERS(file, &scores);
10242 putFileChunkBE(file, "INFO", info_chunk_size);
10243 SaveScore_INFO(file, &scores);
10245 putFileChunkBE(file, "NAME", name_chunk_size);
10246 SaveScore_NAME(file, &scores);
10248 if (has_large_score_values)
10250 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10251 SaveScore_SC4R(file, &scores);
10255 putFileChunkBE(file, "SCOR", scor_chunk_size);
10256 SaveScore_SCOR(file, &scores);
10259 putFileChunkBE(file, "TIME", time_chunk_size);
10260 SaveScore_TIME(file, &scores);
10262 putFileChunkBE(file, "TAPE", tape_chunk_size);
10263 SaveScore_TAPE(file, &scores);
10267 SetFilePermissions(filename, PERMS_PRIVATE);
10270 void SaveScore(int nr)
10272 char *filename = getScoreFilename(nr);
10275 // used instead of "leveldir_current->subdir" (for network games)
10276 InitScoreDirectory(levelset.identifier);
10278 scores.file_version = FILE_VERSION_ACTUAL;
10279 scores.game_version = GAME_VERSION_ACTUAL;
10281 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10282 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10283 scores.level_nr = level_nr;
10285 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10286 if (scores.entry[i].score == 0 &&
10287 scores.entry[i].time == 0 &&
10288 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10291 scores.num_entries = i;
10293 if (scores.num_entries == 0)
10296 SaveScoreToFilename(filename);
10299 static void LoadServerScoreFromCache(int nr)
10301 struct ScoreEntry score_entry;
10310 { &score_entry.score, FALSE, 0 },
10311 { &score_entry.time, FALSE, 0 },
10312 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10313 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10314 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10315 { &score_entry.id, FALSE, 0 },
10316 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10317 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10318 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10319 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10323 char *filename = getScoreCacheFilename(nr);
10324 SetupFileHash *score_hash = loadSetupFileHash(filename);
10327 server_scores.num_entries = 0;
10329 if (score_hash == NULL)
10332 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10334 score_entry = server_scores.entry[i];
10336 for (j = 0; score_mapping[j].value != NULL; j++)
10340 sprintf(token, "%02d.%d", i, j);
10342 char *value = getHashEntry(score_hash, token);
10347 if (score_mapping[j].is_string)
10349 char *score_value = (char *)score_mapping[j].value;
10350 int value_size = score_mapping[j].string_size;
10352 strncpy(score_value, value, value_size);
10353 score_value[value_size] = '\0';
10357 int *score_value = (int *)score_mapping[j].value;
10359 *score_value = atoi(value);
10362 server_scores.num_entries = i + 1;
10365 server_scores.entry[i] = score_entry;
10368 freeSetupFileHash(score_hash);
10371 void LoadServerScore(int nr, boolean download_score)
10373 if (!setup.use_api_server)
10376 // always start with reliable default values
10377 setServerScoreInfoToDefaults();
10379 // 1st step: load server scores from cache file (which may not exist)
10380 // (this should prevent reading it while the thread is writing to it)
10381 LoadServerScoreFromCache(nr);
10383 if (download_score && runtime.use_api_server)
10385 // 2nd step: download server scores from score server to cache file
10386 // (as thread, as it might time out if the server is not reachable)
10387 ApiGetScoreAsThread(nr);
10391 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10393 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10395 // if score tape not uploaded, ask for uploading missing tapes later
10396 if (!setup.has_remaining_tapes)
10397 setup.ask_for_remaining_tapes = TRUE;
10399 setup.provide_uploading_tapes = TRUE;
10400 setup.has_remaining_tapes = TRUE;
10402 SaveSetup_ServerSetup();
10405 void SaveServerScore(int nr, boolean tape_saved)
10407 if (!runtime.use_api_server)
10409 PrepareScoreTapesForUpload(leveldir_current->subdir);
10414 ApiAddScoreAsThread(nr, tape_saved, NULL);
10417 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10418 char *score_tape_filename)
10420 if (!runtime.use_api_server)
10423 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10426 void LoadLocalAndServerScore(int nr, boolean download_score)
10428 int last_added_local = scores.last_added_local;
10429 boolean force_last_added = scores.force_last_added;
10431 // needed if only showing server scores
10432 setScoreInfoToDefaults();
10434 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10437 // restore last added local score entry (before merging server scores)
10438 scores.last_added = scores.last_added_local = last_added_local;
10440 if (setup.use_api_server &&
10441 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10443 // load server scores from cache file and trigger update from server
10444 LoadServerScore(nr, download_score);
10446 // merge local scores with scores from server
10447 MergeServerScore();
10450 if (force_last_added)
10451 scores.force_last_added = force_last_added;
10455 // ============================================================================
10456 // setup file functions
10457 // ============================================================================
10459 #define TOKEN_STR_PLAYER_PREFIX "player_"
10462 static struct TokenInfo global_setup_tokens[] =
10466 &setup.player_name, "player_name"
10470 &setup.multiple_users, "multiple_users"
10474 &setup.sound, "sound"
10478 &setup.sound_loops, "repeating_sound_loops"
10482 &setup.sound_music, "background_music"
10486 &setup.sound_simple, "simple_sound_effects"
10490 &setup.toons, "toons"
10494 &setup.global_animations, "global_animations"
10498 &setup.scroll_delay, "scroll_delay"
10502 &setup.forced_scroll_delay, "forced_scroll_delay"
10506 &setup.scroll_delay_value, "scroll_delay_value"
10510 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10514 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10518 &setup.fade_screens, "fade_screens"
10522 &setup.autorecord, "automatic_tape_recording"
10526 &setup.autorecord_after_replay, "autorecord_after_replay"
10530 &setup.auto_pause_on_start, "auto_pause_on_start"
10534 &setup.show_titlescreen, "show_titlescreen"
10538 &setup.quick_doors, "quick_doors"
10542 &setup.team_mode, "team_mode"
10546 &setup.handicap, "handicap"
10550 &setup.skip_levels, "skip_levels"
10554 &setup.increment_levels, "increment_levels"
10558 &setup.auto_play_next_level, "auto_play_next_level"
10562 &setup.count_score_after_game, "count_score_after_game"
10566 &setup.show_scores_after_game, "show_scores_after_game"
10570 &setup.time_limit, "time_limit"
10574 &setup.fullscreen, "fullscreen"
10578 &setup.window_scaling_percent, "window_scaling_percent"
10582 &setup.window_scaling_quality, "window_scaling_quality"
10586 &setup.screen_rendering_mode, "screen_rendering_mode"
10590 &setup.vsync_mode, "vsync_mode"
10594 &setup.ask_on_escape, "ask_on_escape"
10598 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10602 &setup.ask_on_game_over, "ask_on_game_over"
10606 &setup.ask_on_quit_game, "ask_on_quit_game"
10610 &setup.ask_on_quit_program, "ask_on_quit_program"
10614 &setup.quick_switch, "quick_player_switch"
10618 &setup.input_on_focus, "input_on_focus"
10622 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10626 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10630 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10634 &setup.game_speed_extended, "game_speed_extended"
10638 &setup.game_frame_delay, "game_frame_delay"
10642 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10646 &setup.bd_skip_hatching, "bd_skip_hatching"
10650 &setup.bd_scroll_delay, "bd_scroll_delay"
10654 &setup.bd_smooth_movements, "bd_smooth_movements"
10658 &setup.sp_show_border_elements, "sp_show_border_elements"
10662 &setup.small_game_graphics, "small_game_graphics"
10666 &setup.show_load_save_buttons, "show_load_save_buttons"
10670 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10674 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10678 &setup.graphics_set, "graphics_set"
10682 &setup.sounds_set, "sounds_set"
10686 &setup.music_set, "music_set"
10690 &setup.override_level_graphics, "override_level_graphics"
10694 &setup.override_level_sounds, "override_level_sounds"
10698 &setup.override_level_music, "override_level_music"
10702 &setup.volume_simple, "volume_simple"
10706 &setup.volume_loops, "volume_loops"
10710 &setup.volume_music, "volume_music"
10714 &setup.network_mode, "network_mode"
10718 &setup.network_player_nr, "network_player"
10722 &setup.network_server_hostname, "network_server_hostname"
10726 &setup.touch.control_type, "touch.control_type"
10730 &setup.touch.move_distance, "touch.move_distance"
10734 &setup.touch.drop_distance, "touch.drop_distance"
10738 &setup.touch.transparency, "touch.transparency"
10742 &setup.touch.draw_outlined, "touch.draw_outlined"
10746 &setup.touch.draw_pressed, "touch.draw_pressed"
10750 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10754 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10758 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10762 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10766 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10770 static struct TokenInfo auto_setup_tokens[] =
10774 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10778 static struct TokenInfo server_setup_tokens[] =
10782 &setup.player_uuid, "player_uuid"
10786 &setup.player_version, "player_version"
10790 &setup.use_api_server, TEST_PREFIX "use_api_server"
10794 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10798 &setup.api_server_password, TEST_PREFIX "api_server_password"
10802 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10806 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10810 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10814 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10818 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10822 static struct TokenInfo editor_setup_tokens[] =
10826 &setup.editor.el_classic, "editor.el_classic"
10830 &setup.editor.el_custom, "editor.el_custom"
10834 &setup.editor.el_user_defined, "editor.el_user_defined"
10838 &setup.editor.el_dynamic, "editor.el_dynamic"
10842 &setup.editor.el_headlines, "editor.el_headlines"
10846 &setup.editor.show_element_token, "editor.show_element_token"
10850 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10854 static struct TokenInfo editor_cascade_setup_tokens[] =
10858 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10862 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10866 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10870 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10874 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10878 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10882 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10886 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10890 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10894 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10898 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10902 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10906 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10910 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10914 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10918 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10922 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10926 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10930 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10934 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10938 static struct TokenInfo shortcut_setup_tokens[] =
10942 &setup.shortcut.save_game, "shortcut.save_game"
10946 &setup.shortcut.load_game, "shortcut.load_game"
10950 &setup.shortcut.restart_game, "shortcut.restart_game"
10954 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10958 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10962 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10966 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10970 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10974 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10978 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10982 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10986 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10990 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10994 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10998 &setup.shortcut.tape_record, "shortcut.tape_record"
11002 &setup.shortcut.tape_play, "shortcut.tape_play"
11006 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11010 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11014 &setup.shortcut.sound_music, "shortcut.sound_music"
11018 &setup.shortcut.snap_left, "shortcut.snap_left"
11022 &setup.shortcut.snap_right, "shortcut.snap_right"
11026 &setup.shortcut.snap_up, "shortcut.snap_up"
11030 &setup.shortcut.snap_down, "shortcut.snap_down"
11034 static struct SetupInputInfo setup_input;
11035 static struct TokenInfo player_setup_tokens[] =
11039 &setup_input.use_joystick, ".use_joystick"
11043 &setup_input.joy.device_name, ".joy.device_name"
11047 &setup_input.joy.xleft, ".joy.xleft"
11051 &setup_input.joy.xmiddle, ".joy.xmiddle"
11055 &setup_input.joy.xright, ".joy.xright"
11059 &setup_input.joy.yupper, ".joy.yupper"
11063 &setup_input.joy.ymiddle, ".joy.ymiddle"
11067 &setup_input.joy.ylower, ".joy.ylower"
11071 &setup_input.joy.snap, ".joy.snap_field"
11075 &setup_input.joy.drop, ".joy.place_bomb"
11079 &setup_input.key.left, ".key.move_left"
11083 &setup_input.key.right, ".key.move_right"
11087 &setup_input.key.up, ".key.move_up"
11091 &setup_input.key.down, ".key.move_down"
11095 &setup_input.key.snap, ".key.snap_field"
11099 &setup_input.key.drop, ".key.place_bomb"
11103 static struct TokenInfo system_setup_tokens[] =
11107 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11111 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11115 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11119 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11123 static struct TokenInfo internal_setup_tokens[] =
11127 &setup.internal.program_title, "program_title"
11131 &setup.internal.program_version, "program_version"
11135 &setup.internal.program_author, "program_author"
11139 &setup.internal.program_email, "program_email"
11143 &setup.internal.program_website, "program_website"
11147 &setup.internal.program_copyright, "program_copyright"
11151 &setup.internal.program_company, "program_company"
11155 &setup.internal.program_icon_file, "program_icon_file"
11159 &setup.internal.default_graphics_set, "default_graphics_set"
11163 &setup.internal.default_sounds_set, "default_sounds_set"
11167 &setup.internal.default_music_set, "default_music_set"
11171 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11175 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11179 &setup.internal.fallback_music_file, "fallback_music_file"
11183 &setup.internal.default_level_series, "default_level_series"
11187 &setup.internal.default_window_width, "default_window_width"
11191 &setup.internal.default_window_height, "default_window_height"
11195 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11199 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11203 &setup.internal.create_user_levelset, "create_user_levelset"
11207 &setup.internal.info_screens_from_main, "info_screens_from_main"
11211 &setup.internal.menu_game, "menu_game"
11215 &setup.internal.menu_engines, "menu_engines"
11219 &setup.internal.menu_editor, "menu_editor"
11223 &setup.internal.menu_graphics, "menu_graphics"
11227 &setup.internal.menu_sound, "menu_sound"
11231 &setup.internal.menu_artwork, "menu_artwork"
11235 &setup.internal.menu_input, "menu_input"
11239 &setup.internal.menu_touch, "menu_touch"
11243 &setup.internal.menu_shortcuts, "menu_shortcuts"
11247 &setup.internal.menu_exit, "menu_exit"
11251 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11255 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11259 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11263 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11267 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11271 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11275 &setup.internal.info_title, "info_title"
11279 &setup.internal.info_elements, "info_elements"
11283 &setup.internal.info_music, "info_music"
11287 &setup.internal.info_credits, "info_credits"
11291 &setup.internal.info_program, "info_program"
11295 &setup.internal.info_version, "info_version"
11299 &setup.internal.info_levelset, "info_levelset"
11303 &setup.internal.info_exit, "info_exit"
11307 static struct TokenInfo debug_setup_tokens[] =
11311 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11315 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11319 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11323 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11327 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11331 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11335 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11339 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11343 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11347 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11351 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11355 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11359 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11363 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11367 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11371 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11375 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11379 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11383 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11387 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11391 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11394 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11398 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11402 &setup.debug.xsn_mode, "debug.xsn_mode"
11406 &setup.debug.xsn_percent, "debug.xsn_percent"
11410 static struct TokenInfo options_setup_tokens[] =
11414 &setup.options.verbose, "options.verbose"
11418 &setup.options.debug, "options.debug"
11422 &setup.options.debug_mode, "options.debug_mode"
11426 static void setSetupInfoToDefaults(struct SetupInfo *si)
11430 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11432 si->multiple_users = TRUE;
11435 si->sound_loops = TRUE;
11436 si->sound_music = TRUE;
11437 si->sound_simple = TRUE;
11439 si->global_animations = TRUE;
11440 si->scroll_delay = TRUE;
11441 si->forced_scroll_delay = FALSE;
11442 si->scroll_delay_value = STD_SCROLL_DELAY;
11443 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11444 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11445 si->fade_screens = TRUE;
11446 si->autorecord = TRUE;
11447 si->autorecord_after_replay = TRUE;
11448 si->auto_pause_on_start = FALSE;
11449 si->show_titlescreen = TRUE;
11450 si->quick_doors = FALSE;
11451 si->team_mode = FALSE;
11452 si->handicap = TRUE;
11453 si->skip_levels = TRUE;
11454 si->increment_levels = TRUE;
11455 si->auto_play_next_level = TRUE;
11456 si->count_score_after_game = TRUE;
11457 si->show_scores_after_game = TRUE;
11458 si->time_limit = TRUE;
11459 si->fullscreen = FALSE;
11460 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11461 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11462 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11463 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11464 si->ask_on_escape = TRUE;
11465 si->ask_on_escape_editor = TRUE;
11466 si->ask_on_game_over = TRUE;
11467 si->ask_on_quit_game = TRUE;
11468 si->ask_on_quit_program = TRUE;
11469 si->quick_switch = FALSE;
11470 si->input_on_focus = FALSE;
11471 si->prefer_aga_graphics = TRUE;
11472 si->prefer_lowpass_sounds = FALSE;
11473 si->prefer_extra_panel_items = TRUE;
11474 si->game_speed_extended = FALSE;
11475 si->game_frame_delay = GAME_FRAME_DELAY;
11476 si->bd_skip_uncovering = FALSE;
11477 si->bd_skip_hatching = FALSE;
11478 si->bd_scroll_delay = TRUE;
11479 si->bd_smooth_movements = AUTO;
11480 si->sp_show_border_elements = FALSE;
11481 si->small_game_graphics = FALSE;
11482 si->show_load_save_buttons = FALSE;
11483 si->show_undo_redo_buttons = FALSE;
11484 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11486 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11487 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11488 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11490 si->override_level_graphics = FALSE;
11491 si->override_level_sounds = FALSE;
11492 si->override_level_music = FALSE;
11494 si->volume_simple = 100; // percent
11495 si->volume_loops = 100; // percent
11496 si->volume_music = 100; // percent
11498 si->network_mode = FALSE;
11499 si->network_player_nr = 0; // first player
11500 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11502 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11503 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11504 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11505 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11506 si->touch.draw_outlined = TRUE;
11507 si->touch.draw_pressed = TRUE;
11509 for (i = 0; i < 2; i++)
11511 char *default_grid_button[6][2] =
11517 { "111222", " vv " },
11518 { "111222", " vv " }
11520 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11521 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11522 int min_xsize = MIN(6, grid_xsize);
11523 int min_ysize = MIN(6, grid_ysize);
11524 int startx = grid_xsize - min_xsize;
11525 int starty = grid_ysize - min_ysize;
11528 // virtual buttons grid can only be set to defaults if video is initialized
11529 // (this will be repeated if virtual buttons are not loaded from setup file)
11530 if (video.initialized)
11532 si->touch.grid_xsize[i] = grid_xsize;
11533 si->touch.grid_ysize[i] = grid_ysize;
11537 si->touch.grid_xsize[i] = -1;
11538 si->touch.grid_ysize[i] = -1;
11541 for (x = 0; x < MAX_GRID_XSIZE; x++)
11542 for (y = 0; y < MAX_GRID_YSIZE; y++)
11543 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11545 for (x = 0; x < min_xsize; x++)
11546 for (y = 0; y < min_ysize; y++)
11547 si->touch.grid_button[i][x][starty + y] =
11548 default_grid_button[y][0][x];
11550 for (x = 0; x < min_xsize; x++)
11551 for (y = 0; y < min_ysize; y++)
11552 si->touch.grid_button[i][startx + x][starty + y] =
11553 default_grid_button[y][1][x];
11556 si->touch.grid_initialized = video.initialized;
11558 si->touch.overlay_buttons = FALSE;
11560 si->editor.el_boulderdash = TRUE;
11561 si->editor.el_boulderdash_native = TRUE;
11562 si->editor.el_boulderdash_effects = TRUE;
11563 si->editor.el_emerald_mine = TRUE;
11564 si->editor.el_emerald_mine_club = TRUE;
11565 si->editor.el_more = TRUE;
11566 si->editor.el_sokoban = TRUE;
11567 si->editor.el_supaplex = TRUE;
11568 si->editor.el_diamond_caves = TRUE;
11569 si->editor.el_dx_boulderdash = TRUE;
11571 si->editor.el_mirror_magic = TRUE;
11572 si->editor.el_deflektor = TRUE;
11574 si->editor.el_chars = TRUE;
11575 si->editor.el_steel_chars = TRUE;
11577 si->editor.el_classic = TRUE;
11578 si->editor.el_custom = TRUE;
11580 si->editor.el_user_defined = FALSE;
11581 si->editor.el_dynamic = TRUE;
11583 si->editor.el_headlines = TRUE;
11585 si->editor.show_element_token = FALSE;
11587 si->editor.show_read_only_warning = TRUE;
11589 si->editor.use_template_for_new_levels = TRUE;
11591 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11592 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11593 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11594 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11595 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11597 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11598 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11599 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11600 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11601 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11603 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11604 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11605 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11606 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11607 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11608 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11610 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11611 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11612 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11614 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11615 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11616 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11617 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11619 for (i = 0; i < MAX_PLAYERS; i++)
11621 si->input[i].use_joystick = FALSE;
11622 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11623 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11624 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11625 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11626 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11627 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11628 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11629 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11630 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11631 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11632 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11633 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11634 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11635 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11636 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11639 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11640 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11641 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11642 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11644 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11645 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11646 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11647 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11648 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11649 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11650 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11652 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11654 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11655 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11656 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11658 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11659 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11660 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11662 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11663 si->internal.choose_from_top_leveldir = FALSE;
11664 si->internal.show_scaling_in_title = TRUE;
11665 si->internal.create_user_levelset = TRUE;
11666 si->internal.info_screens_from_main = FALSE;
11668 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11669 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11671 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11672 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11673 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11674 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11675 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11676 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11677 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11678 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11679 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11680 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11682 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11683 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11684 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11685 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11686 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11687 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11688 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11689 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11690 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11691 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11693 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11694 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11696 si->debug.show_frames_per_second = FALSE;
11698 si->debug.xsn_mode = AUTO;
11699 si->debug.xsn_percent = 0;
11701 si->options.verbose = FALSE;
11702 si->options.debug = FALSE;
11703 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11705 #if defined(PLATFORM_ANDROID)
11706 si->fullscreen = TRUE;
11707 si->touch.overlay_buttons = TRUE;
11710 setHideSetupEntry(&setup.debug.xsn_mode);
11713 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11715 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11718 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11720 si->player_uuid = NULL; // (will be set later)
11721 si->player_version = 1; // (will be set later)
11723 si->use_api_server = TRUE;
11724 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11725 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11726 si->ask_for_uploading_tapes = TRUE;
11727 si->ask_for_remaining_tapes = FALSE;
11728 si->provide_uploading_tapes = TRUE;
11729 si->ask_for_using_api_server = TRUE;
11730 si->has_remaining_tapes = FALSE;
11733 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11735 si->editor_cascade.el_bd = TRUE;
11736 si->editor_cascade.el_bd_native = TRUE;
11737 si->editor_cascade.el_bd_effects = FALSE;
11738 si->editor_cascade.el_em = TRUE;
11739 si->editor_cascade.el_emc = TRUE;
11740 si->editor_cascade.el_rnd = TRUE;
11741 si->editor_cascade.el_sb = TRUE;
11742 si->editor_cascade.el_sp = TRUE;
11743 si->editor_cascade.el_dc = TRUE;
11744 si->editor_cascade.el_dx = TRUE;
11746 si->editor_cascade.el_mm = TRUE;
11747 si->editor_cascade.el_df = TRUE;
11749 si->editor_cascade.el_chars = FALSE;
11750 si->editor_cascade.el_steel_chars = FALSE;
11751 si->editor_cascade.el_ce = FALSE;
11752 si->editor_cascade.el_ge = FALSE;
11753 si->editor_cascade.el_es = FALSE;
11754 si->editor_cascade.el_ref = FALSE;
11755 si->editor_cascade.el_user = FALSE;
11756 si->editor_cascade.el_dynamic = FALSE;
11759 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11761 static char *getHideSetupToken(void *setup_value)
11763 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11765 if (setup_value != NULL)
11766 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11768 return hide_setup_token;
11771 void setHideSetupEntry(void *setup_value)
11773 char *hide_setup_token = getHideSetupToken(setup_value);
11775 if (hide_setup_hash == NULL)
11776 hide_setup_hash = newSetupFileHash();
11778 if (setup_value != NULL)
11779 setHashEntry(hide_setup_hash, hide_setup_token, "");
11782 void removeHideSetupEntry(void *setup_value)
11784 char *hide_setup_token = getHideSetupToken(setup_value);
11786 if (setup_value != NULL)
11787 removeHashEntry(hide_setup_hash, hide_setup_token);
11790 boolean hideSetupEntry(void *setup_value)
11792 char *hide_setup_token = getHideSetupToken(setup_value);
11794 return (setup_value != NULL &&
11795 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11798 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11799 struct TokenInfo *token_info,
11800 int token_nr, char *token_text)
11802 char *token_hide_text = getStringCat2(token_text, ".hide");
11803 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11805 // set the value of this setup option in the setup option structure
11806 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11808 // check if this setup option should be hidden in the setup menu
11809 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11810 setHideSetupEntry(token_info[token_nr].value);
11812 free(token_hide_text);
11815 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11816 struct TokenInfo *token_info,
11819 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11820 token_info[token_nr].text);
11823 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11827 if (!setup_file_hash)
11830 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11831 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11833 setup.touch.grid_initialized = TRUE;
11834 for (i = 0; i < 2; i++)
11836 int grid_xsize = setup.touch.grid_xsize[i];
11837 int grid_ysize = setup.touch.grid_ysize[i];
11840 // if virtual buttons are not loaded from setup file, repeat initializing
11841 // virtual buttons grid with default values later when video is initialized
11842 if (grid_xsize == -1 ||
11845 setup.touch.grid_initialized = FALSE;
11850 for (y = 0; y < grid_ysize; y++)
11852 char token_string[MAX_LINE_LEN];
11854 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11856 char *value_string = getHashEntry(setup_file_hash, token_string);
11858 if (value_string == NULL)
11861 for (x = 0; x < grid_xsize; x++)
11863 char c = value_string[x];
11865 setup.touch.grid_button[i][x][y] =
11866 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11871 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11872 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11874 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11875 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11877 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11881 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11883 setup_input = setup.input[pnr];
11884 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11886 char full_token[100];
11888 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11889 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11892 setup.input[pnr] = setup_input;
11895 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11896 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11898 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11899 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11901 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11902 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11904 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11905 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11907 setHideRelatedSetupEntries();
11910 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11914 if (!setup_file_hash)
11917 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11918 setSetupInfo(auto_setup_tokens, i,
11919 getHashEntry(setup_file_hash,
11920 auto_setup_tokens[i].text));
11923 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11927 if (!setup_file_hash)
11930 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11931 setSetupInfo(server_setup_tokens, i,
11932 getHashEntry(setup_file_hash,
11933 server_setup_tokens[i].text));
11936 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11940 if (!setup_file_hash)
11943 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11944 setSetupInfo(editor_cascade_setup_tokens, i,
11945 getHashEntry(setup_file_hash,
11946 editor_cascade_setup_tokens[i].text));
11949 void LoadUserNames(void)
11951 int last_user_nr = user.nr;
11954 if (global.user_names != NULL)
11956 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11957 checked_free(global.user_names[i]);
11959 checked_free(global.user_names);
11962 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11964 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11968 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11970 if (setup_file_hash)
11972 char *player_name = getHashEntry(setup_file_hash, "player_name");
11974 global.user_names[i] = getFixedUserName(player_name);
11976 freeSetupFileHash(setup_file_hash);
11979 if (global.user_names[i] == NULL)
11980 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11983 user.nr = last_user_nr;
11986 void LoadSetupFromFilename(char *filename)
11988 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11990 if (setup_file_hash)
11992 decodeSetupFileHash_Default(setup_file_hash);
11994 freeSetupFileHash(setup_file_hash);
11998 Debug("setup", "using default setup values");
12002 static void LoadSetup_SpecialPostProcessing(void)
12004 char *player_name_new;
12006 // needed to work around problems with fixed length strings
12007 player_name_new = getFixedUserName(setup.player_name);
12008 free(setup.player_name);
12009 setup.player_name = player_name_new;
12011 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12012 if (setup.scroll_delay == FALSE)
12014 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12015 setup.scroll_delay = TRUE; // now always "on"
12018 // make sure that scroll delay value stays inside valid range
12019 setup.scroll_delay_value =
12020 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12023 void LoadSetup_Default(void)
12027 // always start with reliable default values
12028 setSetupInfoToDefaults(&setup);
12030 // try to load setup values from default setup file
12031 filename = getDefaultSetupFilename();
12033 if (fileExists(filename))
12034 LoadSetupFromFilename(filename);
12036 // try to load setup values from platform setup file
12037 filename = getPlatformSetupFilename();
12039 if (fileExists(filename))
12040 LoadSetupFromFilename(filename);
12042 // try to load setup values from user setup file
12043 filename = getSetupFilename();
12045 LoadSetupFromFilename(filename);
12047 LoadSetup_SpecialPostProcessing();
12050 void LoadSetup_AutoSetup(void)
12052 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12053 SetupFileHash *setup_file_hash = NULL;
12055 // always start with reliable default values
12056 setSetupInfoToDefaults_AutoSetup(&setup);
12058 setup_file_hash = loadSetupFileHash(filename);
12060 if (setup_file_hash)
12062 decodeSetupFileHash_AutoSetup(setup_file_hash);
12064 freeSetupFileHash(setup_file_hash);
12070 void LoadSetup_ServerSetup(void)
12072 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12073 SetupFileHash *setup_file_hash = NULL;
12075 // always start with reliable default values
12076 setSetupInfoToDefaults_ServerSetup(&setup);
12078 setup_file_hash = loadSetupFileHash(filename);
12080 if (setup_file_hash)
12082 decodeSetupFileHash_ServerSetup(setup_file_hash);
12084 freeSetupFileHash(setup_file_hash);
12089 if (setup.player_uuid == NULL)
12091 // player UUID does not yet exist in setup file
12092 setup.player_uuid = getStringCopy(getUUID());
12093 setup.player_version = 2;
12095 SaveSetup_ServerSetup();
12099 void LoadSetup_EditorCascade(void)
12101 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12102 SetupFileHash *setup_file_hash = NULL;
12104 // always start with reliable default values
12105 setSetupInfoToDefaults_EditorCascade(&setup);
12107 setup_file_hash = loadSetupFileHash(filename);
12109 if (setup_file_hash)
12111 decodeSetupFileHash_EditorCascade(setup_file_hash);
12113 freeSetupFileHash(setup_file_hash);
12119 void LoadSetup(void)
12121 LoadSetup_Default();
12122 LoadSetup_AutoSetup();
12123 LoadSetup_ServerSetup();
12124 LoadSetup_EditorCascade();
12127 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12128 char *mapping_line)
12130 char mapping_guid[MAX_LINE_LEN];
12131 char *mapping_start, *mapping_end;
12133 // get GUID from game controller mapping line: copy complete line
12134 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12135 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12137 // get GUID from game controller mapping line: cut after GUID part
12138 mapping_start = strchr(mapping_guid, ',');
12139 if (mapping_start != NULL)
12140 *mapping_start = '\0';
12142 // cut newline from game controller mapping line
12143 mapping_end = strchr(mapping_line, '\n');
12144 if (mapping_end != NULL)
12145 *mapping_end = '\0';
12147 // add mapping entry to game controller mappings hash
12148 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12151 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12156 if (!(file = fopen(filename, MODE_READ)))
12158 Warn("cannot read game controller mappings file '%s'", filename);
12163 while (!feof(file))
12165 char line[MAX_LINE_LEN];
12167 if (!fgets(line, MAX_LINE_LEN, file))
12170 addGameControllerMappingToHash(mappings_hash, line);
12176 void SaveSetup_Default(void)
12178 char *filename = getSetupFilename();
12182 InitUserDataDirectory();
12184 if (!(file = fopen(filename, MODE_WRITE)))
12186 Warn("cannot write setup file '%s'", filename);
12191 fprintFileHeader(file, SETUP_FILENAME);
12193 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12195 // just to make things nicer :)
12196 if (global_setup_tokens[i].value == &setup.multiple_users ||
12197 global_setup_tokens[i].value == &setup.sound ||
12198 global_setup_tokens[i].value == &setup.graphics_set ||
12199 global_setup_tokens[i].value == &setup.volume_simple ||
12200 global_setup_tokens[i].value == &setup.network_mode ||
12201 global_setup_tokens[i].value == &setup.touch.control_type ||
12202 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12203 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12204 fprintf(file, "\n");
12206 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12209 for (i = 0; i < 2; i++)
12211 int grid_xsize = setup.touch.grid_xsize[i];
12212 int grid_ysize = setup.touch.grid_ysize[i];
12215 fprintf(file, "\n");
12217 for (y = 0; y < grid_ysize; y++)
12219 char token_string[MAX_LINE_LEN];
12220 char value_string[MAX_LINE_LEN];
12222 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12224 for (x = 0; x < grid_xsize; x++)
12226 char c = setup.touch.grid_button[i][x][y];
12228 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12231 value_string[grid_xsize] = '\0';
12233 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12237 fprintf(file, "\n");
12238 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12239 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12241 fprintf(file, "\n");
12242 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12243 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12245 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12249 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12250 fprintf(file, "\n");
12252 setup_input = setup.input[pnr];
12253 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12254 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12257 fprintf(file, "\n");
12258 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12259 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12261 // (internal setup values not saved to user setup file)
12263 fprintf(file, "\n");
12264 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12265 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12266 setup.debug.xsn_mode != AUTO)
12267 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12269 fprintf(file, "\n");
12270 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12271 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12275 SetFilePermissions(filename, PERMS_PRIVATE);
12278 void SaveSetup_AutoSetup(void)
12280 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12284 InitUserDataDirectory();
12286 if (!(file = fopen(filename, MODE_WRITE)))
12288 Warn("cannot write auto setup file '%s'", filename);
12295 fprintFileHeader(file, AUTOSETUP_FILENAME);
12297 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12298 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12302 SetFilePermissions(filename, PERMS_PRIVATE);
12307 void SaveSetup_ServerSetup(void)
12309 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12313 InitUserDataDirectory();
12315 if (!(file = fopen(filename, MODE_WRITE)))
12317 Warn("cannot write server setup file '%s'", filename);
12324 fprintFileHeader(file, SERVERSETUP_FILENAME);
12326 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12328 // just to make things nicer :)
12329 if (server_setup_tokens[i].value == &setup.use_api_server)
12330 fprintf(file, "\n");
12332 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12337 SetFilePermissions(filename, PERMS_PRIVATE);
12342 void SaveSetup_EditorCascade(void)
12344 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12348 InitUserDataDirectory();
12350 if (!(file = fopen(filename, MODE_WRITE)))
12352 Warn("cannot write editor cascade state file '%s'", filename);
12359 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12361 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12362 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12366 SetFilePermissions(filename, PERMS_PRIVATE);
12371 void SaveSetup(void)
12373 SaveSetup_Default();
12374 SaveSetup_AutoSetup();
12375 SaveSetup_ServerSetup();
12376 SaveSetup_EditorCascade();
12379 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12384 if (!(file = fopen(filename, MODE_WRITE)))
12386 Warn("cannot write game controller mappings file '%s'", filename);
12391 BEGIN_HASH_ITERATION(mappings_hash, itr)
12393 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12395 END_HASH_ITERATION(mappings_hash, itr)
12400 void SaveSetup_AddGameControllerMapping(char *mapping)
12402 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12403 SetupFileHash *mappings_hash = newSetupFileHash();
12405 InitUserDataDirectory();
12407 // load existing personal game controller mappings
12408 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12410 // add new mapping to personal game controller mappings
12411 addGameControllerMappingToHash(mappings_hash, mapping);
12413 // save updated personal game controller mappings
12414 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12416 freeSetupFileHash(mappings_hash);
12420 void LoadCustomElementDescriptions(void)
12422 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12423 SetupFileHash *setup_file_hash;
12426 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12428 if (element_info[i].custom_description != NULL)
12430 free(element_info[i].custom_description);
12431 element_info[i].custom_description = NULL;
12435 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12438 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12440 char *token = getStringCat2(element_info[i].token_name, ".name");
12441 char *value = getHashEntry(setup_file_hash, token);
12444 element_info[i].custom_description = getStringCopy(value);
12449 freeSetupFileHash(setup_file_hash);
12452 static int getElementFromToken(char *token)
12454 char *value = getHashEntry(element_token_hash, token);
12457 return atoi(value);
12459 Warn("unknown element token '%s'", token);
12461 return EL_UNDEFINED;
12464 void FreeGlobalAnimEventInfo(void)
12466 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12468 if (gaei->event_list == NULL)
12473 for (i = 0; i < gaei->num_event_lists; i++)
12475 checked_free(gaei->event_list[i]->event_value);
12476 checked_free(gaei->event_list[i]);
12479 checked_free(gaei->event_list);
12481 gaei->event_list = NULL;
12482 gaei->num_event_lists = 0;
12485 static int AddGlobalAnimEventList(void)
12487 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12488 int list_pos = gaei->num_event_lists++;
12490 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12491 sizeof(struct GlobalAnimEventListInfo *));
12493 gaei->event_list[list_pos] =
12494 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12496 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12498 gaeli->event_value = NULL;
12499 gaeli->num_event_values = 0;
12504 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12506 // do not add empty global animation events
12507 if (event_value == ANIM_EVENT_NONE)
12510 // if list position is undefined, create new list
12511 if (list_pos == ANIM_EVENT_UNDEFINED)
12512 list_pos = AddGlobalAnimEventList();
12514 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12515 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12516 int value_pos = gaeli->num_event_values++;
12518 gaeli->event_value = checked_realloc(gaeli->event_value,
12519 gaeli->num_event_values * sizeof(int *));
12521 gaeli->event_value[value_pos] = event_value;
12526 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12528 if (list_pos == ANIM_EVENT_UNDEFINED)
12529 return ANIM_EVENT_NONE;
12531 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12532 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12534 return gaeli->event_value[value_pos];
12537 int GetGlobalAnimEventValueCount(int list_pos)
12539 if (list_pos == ANIM_EVENT_UNDEFINED)
12542 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12543 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12545 return gaeli->num_event_values;
12548 // This function checks if a string <s> of the format "string1, string2, ..."
12549 // exactly contains a string <s_contained>.
12551 static boolean string_has_parameter(char *s, char *s_contained)
12555 if (s == NULL || s_contained == NULL)
12558 if (strlen(s_contained) > strlen(s))
12561 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12563 char next_char = s[strlen(s_contained)];
12565 // check if next character is delimiter or whitespace
12566 if (next_char == ',' || next_char == '\0' ||
12567 next_char == ' ' || next_char == '\t')
12571 // check if string contains another parameter string after a comma
12572 substring = strchr(s, ',');
12573 if (substring == NULL) // string does not contain a comma
12576 // advance string pointer to next character after the comma
12579 // skip potential whitespaces after the comma
12580 while (*substring == ' ' || *substring == '\t')
12583 return string_has_parameter(substring, s_contained);
12586 static int get_anim_parameter_value_ce(char *s)
12589 char *pattern_1 = "ce_change:custom_";
12590 char *pattern_2 = ".page_";
12591 int pattern_1_len = strlen(pattern_1);
12592 char *matching_char = strstr(s_ptr, pattern_1);
12593 int result = ANIM_EVENT_NONE;
12595 if (matching_char == NULL)
12596 return ANIM_EVENT_NONE;
12598 result = ANIM_EVENT_CE_CHANGE;
12600 s_ptr = matching_char + pattern_1_len;
12602 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12603 if (*s_ptr >= '0' && *s_ptr <= '9')
12605 int gic_ce_nr = (*s_ptr++ - '0');
12607 if (*s_ptr >= '0' && *s_ptr <= '9')
12609 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12611 if (*s_ptr >= '0' && *s_ptr <= '9')
12612 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12615 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12616 return ANIM_EVENT_NONE;
12618 // custom element stored as 0 to 255
12621 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12625 // invalid custom element number specified
12627 return ANIM_EVENT_NONE;
12630 // check for change page number ("page_X" or "page_XX") (optional)
12631 if (strPrefix(s_ptr, pattern_2))
12633 s_ptr += strlen(pattern_2);
12635 if (*s_ptr >= '0' && *s_ptr <= '9')
12637 int gic_page_nr = (*s_ptr++ - '0');
12639 if (*s_ptr >= '0' && *s_ptr <= '9')
12640 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12642 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12643 return ANIM_EVENT_NONE;
12645 // change page stored as 1 to 32 (0 means "all change pages")
12647 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12651 // invalid animation part number specified
12653 return ANIM_EVENT_NONE;
12657 // discard result if next character is neither delimiter nor whitespace
12658 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12659 *s_ptr == ' ' || *s_ptr == '\t'))
12660 return ANIM_EVENT_NONE;
12665 static int get_anim_parameter_value(char *s)
12667 int event_value[] =
12675 char *pattern_1[] =
12683 char *pattern_2 = ".part_";
12684 char *matching_char = NULL;
12686 int pattern_1_len = 0;
12687 int result = ANIM_EVENT_NONE;
12690 result = get_anim_parameter_value_ce(s);
12692 if (result != ANIM_EVENT_NONE)
12695 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12697 matching_char = strstr(s_ptr, pattern_1[i]);
12698 pattern_1_len = strlen(pattern_1[i]);
12699 result = event_value[i];
12701 if (matching_char != NULL)
12705 if (matching_char == NULL)
12706 return ANIM_EVENT_NONE;
12708 s_ptr = matching_char + pattern_1_len;
12710 // check for main animation number ("anim_X" or "anim_XX")
12711 if (*s_ptr >= '0' && *s_ptr <= '9')
12713 int gic_anim_nr = (*s_ptr++ - '0');
12715 if (*s_ptr >= '0' && *s_ptr <= '9')
12716 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12718 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12719 return ANIM_EVENT_NONE;
12721 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12725 // invalid main animation number specified
12727 return ANIM_EVENT_NONE;
12730 // check for animation part number ("part_X" or "part_XX") (optional)
12731 if (strPrefix(s_ptr, pattern_2))
12733 s_ptr += strlen(pattern_2);
12735 if (*s_ptr >= '0' && *s_ptr <= '9')
12737 int gic_part_nr = (*s_ptr++ - '0');
12739 if (*s_ptr >= '0' && *s_ptr <= '9')
12740 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12742 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12743 return ANIM_EVENT_NONE;
12745 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12749 // invalid animation part number specified
12751 return ANIM_EVENT_NONE;
12755 // discard result if next character is neither delimiter nor whitespace
12756 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12757 *s_ptr == ' ' || *s_ptr == '\t'))
12758 return ANIM_EVENT_NONE;
12763 static int get_anim_parameter_values(char *s)
12765 int list_pos = ANIM_EVENT_UNDEFINED;
12766 int event_value = ANIM_EVENT_DEFAULT;
12768 if (string_has_parameter(s, "any"))
12769 event_value |= ANIM_EVENT_ANY;
12771 if (string_has_parameter(s, "click:self") ||
12772 string_has_parameter(s, "click") ||
12773 string_has_parameter(s, "self"))
12774 event_value |= ANIM_EVENT_SELF;
12776 if (string_has_parameter(s, "unclick:any"))
12777 event_value |= ANIM_EVENT_UNCLICK_ANY;
12779 // if animation event found, add it to global animation event list
12780 if (event_value != ANIM_EVENT_NONE)
12781 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12785 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12786 event_value = get_anim_parameter_value(s);
12788 // if animation event found, add it to global animation event list
12789 if (event_value != ANIM_EVENT_NONE)
12790 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12792 // continue with next part of the string, starting with next comma
12793 s = strchr(s + 1, ',');
12799 static int get_anim_action_parameter_value(char *token)
12801 // check most common default case first to massively speed things up
12802 if (strEqual(token, ARG_UNDEFINED))
12803 return ANIM_EVENT_ACTION_NONE;
12805 int result = getImageIDFromToken(token);
12809 char *gfx_token = getStringCat2("gfx.", token);
12811 result = getImageIDFromToken(gfx_token);
12813 checked_free(gfx_token);
12818 Key key = getKeyFromX11KeyName(token);
12820 if (key != KSYM_UNDEFINED)
12821 result = -(int)key;
12828 result = get_hash_from_string(token); // unsigned int => int
12829 result = ABS(result); // may be negative now
12830 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12832 setHashEntry(anim_url_hash, int2str(result, 0), token);
12837 result = ANIM_EVENT_ACTION_NONE;
12842 int get_parameter_value(char *value_raw, char *suffix, int type)
12844 char *value = getStringToLower(value_raw);
12845 int result = 0; // probably a save default value
12847 if (strEqual(suffix, ".direction"))
12849 result = (strEqual(value, "left") ? MV_LEFT :
12850 strEqual(value, "right") ? MV_RIGHT :
12851 strEqual(value, "up") ? MV_UP :
12852 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12854 else if (strEqual(suffix, ".position"))
12856 result = (strEqual(value, "left") ? POS_LEFT :
12857 strEqual(value, "right") ? POS_RIGHT :
12858 strEqual(value, "top") ? POS_TOP :
12859 strEqual(value, "upper") ? POS_UPPER :
12860 strEqual(value, "middle") ? POS_MIDDLE :
12861 strEqual(value, "lower") ? POS_LOWER :
12862 strEqual(value, "bottom") ? POS_BOTTOM :
12863 strEqual(value, "any") ? POS_ANY :
12864 strEqual(value, "ce") ? POS_CE :
12865 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12866 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12868 else if (strEqual(suffix, ".align"))
12870 result = (strEqual(value, "left") ? ALIGN_LEFT :
12871 strEqual(value, "right") ? ALIGN_RIGHT :
12872 strEqual(value, "center") ? ALIGN_CENTER :
12873 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12875 else if (strEqual(suffix, ".valign"))
12877 result = (strEqual(value, "top") ? VALIGN_TOP :
12878 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12879 strEqual(value, "middle") ? VALIGN_MIDDLE :
12880 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12882 else if (strEqual(suffix, ".anim_mode"))
12884 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12885 string_has_parameter(value, "loop") ? ANIM_LOOP :
12886 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12887 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12888 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12889 string_has_parameter(value, "random") ? ANIM_RANDOM :
12890 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12891 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12892 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12893 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12894 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12895 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12896 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12897 string_has_parameter(value, "all") ? ANIM_ALL :
12898 string_has_parameter(value, "tiled") ? ANIM_TILED :
12899 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12902 if (string_has_parameter(value, "once"))
12903 result |= ANIM_ONCE;
12905 if (string_has_parameter(value, "reverse"))
12906 result |= ANIM_REVERSE;
12908 if (string_has_parameter(value, "opaque_player"))
12909 result |= ANIM_OPAQUE_PLAYER;
12911 if (string_has_parameter(value, "static_panel"))
12912 result |= ANIM_STATIC_PANEL;
12914 else if (strEqual(suffix, ".init_event") ||
12915 strEqual(suffix, ".anim_event"))
12917 result = get_anim_parameter_values(value);
12919 else if (strEqual(suffix, ".init_delay_action") ||
12920 strEqual(suffix, ".anim_delay_action") ||
12921 strEqual(suffix, ".post_delay_action") ||
12922 strEqual(suffix, ".init_event_action") ||
12923 strEqual(suffix, ".anim_event_action"))
12925 result = get_anim_action_parameter_value(value_raw);
12927 else if (strEqual(suffix, ".class"))
12929 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12930 get_hash_from_string(value));
12932 else if (strEqual(suffix, ".style"))
12934 result = STYLE_DEFAULT;
12936 if (string_has_parameter(value, "accurate_borders"))
12937 result |= STYLE_ACCURATE_BORDERS;
12939 if (string_has_parameter(value, "inner_corners"))
12940 result |= STYLE_INNER_CORNERS;
12942 if (string_has_parameter(value, "reverse"))
12943 result |= STYLE_REVERSE;
12945 if (string_has_parameter(value, "leftmost_position"))
12946 result |= STYLE_LEFTMOST_POSITION;
12948 if (string_has_parameter(value, "block_clicks"))
12949 result |= STYLE_BLOCK;
12951 if (string_has_parameter(value, "passthrough_clicks"))
12952 result |= STYLE_PASSTHROUGH;
12954 if (string_has_parameter(value, "multiple_actions"))
12955 result |= STYLE_MULTIPLE_ACTIONS;
12957 if (string_has_parameter(value, "consume_ce_event"))
12958 result |= STYLE_CONSUME_CE_EVENT;
12960 else if (strEqual(suffix, ".fade_mode"))
12962 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12963 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12964 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12965 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12966 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12967 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12968 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12969 FADE_MODE_DEFAULT);
12971 else if (strEqual(suffix, ".auto_delay_unit"))
12973 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12974 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12975 AUTO_DELAY_UNIT_DEFAULT);
12977 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12979 result = gfx.get_font_from_token_function(value);
12981 else // generic parameter of type integer or boolean
12983 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12984 type == TYPE_INTEGER ? get_integer_from_string(value) :
12985 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12986 ARG_UNDEFINED_VALUE);
12994 static int get_token_parameter_value(char *token, char *value_raw)
12998 if (token == NULL || value_raw == NULL)
12999 return ARG_UNDEFINED_VALUE;
13001 suffix = strrchr(token, '.');
13002 if (suffix == NULL)
13005 if (strEqual(suffix, ".element"))
13006 return getElementFromToken(value_raw);
13008 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13009 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13012 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13013 boolean ignore_defaults)
13017 for (i = 0; image_config_vars[i].token != NULL; i++)
13019 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13021 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13022 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13026 *image_config_vars[i].value =
13027 get_token_parameter_value(image_config_vars[i].token, value);
13031 void InitMenuDesignSettings_Static(void)
13033 // always start with reliable default values from static default config
13034 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13037 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13041 // the following initializes hierarchical values from static configuration
13043 // special case: initialize "ARG_DEFAULT" values in static default config
13044 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13045 titlescreen_initial_first_default.fade_mode =
13046 title_initial_first_default.fade_mode;
13047 titlescreen_initial_first_default.fade_delay =
13048 title_initial_first_default.fade_delay;
13049 titlescreen_initial_first_default.post_delay =
13050 title_initial_first_default.post_delay;
13051 titlescreen_initial_first_default.auto_delay =
13052 title_initial_first_default.auto_delay;
13053 titlescreen_initial_first_default.auto_delay_unit =
13054 title_initial_first_default.auto_delay_unit;
13055 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13056 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13057 titlescreen_first_default.post_delay = title_first_default.post_delay;
13058 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13059 titlescreen_first_default.auto_delay_unit =
13060 title_first_default.auto_delay_unit;
13061 titlemessage_initial_first_default.fade_mode =
13062 title_initial_first_default.fade_mode;
13063 titlemessage_initial_first_default.fade_delay =
13064 title_initial_first_default.fade_delay;
13065 titlemessage_initial_first_default.post_delay =
13066 title_initial_first_default.post_delay;
13067 titlemessage_initial_first_default.auto_delay =
13068 title_initial_first_default.auto_delay;
13069 titlemessage_initial_first_default.auto_delay_unit =
13070 title_initial_first_default.auto_delay_unit;
13071 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13072 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13073 titlemessage_first_default.post_delay = title_first_default.post_delay;
13074 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13075 titlemessage_first_default.auto_delay_unit =
13076 title_first_default.auto_delay_unit;
13078 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13079 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13080 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13081 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13082 titlescreen_initial_default.auto_delay_unit =
13083 title_initial_default.auto_delay_unit;
13084 titlescreen_default.fade_mode = title_default.fade_mode;
13085 titlescreen_default.fade_delay = title_default.fade_delay;
13086 titlescreen_default.post_delay = title_default.post_delay;
13087 titlescreen_default.auto_delay = title_default.auto_delay;
13088 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13089 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13090 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13091 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13092 titlemessage_initial_default.auto_delay_unit =
13093 title_initial_default.auto_delay_unit;
13094 titlemessage_default.fade_mode = title_default.fade_mode;
13095 titlemessage_default.fade_delay = title_default.fade_delay;
13096 titlemessage_default.post_delay = title_default.post_delay;
13097 titlemessage_default.auto_delay = title_default.auto_delay;
13098 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13100 // special case: initialize "ARG_DEFAULT" values in static default config
13101 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13102 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13104 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13105 titlescreen_first[i] = titlescreen_first_default;
13106 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13107 titlemessage_first[i] = titlemessage_first_default;
13109 titlescreen_initial[i] = titlescreen_initial_default;
13110 titlescreen[i] = titlescreen_default;
13111 titlemessage_initial[i] = titlemessage_initial_default;
13112 titlemessage[i] = titlemessage_default;
13115 // special case: initialize "ARG_DEFAULT" values in static default config
13116 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13117 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13119 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13122 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13123 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13124 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13127 // special case: initialize "ARG_DEFAULT" values in static default config
13128 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13129 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13131 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13132 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13133 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13135 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13138 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13142 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13146 struct XY *dst, *src;
13148 game_buttons_xy[] =
13150 { &game.button.save, &game.button.stop },
13151 { &game.button.pause2, &game.button.pause },
13152 { &game.button.load, &game.button.play },
13153 { &game.button.undo, &game.button.stop },
13154 { &game.button.redo, &game.button.play },
13160 // special case: initialize later added SETUP list size from LEVELS value
13161 if (menu.list_size[GAME_MODE_SETUP] == -1)
13162 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13164 // set default position for snapshot buttons to stop/pause/play buttons
13165 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13166 if ((*game_buttons_xy[i].dst).x == -1 &&
13167 (*game_buttons_xy[i].dst).y == -1)
13168 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13170 // --------------------------------------------------------------------------
13171 // dynamic viewports (including playfield margins, borders and alignments)
13172 // --------------------------------------------------------------------------
13174 // dynamic viewports currently only supported for landscape mode
13175 int display_width = MAX(video.display_width, video.display_height);
13176 int display_height = MIN(video.display_width, video.display_height);
13178 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13180 struct RectWithBorder *vp_window = &viewport.window[i];
13181 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13182 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13183 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13184 boolean dynamic_window_width = (vp_window->min_width != -1);
13185 boolean dynamic_window_height = (vp_window->min_height != -1);
13186 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13187 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13189 // adjust window size if min/max width/height is specified
13191 if (vp_window->min_width != -1)
13193 int window_width = display_width;
13195 // when using static window height, use aspect ratio of display
13196 if (vp_window->min_height == -1)
13197 window_width = vp_window->height * display_width / display_height;
13199 vp_window->width = MAX(vp_window->min_width, window_width);
13202 if (vp_window->min_height != -1)
13204 int window_height = display_height;
13206 // when using static window width, use aspect ratio of display
13207 if (vp_window->min_width == -1)
13208 window_height = vp_window->width * display_height / display_width;
13210 vp_window->height = MAX(vp_window->min_height, window_height);
13213 if (vp_window->max_width != -1)
13214 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13216 if (vp_window->max_height != -1)
13217 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13219 int playfield_width = vp_window->width;
13220 int playfield_height = vp_window->height;
13222 // adjust playfield size and position according to specified margins
13224 playfield_width -= vp_playfield->margin_left;
13225 playfield_width -= vp_playfield->margin_right;
13227 playfield_height -= vp_playfield->margin_top;
13228 playfield_height -= vp_playfield->margin_bottom;
13230 // adjust playfield size if min/max width/height is specified
13232 if (vp_playfield->min_width != -1)
13233 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13235 if (vp_playfield->min_height != -1)
13236 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13238 if (vp_playfield->max_width != -1)
13239 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13241 if (vp_playfield->max_height != -1)
13242 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13244 // adjust playfield position according to specified alignment
13246 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13247 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13248 else if (vp_playfield->align == ALIGN_CENTER)
13249 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13250 else if (vp_playfield->align == ALIGN_RIGHT)
13251 vp_playfield->x += playfield_width - vp_playfield->width;
13253 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13254 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13255 else if (vp_playfield->valign == VALIGN_MIDDLE)
13256 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13257 else if (vp_playfield->valign == VALIGN_BOTTOM)
13258 vp_playfield->y += playfield_height - vp_playfield->height;
13260 vp_playfield->x += vp_playfield->margin_left;
13261 vp_playfield->y += vp_playfield->margin_top;
13263 // adjust individual playfield borders if only default border is specified
13265 if (vp_playfield->border_left == -1)
13266 vp_playfield->border_left = vp_playfield->border_size;
13267 if (vp_playfield->border_right == -1)
13268 vp_playfield->border_right = vp_playfield->border_size;
13269 if (vp_playfield->border_top == -1)
13270 vp_playfield->border_top = vp_playfield->border_size;
13271 if (vp_playfield->border_bottom == -1)
13272 vp_playfield->border_bottom = vp_playfield->border_size;
13274 // set dynamic playfield borders if borders are specified as undefined
13275 // (but only if window size was dynamic and playfield size was static)
13277 if (dynamic_window_width && !dynamic_playfield_width)
13279 if (vp_playfield->border_left == -1)
13281 vp_playfield->border_left = (vp_playfield->x -
13282 vp_playfield->margin_left);
13283 vp_playfield->x -= vp_playfield->border_left;
13284 vp_playfield->width += vp_playfield->border_left;
13287 if (vp_playfield->border_right == -1)
13289 vp_playfield->border_right = (vp_window->width -
13291 vp_playfield->width -
13292 vp_playfield->margin_right);
13293 vp_playfield->width += vp_playfield->border_right;
13297 if (dynamic_window_height && !dynamic_playfield_height)
13299 if (vp_playfield->border_top == -1)
13301 vp_playfield->border_top = (vp_playfield->y -
13302 vp_playfield->margin_top);
13303 vp_playfield->y -= vp_playfield->border_top;
13304 vp_playfield->height += vp_playfield->border_top;
13307 if (vp_playfield->border_bottom == -1)
13309 vp_playfield->border_bottom = (vp_window->height -
13311 vp_playfield->height -
13312 vp_playfield->margin_bottom);
13313 vp_playfield->height += vp_playfield->border_bottom;
13317 // adjust playfield size to be a multiple of a defined alignment tile size
13319 int align_size = vp_playfield->align_size;
13320 int playfield_xtiles = vp_playfield->width / align_size;
13321 int playfield_ytiles = vp_playfield->height / align_size;
13322 int playfield_width_corrected = playfield_xtiles * align_size;
13323 int playfield_height_corrected = playfield_ytiles * align_size;
13324 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13325 i == GFX_SPECIAL_ARG_EDITOR);
13327 if (is_playfield_mode &&
13328 dynamic_playfield_width &&
13329 vp_playfield->width != playfield_width_corrected)
13331 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13333 vp_playfield->width = playfield_width_corrected;
13335 if (vp_playfield->align == ALIGN_LEFT)
13337 vp_playfield->border_left += playfield_xdiff;
13339 else if (vp_playfield->align == ALIGN_RIGHT)
13341 vp_playfield->border_right += playfield_xdiff;
13343 else if (vp_playfield->align == ALIGN_CENTER)
13345 int border_left_diff = playfield_xdiff / 2;
13346 int border_right_diff = playfield_xdiff - border_left_diff;
13348 vp_playfield->border_left += border_left_diff;
13349 vp_playfield->border_right += border_right_diff;
13353 if (is_playfield_mode &&
13354 dynamic_playfield_height &&
13355 vp_playfield->height != playfield_height_corrected)
13357 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13359 vp_playfield->height = playfield_height_corrected;
13361 if (vp_playfield->valign == VALIGN_TOP)
13363 vp_playfield->border_top += playfield_ydiff;
13365 else if (vp_playfield->align == VALIGN_BOTTOM)
13367 vp_playfield->border_right += playfield_ydiff;
13369 else if (vp_playfield->align == VALIGN_MIDDLE)
13371 int border_top_diff = playfield_ydiff / 2;
13372 int border_bottom_diff = playfield_ydiff - border_top_diff;
13374 vp_playfield->border_top += border_top_diff;
13375 vp_playfield->border_bottom += border_bottom_diff;
13379 // adjust door positions according to specified alignment
13381 for (j = 0; j < 2; j++)
13383 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13385 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13386 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13387 else if (vp_door->align == ALIGN_CENTER)
13388 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13389 else if (vp_door->align == ALIGN_RIGHT)
13390 vp_door->x += vp_window->width - vp_door->width;
13392 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13393 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13394 else if (vp_door->valign == VALIGN_MIDDLE)
13395 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13396 else if (vp_door->valign == VALIGN_BOTTOM)
13397 vp_door->y += vp_window->height - vp_door->height;
13402 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13406 struct XYTileSize *dst, *src;
13409 editor_buttons_xy[] =
13412 &editor.button.element_left, &editor.palette.element_left,
13413 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13416 &editor.button.element_middle, &editor.palette.element_middle,
13417 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13420 &editor.button.element_right, &editor.palette.element_right,
13421 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13428 // set default position for element buttons to element graphics
13429 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13431 if ((*editor_buttons_xy[i].dst).x == -1 &&
13432 (*editor_buttons_xy[i].dst).y == -1)
13434 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13436 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13438 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13442 // adjust editor palette rows and columns if specified to be dynamic
13444 if (editor.palette.cols == -1)
13446 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13447 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13448 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13450 editor.palette.cols = (vp_width - sc_width) / bt_width;
13452 if (editor.palette.x == -1)
13454 int palette_width = editor.palette.cols * bt_width + sc_width;
13456 editor.palette.x = (vp_width - palette_width) / 2;
13460 if (editor.palette.rows == -1)
13462 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13463 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13464 int tx_height = getFontHeight(FONT_TEXT_2);
13466 editor.palette.rows = (vp_height - tx_height) / bt_height;
13468 if (editor.palette.y == -1)
13470 int palette_height = editor.palette.rows * bt_height + tx_height;
13472 editor.palette.y = (vp_height - palette_height) / 2;
13477 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13478 boolean initialize)
13480 // special case: check if network and preview player positions are redefined,
13481 // to compare this later against the main menu level preview being redefined
13482 struct TokenIntPtrInfo menu_config_players[] =
13484 { "main.network_players.x", &menu.main.network_players.redefined },
13485 { "main.network_players.y", &menu.main.network_players.redefined },
13486 { "main.preview_players.x", &menu.main.preview_players.redefined },
13487 { "main.preview_players.y", &menu.main.preview_players.redefined },
13488 { "preview.x", &preview.redefined },
13489 { "preview.y", &preview.redefined }
13495 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13496 *menu_config_players[i].value = FALSE;
13500 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13501 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13502 *menu_config_players[i].value = TRUE;
13506 static void InitMenuDesignSettings_PreviewPlayers(void)
13508 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13511 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13513 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13516 static void LoadMenuDesignSettingsFromFilename(char *filename)
13518 static struct TitleFadingInfo tfi;
13519 static struct TitleMessageInfo tmi;
13520 static struct TokenInfo title_tokens[] =
13522 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13523 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13524 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13525 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13526 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13530 static struct TokenInfo titlemessage_tokens[] =
13532 { TYPE_INTEGER, &tmi.x, ".x" },
13533 { TYPE_INTEGER, &tmi.y, ".y" },
13534 { TYPE_INTEGER, &tmi.width, ".width" },
13535 { TYPE_INTEGER, &tmi.height, ".height" },
13536 { TYPE_INTEGER, &tmi.chars, ".chars" },
13537 { TYPE_INTEGER, &tmi.lines, ".lines" },
13538 { TYPE_INTEGER, &tmi.align, ".align" },
13539 { TYPE_INTEGER, &tmi.valign, ".valign" },
13540 { TYPE_INTEGER, &tmi.font, ".font" },
13541 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13542 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13543 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13544 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13545 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13546 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13547 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13548 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13549 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13555 struct TitleFadingInfo *info;
13560 // initialize first titles from "enter screen" definitions, if defined
13561 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13562 { &title_first_default, "menu.enter_screen.TITLE" },
13564 // initialize title screens from "next screen" definitions, if defined
13565 { &title_initial_default, "menu.next_screen.TITLE" },
13566 { &title_default, "menu.next_screen.TITLE" },
13572 struct TitleMessageInfo *array;
13575 titlemessage_arrays[] =
13577 // initialize first titles from "enter screen" definitions, if defined
13578 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13579 { titlescreen_first, "menu.enter_screen.TITLE" },
13580 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13581 { titlemessage_first, "menu.enter_screen.TITLE" },
13583 // initialize titles from "next screen" definitions, if defined
13584 { titlescreen_initial, "menu.next_screen.TITLE" },
13585 { titlescreen, "menu.next_screen.TITLE" },
13586 { titlemessage_initial, "menu.next_screen.TITLE" },
13587 { titlemessage, "menu.next_screen.TITLE" },
13589 // overwrite titles with title definitions, if defined
13590 { titlescreen_initial_first, "[title_initial]" },
13591 { titlescreen_first, "[title]" },
13592 { titlemessage_initial_first, "[title_initial]" },
13593 { titlemessage_first, "[title]" },
13595 { titlescreen_initial, "[title_initial]" },
13596 { titlescreen, "[title]" },
13597 { titlemessage_initial, "[title_initial]" },
13598 { titlemessage, "[title]" },
13600 // overwrite titles with title screen/message definitions, if defined
13601 { titlescreen_initial_first, "[titlescreen_initial]" },
13602 { titlescreen_first, "[titlescreen]" },
13603 { titlemessage_initial_first, "[titlemessage_initial]" },
13604 { titlemessage_first, "[titlemessage]" },
13606 { titlescreen_initial, "[titlescreen_initial]" },
13607 { titlescreen, "[titlescreen]" },
13608 { titlemessage_initial, "[titlemessage_initial]" },
13609 { titlemessage, "[titlemessage]" },
13613 SetupFileHash *setup_file_hash;
13616 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13619 // the following initializes hierarchical values from dynamic configuration
13621 // special case: initialize with default values that may be overwritten
13622 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13623 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13625 struct TokenIntPtrInfo menu_config[] =
13627 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13628 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13629 { "menu.list_size", &menu.list_size[i] }
13632 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13634 char *token = menu_config[j].token;
13635 char *value = getHashEntry(setup_file_hash, token);
13638 *menu_config[j].value = get_integer_from_string(value);
13642 // special case: initialize with default values that may be overwritten
13643 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13644 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13646 struct TokenIntPtrInfo menu_config[] =
13648 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13649 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13650 { "menu.list_size.INFO", &menu.list_size_info[i] },
13651 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13652 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13655 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13657 char *token = menu_config[j].token;
13658 char *value = getHashEntry(setup_file_hash, token);
13661 *menu_config[j].value = get_integer_from_string(value);
13665 // special case: initialize with default values that may be overwritten
13666 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13667 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13669 struct TokenIntPtrInfo menu_config[] =
13671 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13672 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13675 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13677 char *token = menu_config[j].token;
13678 char *value = getHashEntry(setup_file_hash, token);
13681 *menu_config[j].value = get_integer_from_string(value);
13685 // special case: initialize with default values that may be overwritten
13686 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13687 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13689 struct TokenIntPtrInfo menu_config[] =
13691 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13692 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13693 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13694 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13695 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13696 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13697 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13698 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13699 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13700 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13703 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13705 char *token = menu_config[j].token;
13706 char *value = getHashEntry(setup_file_hash, token);
13709 *menu_config[j].value = get_integer_from_string(value);
13713 // special case: initialize with default values that may be overwritten
13714 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13715 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13717 struct TokenIntPtrInfo menu_config[] =
13719 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13720 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13721 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13722 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13723 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13724 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13725 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13726 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13727 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13730 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13732 char *token = menu_config[j].token;
13733 char *value = getHashEntry(setup_file_hash, token);
13736 *menu_config[j].value = get_token_parameter_value(token, value);
13740 // special case: initialize with default values that may be overwritten
13741 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13742 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13746 char *token_prefix;
13747 struct RectWithBorder *struct_ptr;
13751 { "viewport.window", &viewport.window[i] },
13752 { "viewport.playfield", &viewport.playfield[i] },
13753 { "viewport.door_1", &viewport.door_1[i] },
13754 { "viewport.door_2", &viewport.door_2[i] }
13757 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13759 struct TokenIntPtrInfo vp_config[] =
13761 { ".x", &vp_struct[j].struct_ptr->x },
13762 { ".y", &vp_struct[j].struct_ptr->y },
13763 { ".width", &vp_struct[j].struct_ptr->width },
13764 { ".height", &vp_struct[j].struct_ptr->height },
13765 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13766 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13767 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13768 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13769 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13770 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13771 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13772 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13773 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13774 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13775 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13776 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13777 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13778 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13779 { ".align", &vp_struct[j].struct_ptr->align },
13780 { ".valign", &vp_struct[j].struct_ptr->valign }
13783 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13785 char *token = getStringCat2(vp_struct[j].token_prefix,
13786 vp_config[k].token);
13787 char *value = getHashEntry(setup_file_hash, token);
13790 *vp_config[k].value = get_token_parameter_value(token, value);
13797 // special case: initialize with default values that may be overwritten
13798 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13799 for (i = 0; title_info[i].info != NULL; i++)
13801 struct TitleFadingInfo *info = title_info[i].info;
13802 char *base_token = title_info[i].text;
13804 for (j = 0; title_tokens[j].type != -1; j++)
13806 char *token = getStringCat2(base_token, title_tokens[j].text);
13807 char *value = getHashEntry(setup_file_hash, token);
13811 int parameter_value = get_token_parameter_value(token, value);
13815 *(int *)title_tokens[j].value = (int)parameter_value;
13824 // special case: initialize with default values that may be overwritten
13825 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13826 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13828 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13829 char *base_token = titlemessage_arrays[i].text;
13831 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13833 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13834 char *value = getHashEntry(setup_file_hash, token);
13838 int parameter_value = get_token_parameter_value(token, value);
13840 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13844 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13845 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13847 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13857 // read (and overwrite with) values that may be specified in config file
13858 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13860 // special case: check if network and preview player positions are redefined
13861 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13863 freeSetupFileHash(setup_file_hash);
13866 void LoadMenuDesignSettings(void)
13868 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13870 InitMenuDesignSettings_Static();
13871 InitMenuDesignSettings_SpecialPreProcessing();
13872 InitMenuDesignSettings_PreviewPlayers();
13874 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13876 // first look for special settings configured in level series config
13877 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13879 if (fileExists(filename_base))
13880 LoadMenuDesignSettingsFromFilename(filename_base);
13883 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13885 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13886 LoadMenuDesignSettingsFromFilename(filename_local);
13888 InitMenuDesignSettings_SpecialPostProcessing();
13891 void LoadMenuDesignSettings_AfterGraphics(void)
13893 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13896 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13897 boolean ignore_defaults)
13901 for (i = 0; sound_config_vars[i].token != NULL; i++)
13903 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13905 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13906 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13910 *sound_config_vars[i].value =
13911 get_token_parameter_value(sound_config_vars[i].token, value);
13915 void InitSoundSettings_Static(void)
13917 // always start with reliable default values from static default config
13918 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13921 static void LoadSoundSettingsFromFilename(char *filename)
13923 SetupFileHash *setup_file_hash;
13925 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13928 // read (and overwrite with) values that may be specified in config file
13929 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13931 freeSetupFileHash(setup_file_hash);
13934 void LoadSoundSettings(void)
13936 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13938 InitSoundSettings_Static();
13940 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13942 // first look for special settings configured in level series config
13943 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13945 if (fileExists(filename_base))
13946 LoadSoundSettingsFromFilename(filename_base);
13949 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13951 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13952 LoadSoundSettingsFromFilename(filename_local);
13955 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13957 char *filename = getEditorSetupFilename();
13958 SetupFileList *setup_file_list, *list;
13959 SetupFileHash *element_hash;
13960 int num_unknown_tokens = 0;
13963 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13966 element_hash = newSetupFileHash();
13968 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13969 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13971 // determined size may be larger than needed (due to unknown elements)
13973 for (list = setup_file_list; list != NULL; list = list->next)
13976 // add space for up to 3 more elements for padding that may be needed
13977 *num_elements += 3;
13979 // free memory for old list of elements, if needed
13980 checked_free(*elements);
13982 // allocate memory for new list of elements
13983 *elements = checked_malloc(*num_elements * sizeof(int));
13986 for (list = setup_file_list; list != NULL; list = list->next)
13988 char *value = getHashEntry(element_hash, list->token);
13990 if (value == NULL) // try to find obsolete token mapping
13992 char *mapped_token = get_mapped_token(list->token);
13994 if (mapped_token != NULL)
13996 value = getHashEntry(element_hash, mapped_token);
13998 free(mapped_token);
14004 (*elements)[(*num_elements)++] = atoi(value);
14008 if (num_unknown_tokens == 0)
14011 Warn("unknown token(s) found in config file:");
14012 Warn("- config file: '%s'", filename);
14014 num_unknown_tokens++;
14017 Warn("- token: '%s'", list->token);
14021 if (num_unknown_tokens > 0)
14024 while (*num_elements % 4) // pad with empty elements, if needed
14025 (*elements)[(*num_elements)++] = EL_EMPTY;
14027 freeSetupFileList(setup_file_list);
14028 freeSetupFileHash(element_hash);
14031 for (i = 0; i < *num_elements; i++)
14032 Debug("editor", "element '%s' [%d]\n",
14033 element_info[(*elements)[i]].token_name, (*elements)[i]);
14037 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14040 SetupFileHash *setup_file_hash = NULL;
14041 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14042 char *filename_music, *filename_prefix, *filename_info;
14048 token_to_value_ptr[] =
14050 { "title_header", &tmp_music_file_info.title_header },
14051 { "artist_header", &tmp_music_file_info.artist_header },
14052 { "album_header", &tmp_music_file_info.album_header },
14053 { "year_header", &tmp_music_file_info.year_header },
14054 { "played_header", &tmp_music_file_info.played_header },
14056 { "title", &tmp_music_file_info.title },
14057 { "artist", &tmp_music_file_info.artist },
14058 { "album", &tmp_music_file_info.album },
14059 { "year", &tmp_music_file_info.year },
14060 { "played", &tmp_music_file_info.played },
14066 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14067 getCustomMusicFilename(basename));
14069 if (filename_music == NULL)
14072 // ---------- try to replace file extension ----------
14074 filename_prefix = getStringCopy(filename_music);
14075 if (strrchr(filename_prefix, '.') != NULL)
14076 *strrchr(filename_prefix, '.') = '\0';
14077 filename_info = getStringCat2(filename_prefix, ".txt");
14079 if (fileExists(filename_info))
14080 setup_file_hash = loadSetupFileHash(filename_info);
14082 free(filename_prefix);
14083 free(filename_info);
14085 if (setup_file_hash == NULL)
14087 // ---------- try to add file extension ----------
14089 filename_prefix = getStringCopy(filename_music);
14090 filename_info = getStringCat2(filename_prefix, ".txt");
14092 if (fileExists(filename_info))
14093 setup_file_hash = loadSetupFileHash(filename_info);
14095 free(filename_prefix);
14096 free(filename_info);
14099 if (setup_file_hash == NULL)
14102 // ---------- music file info found ----------
14104 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14106 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14108 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14110 *token_to_value_ptr[i].value_ptr =
14111 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14114 tmp_music_file_info.basename = getStringCopy(basename);
14115 tmp_music_file_info.music = music;
14116 tmp_music_file_info.is_sound = is_sound;
14118 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14119 *new_music_file_info = tmp_music_file_info;
14121 return new_music_file_info;
14124 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14126 return get_music_file_info_ext(basename, music, FALSE);
14129 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14131 return get_music_file_info_ext(basename, sound, TRUE);
14134 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14135 char *basename, boolean is_sound)
14137 for (; list != NULL; list = list->next)
14138 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14144 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14146 return music_info_listed_ext(list, basename, FALSE);
14149 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14151 return music_info_listed_ext(list, basename, TRUE);
14154 void LoadMusicInfo(void)
14156 int num_music_noconf = getMusicListSize_NoConf();
14157 int num_music = getMusicListSize();
14158 int num_sounds = getSoundListSize();
14159 struct FileInfo *music, *sound;
14160 struct MusicFileInfo *next, **new;
14164 while (music_file_info != NULL)
14166 next = music_file_info->next;
14168 checked_free(music_file_info->basename);
14170 checked_free(music_file_info->title_header);
14171 checked_free(music_file_info->artist_header);
14172 checked_free(music_file_info->album_header);
14173 checked_free(music_file_info->year_header);
14174 checked_free(music_file_info->played_header);
14176 checked_free(music_file_info->title);
14177 checked_free(music_file_info->artist);
14178 checked_free(music_file_info->album);
14179 checked_free(music_file_info->year);
14180 checked_free(music_file_info->played);
14182 free(music_file_info);
14184 music_file_info = next;
14187 new = &music_file_info;
14189 // get (configured or unconfigured) music file info for all levels
14190 for (i = leveldir_current->first_level;
14191 i <= leveldir_current->last_level; i++)
14195 if (levelset.music[i] != MUS_UNDEFINED)
14197 // get music file info for configured level music
14198 music_nr = levelset.music[i];
14200 else if (num_music_noconf > 0)
14202 // get music file info for unconfigured level music
14203 int level_pos = i - leveldir_current->first_level;
14205 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14212 char *basename = getMusicInfoEntryFilename(music_nr);
14214 if (basename == NULL)
14217 if (!music_info_listed(music_file_info, basename))
14219 *new = get_music_file_info(basename, music_nr);
14222 new = &(*new)->next;
14226 // get music file info for all remaining configured music files
14227 for (i = 0; i < num_music; i++)
14229 music = getMusicListEntry(i);
14231 if (music->filename == NULL)
14234 if (strEqual(music->filename, UNDEFINED_FILENAME))
14237 // a configured file may be not recognized as music
14238 if (!FileIsMusic(music->filename))
14241 if (!music_info_listed(music_file_info, music->filename))
14243 *new = get_music_file_info(music->filename, i);
14246 new = &(*new)->next;
14250 // get sound file info for all configured sound files
14251 for (i = 0; i < num_sounds; i++)
14253 sound = getSoundListEntry(i);
14255 if (sound->filename == NULL)
14258 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14261 // a configured file may be not recognized as sound
14262 if (!FileIsSound(sound->filename))
14265 if (!sound_info_listed(music_file_info, sound->filename))
14267 *new = get_sound_file_info(sound->filename, i);
14269 new = &(*new)->next;
14273 // add pointers to previous list nodes
14275 struct MusicFileInfo *node = music_file_info;
14277 while (node != NULL)
14280 node->next->prev = node;
14286 static void add_helpanim_entry(int element, int action, int direction,
14287 int delay, int *num_list_entries)
14289 struct HelpAnimInfo *new_list_entry;
14290 (*num_list_entries)++;
14293 checked_realloc(helpanim_info,
14294 *num_list_entries * sizeof(struct HelpAnimInfo));
14295 new_list_entry = &helpanim_info[*num_list_entries - 1];
14297 new_list_entry->element = element;
14298 new_list_entry->action = action;
14299 new_list_entry->direction = direction;
14300 new_list_entry->delay = delay;
14303 static void print_unknown_token(char *filename, char *token, int token_nr)
14308 Warn("unknown token(s) found in config file:");
14309 Warn("- config file: '%s'", filename);
14312 Warn("- token: '%s'", token);
14315 static void print_unknown_token_end(int token_nr)
14321 void LoadHelpAnimInfo(void)
14323 char *filename = getHelpAnimFilename();
14324 SetupFileList *setup_file_list = NULL, *list;
14325 SetupFileHash *element_hash, *action_hash, *direction_hash;
14326 int num_list_entries = 0;
14327 int num_unknown_tokens = 0;
14330 if (fileExists(filename))
14331 setup_file_list = loadSetupFileList(filename);
14333 if (setup_file_list == NULL)
14335 // use reliable default values from static configuration
14336 SetupFileList *insert_ptr;
14338 insert_ptr = setup_file_list =
14339 newSetupFileList(helpanim_config[0].token,
14340 helpanim_config[0].value);
14342 for (i = 1; helpanim_config[i].token; i++)
14343 insert_ptr = addListEntry(insert_ptr,
14344 helpanim_config[i].token,
14345 helpanim_config[i].value);
14348 element_hash = newSetupFileHash();
14349 action_hash = newSetupFileHash();
14350 direction_hash = newSetupFileHash();
14352 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14353 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14355 for (i = 0; i < NUM_ACTIONS; i++)
14356 setHashEntry(action_hash, element_action_info[i].suffix,
14357 i_to_a(element_action_info[i].value));
14359 // do not store direction index (bit) here, but direction value!
14360 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14361 setHashEntry(direction_hash, element_direction_info[i].suffix,
14362 i_to_a(1 << element_direction_info[i].value));
14364 for (list = setup_file_list; list != NULL; list = list->next)
14366 char *element_token, *action_token, *direction_token;
14367 char *element_value, *action_value, *direction_value;
14368 int delay = atoi(list->value);
14370 if (strEqual(list->token, "end"))
14372 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14377 /* first try to break element into element/action/direction parts;
14378 if this does not work, also accept combined "element[.act][.dir]"
14379 elements (like "dynamite.active"), which are unique elements */
14381 if (strchr(list->token, '.') == NULL) // token contains no '.'
14383 element_value = getHashEntry(element_hash, list->token);
14384 if (element_value != NULL) // element found
14385 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14386 &num_list_entries);
14389 // no further suffixes found -- this is not an element
14390 print_unknown_token(filename, list->token, num_unknown_tokens++);
14396 // token has format "<prefix>.<something>"
14398 action_token = strchr(list->token, '.'); // suffix may be action ...
14399 direction_token = action_token; // ... or direction
14401 element_token = getStringCopy(list->token);
14402 *strchr(element_token, '.') = '\0';
14404 element_value = getHashEntry(element_hash, element_token);
14406 if (element_value == NULL) // this is no element
14408 element_value = getHashEntry(element_hash, list->token);
14409 if (element_value != NULL) // combined element found
14410 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14411 &num_list_entries);
14413 print_unknown_token(filename, list->token, num_unknown_tokens++);
14415 free(element_token);
14420 action_value = getHashEntry(action_hash, action_token);
14422 if (action_value != NULL) // action found
14424 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14425 &num_list_entries);
14427 free(element_token);
14432 direction_value = getHashEntry(direction_hash, direction_token);
14434 if (direction_value != NULL) // direction found
14436 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14437 &num_list_entries);
14439 free(element_token);
14444 if (strchr(action_token + 1, '.') == NULL)
14446 // no further suffixes found -- this is not an action nor direction
14448 element_value = getHashEntry(element_hash, list->token);
14449 if (element_value != NULL) // combined element found
14450 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14451 &num_list_entries);
14453 print_unknown_token(filename, list->token, num_unknown_tokens++);
14455 free(element_token);
14460 // token has format "<prefix>.<suffix>.<something>"
14462 direction_token = strchr(action_token + 1, '.');
14464 action_token = getStringCopy(action_token);
14465 *strchr(action_token + 1, '.') = '\0';
14467 action_value = getHashEntry(action_hash, action_token);
14469 if (action_value == NULL) // this is no action
14471 element_value = getHashEntry(element_hash, list->token);
14472 if (element_value != NULL) // combined element found
14473 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14474 &num_list_entries);
14476 print_unknown_token(filename, list->token, num_unknown_tokens++);
14478 free(element_token);
14479 free(action_token);
14484 direction_value = getHashEntry(direction_hash, direction_token);
14486 if (direction_value != NULL) // direction found
14488 add_helpanim_entry(atoi(element_value), atoi(action_value),
14489 atoi(direction_value), delay, &num_list_entries);
14491 free(element_token);
14492 free(action_token);
14497 // this is no direction
14499 element_value = getHashEntry(element_hash, list->token);
14500 if (element_value != NULL) // combined element found
14501 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14502 &num_list_entries);
14504 print_unknown_token(filename, list->token, num_unknown_tokens++);
14506 free(element_token);
14507 free(action_token);
14510 print_unknown_token_end(num_unknown_tokens);
14512 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14513 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14515 freeSetupFileList(setup_file_list);
14516 freeSetupFileHash(element_hash);
14517 freeSetupFileHash(action_hash);
14518 freeSetupFileHash(direction_hash);
14521 for (i = 0; i < num_list_entries; i++)
14522 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14523 EL_NAME(helpanim_info[i].element),
14524 helpanim_info[i].element,
14525 helpanim_info[i].action,
14526 helpanim_info[i].direction,
14527 helpanim_info[i].delay);
14531 void LoadHelpTextInfo(void)
14533 char *filename = getHelpTextFilename();
14536 if (helptext_info != NULL)
14538 freeSetupFileHash(helptext_info);
14539 helptext_info = NULL;
14542 if (fileExists(filename))
14543 helptext_info = loadSetupFileHash(filename);
14545 if (helptext_info == NULL)
14547 // use reliable default values from static configuration
14548 helptext_info = newSetupFileHash();
14550 for (i = 0; helptext_config[i].token; i++)
14551 setHashEntry(helptext_info,
14552 helptext_config[i].token,
14553 helptext_config[i].value);
14557 BEGIN_HASH_ITERATION(helptext_info, itr)
14559 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14560 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14562 END_HASH_ITERATION(hash, itr)
14567 // ----------------------------------------------------------------------------
14569 // ----------------------------------------------------------------------------
14571 #define MAX_NUM_CONVERT_LEVELS 1000
14573 void ConvertLevels(void)
14575 static LevelDirTree *convert_leveldir = NULL;
14576 static int convert_level_nr = -1;
14577 static int num_levels_handled = 0;
14578 static int num_levels_converted = 0;
14579 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14582 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14583 global.convert_leveldir);
14585 if (convert_leveldir == NULL)
14586 Fail("no such level identifier: '%s'", global.convert_leveldir);
14588 leveldir_current = convert_leveldir;
14590 if (global.convert_level_nr != -1)
14592 convert_leveldir->first_level = global.convert_level_nr;
14593 convert_leveldir->last_level = global.convert_level_nr;
14596 convert_level_nr = convert_leveldir->first_level;
14598 PrintLine("=", 79);
14599 Print("Converting levels\n");
14600 PrintLine("-", 79);
14601 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14602 Print("Level series name: '%s'\n", convert_leveldir->name);
14603 Print("Level series author: '%s'\n", convert_leveldir->author);
14604 Print("Number of levels: %d\n", convert_leveldir->levels);
14605 PrintLine("=", 79);
14608 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14609 levels_failed[i] = FALSE;
14611 while (convert_level_nr <= convert_leveldir->last_level)
14613 char *level_filename;
14616 level_nr = convert_level_nr++;
14618 Print("Level %03d: ", level_nr);
14620 LoadLevel(level_nr);
14621 if (level.no_level_file || level.no_valid_file)
14623 Print("(no level)\n");
14627 Print("converting level ... ");
14630 // special case: conversion of some EMC levels as requested by ACME
14631 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14634 level_filename = getDefaultLevelFilename(level_nr);
14635 new_level = !fileExists(level_filename);
14639 SaveLevel(level_nr);
14641 num_levels_converted++;
14643 Print("converted.\n");
14647 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14648 levels_failed[level_nr] = TRUE;
14650 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14653 num_levels_handled++;
14657 PrintLine("=", 79);
14658 Print("Number of levels handled: %d\n", num_levels_handled);
14659 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14660 (num_levels_handled ?
14661 num_levels_converted * 100 / num_levels_handled : 0));
14662 PrintLine("-", 79);
14663 Print("Summary (for automatic parsing by scripts):\n");
14664 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14665 convert_leveldir->identifier, num_levels_converted,
14666 num_levels_handled,
14667 (num_levels_handled ?
14668 num_levels_converted * 100 / num_levels_handled : 0));
14670 if (num_levels_handled != num_levels_converted)
14672 Print(", FAILED:");
14673 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14674 if (levels_failed[i])
14679 PrintLine("=", 79);
14681 CloseAllAndExit(0);
14685 // ----------------------------------------------------------------------------
14686 // create and save images for use in level sketches (raw BMP format)
14687 // ----------------------------------------------------------------------------
14689 void CreateLevelSketchImages(void)
14695 InitElementPropertiesGfxElement();
14697 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14698 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14700 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14702 int element = getMappedElement(i);
14703 char basename1[16];
14704 char basename2[16];
14708 sprintf(basename1, "%04d.bmp", i);
14709 sprintf(basename2, "%04ds.bmp", i);
14711 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14712 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14714 DrawSizedElement(0, 0, element, TILESIZE);
14715 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14717 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14718 Fail("cannot save level sketch image file '%s'", filename1);
14720 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14721 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14723 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14724 Fail("cannot save level sketch image file '%s'", filename2);
14729 // create corresponding SQL statements (for normal and small images)
14732 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14733 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14736 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14737 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14739 // optional: create content for forum level sketch demonstration post
14741 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14744 FreeBitmap(bitmap1);
14745 FreeBitmap(bitmap2);
14748 fprintf(stderr, "\n");
14750 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14752 CloseAllAndExit(0);
14756 // ----------------------------------------------------------------------------
14757 // create and save images for element collecting animations (raw BMP format)
14758 // ----------------------------------------------------------------------------
14760 static boolean createCollectImage(int element)
14762 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14765 void CreateCollectElementImages(void)
14769 int anim_frames = num_steps - 1;
14770 int tile_size = TILESIZE;
14771 int anim_width = tile_size * anim_frames;
14772 int anim_height = tile_size;
14773 int num_collect_images = 0;
14774 int pos_collect_images = 0;
14776 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14777 if (createCollectImage(i))
14778 num_collect_images++;
14780 Info("Creating %d element collecting animation images ...",
14781 num_collect_images);
14783 int dst_width = anim_width * 2;
14784 int dst_height = anim_height * num_collect_images / 2;
14785 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14786 char *basename_bmp = "RocksCollect.bmp";
14787 char *basename_png = "RocksCollect.png";
14788 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14789 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14790 int len_filename_bmp = strlen(filename_bmp);
14791 int len_filename_png = strlen(filename_png);
14792 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14793 char cmd_convert[max_command_len];
14795 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14799 // force using RGBA surface for destination bitmap
14800 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14801 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14803 dst_bitmap->surface =
14804 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14806 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14808 if (!createCollectImage(i))
14811 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14812 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14813 int graphic = el2img(i);
14814 char *token_name = element_info[i].token_name;
14815 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14816 Bitmap *src_bitmap;
14819 Info("- creating collecting image for '%s' ...", token_name);
14821 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14823 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14824 tile_size, tile_size, 0, 0);
14826 // force using RGBA surface for temporary bitmap (using transparent black)
14827 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14828 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14830 tmp_bitmap->surface =
14831 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14833 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14835 for (j = 0; j < anim_frames; j++)
14837 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14838 int frame_size = frame_size_final * num_steps;
14839 int offset = (tile_size - frame_size_final) / 2;
14840 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14842 while (frame_size > frame_size_final)
14846 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14848 FreeBitmap(frame_bitmap);
14850 frame_bitmap = half_bitmap;
14853 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14854 frame_size_final, frame_size_final,
14855 dst_x + j * tile_size + offset, dst_y + offset);
14857 FreeBitmap(frame_bitmap);
14860 tmp_bitmap->surface_masked = NULL;
14862 FreeBitmap(tmp_bitmap);
14864 pos_collect_images++;
14867 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14868 Fail("cannot save element collecting image file '%s'", filename_bmp);
14870 FreeBitmap(dst_bitmap);
14872 Info("Converting image file from BMP to PNG ...");
14874 if (system(cmd_convert) != 0)
14875 Fail("converting image file failed");
14877 unlink(filename_bmp);
14881 CloseAllAndExit(0);
14885 // ----------------------------------------------------------------------------
14886 // create and save images for custom and group elements (raw BMP format)
14887 // ----------------------------------------------------------------------------
14889 void CreateCustomElementImages(char *directory)
14891 char *src_basename = "RocksCE-template.ilbm";
14892 char *dst_basename = "RocksCE.bmp";
14893 char *src_filename = getPath2(directory, src_basename);
14894 char *dst_filename = getPath2(directory, dst_basename);
14895 Bitmap *src_bitmap;
14897 int yoffset_ce = 0;
14898 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14901 InitVideoDefaults();
14903 ReCreateBitmap(&backbuffer, video.width, video.height);
14905 src_bitmap = LoadImage(src_filename);
14907 bitmap = CreateBitmap(TILEX * 16 * 2,
14908 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14911 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14918 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14919 TILEX * x, TILEY * y + yoffset_ce);
14921 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14923 TILEX * x + TILEX * 16,
14924 TILEY * y + yoffset_ce);
14926 for (j = 2; j >= 0; j--)
14930 BlitBitmap(src_bitmap, bitmap,
14931 TILEX + c * 7, 0, 6, 10,
14932 TILEX * x + 6 + j * 7,
14933 TILEY * y + 11 + yoffset_ce);
14935 BlitBitmap(src_bitmap, bitmap,
14936 TILEX + c * 8, TILEY, 6, 10,
14937 TILEX * 16 + TILEX * x + 6 + j * 8,
14938 TILEY * y + 10 + yoffset_ce);
14944 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14951 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14952 TILEX * x, TILEY * y + yoffset_ge);
14954 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14956 TILEX * x + TILEX * 16,
14957 TILEY * y + yoffset_ge);
14959 for (j = 1; j >= 0; j--)
14963 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14964 TILEX * x + 6 + j * 10,
14965 TILEY * y + 11 + yoffset_ge);
14967 BlitBitmap(src_bitmap, bitmap,
14968 TILEX + c * 8, TILEY + 12, 6, 10,
14969 TILEX * 16 + TILEX * x + 10 + j * 8,
14970 TILEY * y + 10 + yoffset_ge);
14976 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14977 Fail("cannot save CE graphics file '%s'", dst_filename);
14979 FreeBitmap(bitmap);
14981 CloseAllAndExit(0);