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
954 EL_BD_ROCKET_LAUNCHER, -1,
955 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
956 &li.bd_infinite_rockets, FALSE
961 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
962 &li.bd_num_skeletons_needed_for_pot, 5
966 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
967 &li.bd_skeleton_worth_num_diamonds, 0
971 EL_BD_CREATURE_SWITCH, -1,
972 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
973 &li.bd_creatures_start_backwards, FALSE
976 EL_BD_CREATURE_SWITCH, -1,
977 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
978 &li.bd_creatures_turn_on_hatching, FALSE
981 EL_BD_CREATURE_SWITCH, -1,
982 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
983 &li.bd_creatures_auto_turn_delay, 0
987 EL_BD_GRAVITY_SWITCH, -1,
988 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
989 &li.bd_gravity_direction, GD_MV_DOWN
992 EL_BD_GRAVITY_SWITCH, -1,
993 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
994 &li.bd_gravity_switch_active, FALSE
997 EL_BD_GRAVITY_SWITCH, -1,
998 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
999 &li.bd_gravity_switch_delay, 10
1002 EL_BD_GRAVITY_SWITCH, -1,
1003 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1004 &li.bd_gravity_affects_all, TRUE
1007 // (the following values are related to various game elements)
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1012 &li.score[SC_EMERALD], 10
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1018 &li.score[SC_DIAMOND], 10
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1024 &li.score[SC_BUG], 10
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1030 &li.score[SC_SPACESHIP], 10
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1036 &li.score[SC_PACMAN], 10
1041 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1042 &li.score[SC_NUT], 10
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1048 &li.score[SC_DYNAMITE], 10
1053 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1054 &li.score[SC_KEY], 10
1059 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1060 &li.score[SC_PEARL], 10
1065 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1066 &li.score[SC_CRYSTAL], 10
1069 // (amoeba values used by R'n'D game engine only)
1072 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1073 &li.amoeba_content, EL_DIAMOND
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1078 &li.amoeba_speed, 10
1082 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1083 &li.grow_into_diggable, TRUE
1085 // (amoeba values used by BD game engine only)
1088 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1089 &li.bd_amoeba_wait_for_hatching, FALSE
1093 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1094 &li.bd_amoeba_start_immediately, TRUE
1098 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1099 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1103 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1104 &li.bd_amoeba_threshold_too_big, 200
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1109 &li.bd_amoeba_slow_growth_time, 200
1113 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1114 &li.bd_amoeba_slow_growth_rate, 3
1118 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1119 &li.bd_amoeba_fast_growth_rate, 25
1123 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1124 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1128 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1129 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1134 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1135 &li.bd_amoeba_2_threshold_too_big, 200
1139 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1140 &li.bd_amoeba_2_slow_growth_time, 200
1144 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1145 &li.bd_amoeba_2_slow_growth_rate, 3
1149 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1150 &li.bd_amoeba_2_fast_growth_rate, 25
1154 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1155 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1159 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1160 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1164 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1165 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1169 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1170 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1175 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1176 &li.yamyam_content, EL_ROCK, NULL,
1177 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1182 &li.score[SC_YAMYAM], 10
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1188 &li.score[SC_ROBOT], 10
1192 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1198 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1204 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1205 &li.time_magic_wall, 10
1209 EL_GAME_OF_LIFE, -1,
1210 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1211 &li.game_of_life[0], 2
1214 EL_GAME_OF_LIFE, -1,
1215 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1216 &li.game_of_life[1], 3
1219 EL_GAME_OF_LIFE, -1,
1220 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1221 &li.game_of_life[2], 3
1224 EL_GAME_OF_LIFE, -1,
1225 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1226 &li.game_of_life[3], 3
1229 EL_GAME_OF_LIFE, -1,
1230 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1231 &li.use_life_bugs, FALSE
1236 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1241 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1246 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1251 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1256 EL_TIMEGATE_SWITCH, -1,
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1258 &li.time_timegate, 10
1262 EL_LIGHT_SWITCH_ACTIVE, -1,
1263 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1268 EL_SHIELD_NORMAL, -1,
1269 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1270 &li.shield_normal_time, 10
1273 EL_SHIELD_NORMAL, -1,
1274 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1275 &li.score[SC_SHIELD], 10
1279 EL_SHIELD_DEADLY, -1,
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1281 &li.shield_deadly_time, 10
1284 EL_SHIELD_DEADLY, -1,
1285 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1286 &li.score[SC_SHIELD], 10
1291 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1296 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1297 &li.extra_time_score, 10
1301 EL_TIME_ORB_FULL, -1,
1302 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1303 &li.time_orb_time, 10
1306 EL_TIME_ORB_FULL, -1,
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1308 &li.use_time_orb_bug, FALSE
1313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1314 &li.use_spring_bug, FALSE
1319 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1320 &li.android_move_time, 10
1324 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1325 &li.android_clone_time, 10
1328 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1329 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1330 &li.android_clone_element[0], EL_EMPTY, NULL,
1331 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1335 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1336 &li.android_clone_element[0], EL_EMPTY, NULL,
1337 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1343 &li.lenses_score, 10
1347 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1352 EL_EMC_MAGNIFIER, -1,
1353 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1354 &li.magnify_score, 10
1357 EL_EMC_MAGNIFIER, -1,
1358 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1359 &li.magnify_time, 10
1363 EL_EMC_MAGIC_BALL, -1,
1364 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1368 EL_EMC_MAGIC_BALL, -1,
1369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1370 &li.ball_random, FALSE
1373 EL_EMC_MAGIC_BALL, -1,
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1375 &li.ball_active_initial, FALSE
1378 EL_EMC_MAGIC_BALL, -1,
1379 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1380 &li.ball_content, EL_EMPTY, NULL,
1381 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1385 EL_SOKOBAN_FIELD_EMPTY, -1,
1386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1387 &li.sb_fields_needed, TRUE
1391 EL_SOKOBAN_OBJECT, -1,
1392 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1393 &li.sb_objects_needed, TRUE
1398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1399 &li.mm_laser_red, FALSE
1403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1404 &li.mm_laser_green, FALSE
1408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1409 &li.mm_laser_blue, TRUE
1414 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1415 &li.df_laser_red, TRUE
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1420 &li.df_laser_green, TRUE
1424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1425 &li.df_laser_blue, FALSE
1429 EL_MM_FUSE_ACTIVE, -1,
1430 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1431 &li.mm_time_fuse, 25
1435 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1436 &li.mm_time_bomb, 75
1440 EL_MM_GRAY_BALL, -1,
1441 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1442 &li.mm_time_ball, 75
1445 EL_MM_GRAY_BALL, -1,
1446 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1447 &li.mm_ball_choice_mode, ANIM_RANDOM
1450 EL_MM_GRAY_BALL, -1,
1451 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1452 &li.mm_ball_content, EL_EMPTY, NULL,
1453 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1456 EL_MM_GRAY_BALL, -1,
1457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1458 &li.rotate_mm_ball_content, TRUE
1461 EL_MM_GRAY_BALL, -1,
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1463 &li.explode_mm_ball, FALSE
1467 EL_MM_STEEL_BLOCK, -1,
1468 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1469 &li.mm_time_block, 75
1472 EL_MM_LIGHTBALL, -1,
1473 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1474 &li.score[SC_ELEM_BONUS], 10
1484 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1488 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1489 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1493 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1494 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1500 &xx_envelope.autowrap, FALSE
1504 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1505 &xx_envelope.centered, FALSE
1510 TYPE_STRING, CONF_VALUE_BYTES(1),
1511 &xx_envelope.text, -1, NULL,
1512 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1513 &xx_default_string_empty[0]
1523 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1527 TYPE_STRING, CONF_VALUE_BYTES(1),
1528 &xx_ei.description[0], -1,
1529 &yy_ei.description[0],
1530 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1531 &xx_default_description[0]
1536 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1537 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1538 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1540 #if ENABLE_RESERVED_CODE
1541 // (reserved for later use)
1544 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1545 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1546 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1552 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1553 &xx_ei.use_gfx_element, FALSE,
1554 &yy_ei.use_gfx_element
1558 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1559 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1560 &yy_ei.gfx_element_initial
1565 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1566 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1567 &yy_ei.access_direction
1572 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1573 &xx_ei.collect_score_initial, 10,
1574 &yy_ei.collect_score_initial
1578 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1579 &xx_ei.collect_count_initial, 1,
1580 &yy_ei.collect_count_initial
1585 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1586 &xx_ei.ce_value_fixed_initial, 0,
1587 &yy_ei.ce_value_fixed_initial
1591 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1592 &xx_ei.ce_value_random_initial, 0,
1593 &yy_ei.ce_value_random_initial
1597 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1598 &xx_ei.use_last_ce_value, FALSE,
1599 &yy_ei.use_last_ce_value
1604 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1605 &xx_ei.push_delay_fixed, 8,
1606 &yy_ei.push_delay_fixed
1610 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1611 &xx_ei.push_delay_random, 8,
1612 &yy_ei.push_delay_random
1616 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1617 &xx_ei.drop_delay_fixed, 0,
1618 &yy_ei.drop_delay_fixed
1622 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1623 &xx_ei.drop_delay_random, 0,
1624 &yy_ei.drop_delay_random
1628 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1629 &xx_ei.move_delay_fixed, 0,
1630 &yy_ei.move_delay_fixed
1634 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1635 &xx_ei.move_delay_random, 0,
1636 &yy_ei.move_delay_random
1640 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1641 &xx_ei.step_delay_fixed, 0,
1642 &yy_ei.step_delay_fixed
1646 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1647 &xx_ei.step_delay_random, 0,
1648 &yy_ei.step_delay_random
1653 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1654 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1659 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1660 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1661 &yy_ei.move_direction_initial
1665 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1666 &xx_ei.move_stepsize, TILEX / 8,
1667 &yy_ei.move_stepsize
1672 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1673 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1674 &yy_ei.move_enter_element
1678 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1679 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1680 &yy_ei.move_leave_element
1684 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1685 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1686 &yy_ei.move_leave_type
1691 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1692 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1693 &yy_ei.slippery_type
1698 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1699 &xx_ei.explosion_type, EXPLODES_3X3,
1700 &yy_ei.explosion_type
1704 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1705 &xx_ei.explosion_delay, 16,
1706 &yy_ei.explosion_delay
1710 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1711 &xx_ei.ignition_delay, 8,
1712 &yy_ei.ignition_delay
1717 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1718 &xx_ei.content, EL_EMPTY_SPACE,
1720 &xx_num_contents, 1, 1
1723 // ---------- "num_change_pages" must be the last entry ---------------------
1726 -1, SAVE_CONF_ALWAYS,
1727 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1728 &xx_ei.num_change_pages, 1,
1729 &yy_ei.num_change_pages
1740 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1742 // ---------- "current_change_page" must be the first entry -----------------
1745 -1, SAVE_CONF_ALWAYS,
1746 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1747 &xx_current_change_page, -1
1750 // ---------- (the remaining entries can be in any order) -------------------
1754 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1755 &xx_change.can_change, FALSE
1760 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1761 &xx_event_bits[0], 0
1765 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1766 &xx_event_bits[1], 0
1771 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1772 &xx_change.trigger_player, CH_PLAYER_ANY
1776 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1777 &xx_change.trigger_side, CH_SIDE_ANY
1781 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1782 &xx_change.trigger_page, CH_PAGE_ANY
1787 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1788 &xx_change.target_element, EL_EMPTY_SPACE
1793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1794 &xx_change.delay_fixed, 0
1798 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1799 &xx_change.delay_random, 0
1803 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1804 &xx_change.delay_frames, FRAMES_PER_SECOND
1809 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1810 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1815 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1816 &xx_change.explode, FALSE
1820 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1821 &xx_change.use_target_content, FALSE
1825 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1826 &xx_change.only_if_complete, FALSE
1830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1831 &xx_change.use_random_replace, FALSE
1835 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1836 &xx_change.random_percentage, 100
1840 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1841 &xx_change.replace_when, CP_WHEN_EMPTY
1846 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1847 &xx_change.has_action, FALSE
1851 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1852 &xx_change.action_type, CA_NO_ACTION
1856 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1857 &xx_change.action_mode, CA_MODE_UNDEFINED
1861 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1862 &xx_change.action_arg, CA_ARG_UNDEFINED
1867 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1868 &xx_change.action_element, EL_EMPTY_SPACE
1873 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1874 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1875 &xx_num_contents, 1, 1
1885 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1889 TYPE_STRING, CONF_VALUE_BYTES(1),
1890 &xx_ei.description[0], -1, NULL,
1891 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1892 &xx_default_description[0]
1897 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1898 &xx_ei.use_gfx_element, FALSE
1902 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1903 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1908 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1909 &xx_group.choice_mode, ANIM_RANDOM
1914 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1915 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1916 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1926 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1931 &xx_ei.use_gfx_element, FALSE
1935 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1936 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1946 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1950 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1951 &li.block_snap_field, TRUE
1955 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1956 &li.continuous_snapping, TRUE
1960 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1961 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1965 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1966 &li.use_start_element[0], FALSE
1970 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1971 &li.start_element[0], EL_PLAYER_1
1975 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1976 &li.use_artwork_element[0], FALSE
1980 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1981 &li.artwork_element[0], EL_PLAYER_1
1985 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1986 &li.use_explosion_element[0], FALSE
1990 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1991 &li.explosion_element[0], EL_PLAYER_1
2006 filetype_id_list[] =
2008 { LEVEL_FILE_TYPE_RND, "RND" },
2009 { LEVEL_FILE_TYPE_BD, "BD" },
2010 { LEVEL_FILE_TYPE_EM, "EM" },
2011 { LEVEL_FILE_TYPE_SP, "SP" },
2012 { LEVEL_FILE_TYPE_DX, "DX" },
2013 { LEVEL_FILE_TYPE_SB, "SB" },
2014 { LEVEL_FILE_TYPE_DC, "DC" },
2015 { LEVEL_FILE_TYPE_MM, "MM" },
2016 { LEVEL_FILE_TYPE_MM, "DF" },
2021 // ============================================================================
2022 // level file functions
2023 // ============================================================================
2025 static boolean check_special_flags(char *flag)
2027 if (strEqual(options.special_flags, flag) ||
2028 strEqual(leveldir_current->special_flags, flag))
2034 static struct DateInfo getCurrentDate(void)
2036 time_t epoch_seconds = time(NULL);
2037 struct tm *now = localtime(&epoch_seconds);
2038 struct DateInfo date;
2040 date.year = now->tm_year + 1900;
2041 date.month = now->tm_mon + 1;
2042 date.day = now->tm_mday;
2044 date.src = DATE_SRC_CLOCK;
2049 static void resetEventFlags(struct ElementChangeInfo *change)
2053 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2054 change->has_event[i] = FALSE;
2057 static void resetEventBits(void)
2061 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2062 xx_event_bits[i] = 0;
2065 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2069 /* important: only change event flag if corresponding event bit is set
2070 (this is because all xx_event_bits[] values are loaded separately,
2071 and all xx_event_bits[] values are set back to zero before loading
2072 another value xx_event_bits[x] (each value representing 32 flags)) */
2074 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2075 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2076 change->has_event[i] = TRUE;
2079 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2083 /* in contrast to the above function setEventFlagsFromEventBits(), it
2084 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2085 depending on the corresponding change->has_event[i] values here, as
2086 all xx_event_bits[] values are reset in resetEventBits() before */
2088 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2089 if (change->has_event[i])
2090 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2093 static char *getDefaultElementDescription(struct ElementInfo *ei)
2095 static char description[MAX_ELEMENT_NAME_LEN + 1];
2096 char *default_description = (ei->custom_description != NULL ?
2097 ei->custom_description :
2098 ei->editor_description);
2101 // always start with reliable default values
2102 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2103 description[i] = '\0';
2105 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2106 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2108 return &description[0];
2111 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2113 char *default_description = getDefaultElementDescription(ei);
2116 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2117 ei->description[i] = default_description[i];
2120 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2124 for (i = 0; conf[i].data_type != -1; i++)
2126 int default_value = conf[i].default_value;
2127 int data_type = conf[i].data_type;
2128 int conf_type = conf[i].conf_type;
2129 int byte_mask = conf_type & CONF_MASK_BYTES;
2131 if (byte_mask == CONF_MASK_MULTI_BYTES)
2133 int default_num_entities = conf[i].default_num_entities;
2134 int max_num_entities = conf[i].max_num_entities;
2136 *(int *)(conf[i].num_entities) = default_num_entities;
2138 if (data_type == TYPE_STRING)
2140 char *default_string = conf[i].default_string;
2141 char *string = (char *)(conf[i].value);
2143 strncpy(string, default_string, max_num_entities);
2145 else if (data_type == TYPE_ELEMENT_LIST)
2147 int *element_array = (int *)(conf[i].value);
2150 for (j = 0; j < max_num_entities; j++)
2151 element_array[j] = default_value;
2153 else if (data_type == TYPE_CONTENT_LIST)
2155 struct Content *content = (struct Content *)(conf[i].value);
2158 for (c = 0; c < max_num_entities; c++)
2159 for (y = 0; y < 3; y++)
2160 for (x = 0; x < 3; x++)
2161 content[c].e[x][y] = default_value;
2164 else // constant size configuration data (1, 2 or 4 bytes)
2166 if (data_type == TYPE_BOOLEAN)
2167 *(boolean *)(conf[i].value) = default_value;
2169 *(int *) (conf[i].value) = default_value;
2174 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2178 for (i = 0; conf[i].data_type != -1; i++)
2180 int data_type = conf[i].data_type;
2181 int conf_type = conf[i].conf_type;
2182 int byte_mask = conf_type & CONF_MASK_BYTES;
2184 if (byte_mask == CONF_MASK_MULTI_BYTES)
2186 int max_num_entities = conf[i].max_num_entities;
2188 if (data_type == TYPE_STRING)
2190 char *string = (char *)(conf[i].value);
2191 char *string_copy = (char *)(conf[i].value_copy);
2193 strncpy(string_copy, string, max_num_entities);
2195 else if (data_type == TYPE_ELEMENT_LIST)
2197 int *element_array = (int *)(conf[i].value);
2198 int *element_array_copy = (int *)(conf[i].value_copy);
2201 for (j = 0; j < max_num_entities; j++)
2202 element_array_copy[j] = element_array[j];
2204 else if (data_type == TYPE_CONTENT_LIST)
2206 struct Content *content = (struct Content *)(conf[i].value);
2207 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2210 for (c = 0; c < max_num_entities; c++)
2211 for (y = 0; y < 3; y++)
2212 for (x = 0; x < 3; x++)
2213 content_copy[c].e[x][y] = content[c].e[x][y];
2216 else // constant size configuration data (1, 2 or 4 bytes)
2218 if (data_type == TYPE_BOOLEAN)
2219 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2221 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2226 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2230 xx_ei = *ei_from; // copy element data into temporary buffer
2231 yy_ei = *ei_to; // copy element data into temporary buffer
2233 copyConfigFromConfigList(chunk_config_CUSX_base);
2238 // ---------- reinitialize and copy change pages ----------
2240 ei_to->num_change_pages = ei_from->num_change_pages;
2241 ei_to->current_change_page = ei_from->current_change_page;
2243 setElementChangePages(ei_to, ei_to->num_change_pages);
2245 for (i = 0; i < ei_to->num_change_pages; i++)
2246 ei_to->change_page[i] = ei_from->change_page[i];
2248 // ---------- copy group element info ----------
2249 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2250 *ei_to->group = *ei_from->group;
2252 // mark this custom element as modified
2253 ei_to->modified_settings = TRUE;
2256 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2258 int change_page_size = sizeof(struct ElementChangeInfo);
2260 ei->num_change_pages = MAX(1, change_pages);
2263 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2265 if (ei->current_change_page >= ei->num_change_pages)
2266 ei->current_change_page = ei->num_change_pages - 1;
2268 ei->change = &ei->change_page[ei->current_change_page];
2271 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2273 xx_change = *change; // copy change data into temporary buffer
2275 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2277 *change = xx_change;
2279 resetEventFlags(change);
2281 change->direct_action = 0;
2282 change->other_action = 0;
2284 change->pre_change_function = NULL;
2285 change->change_function = NULL;
2286 change->post_change_function = NULL;
2289 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2293 li = *level; // copy level data into temporary buffer
2294 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2295 *level = li; // copy temporary buffer back to level data
2297 setLevelInfoToDefaults_BD();
2298 setLevelInfoToDefaults_EM();
2299 setLevelInfoToDefaults_SP();
2300 setLevelInfoToDefaults_MM();
2302 level->native_bd_level = &native_bd_level;
2303 level->native_em_level = &native_em_level;
2304 level->native_sp_level = &native_sp_level;
2305 level->native_mm_level = &native_mm_level;
2307 level->file_version = FILE_VERSION_ACTUAL;
2308 level->game_version = GAME_VERSION_ACTUAL;
2310 level->creation_date = getCurrentDate();
2312 level->encoding_16bit_field = TRUE;
2313 level->encoding_16bit_yamyam = TRUE;
2314 level->encoding_16bit_amoeba = TRUE;
2316 // clear level name and level author string buffers
2317 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2318 level->name[i] = '\0';
2319 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2320 level->author[i] = '\0';
2322 // set level name and level author to default values
2323 strcpy(level->name, NAMELESS_LEVEL_NAME);
2324 strcpy(level->author, ANONYMOUS_NAME);
2326 // set level playfield to playable default level with player and exit
2327 for (x = 0; x < MAX_LEV_FIELDX; x++)
2328 for (y = 0; y < MAX_LEV_FIELDY; y++)
2329 level->field[x][y] = EL_SAND;
2331 level->field[0][0] = EL_PLAYER_1;
2332 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2334 BorderElement = EL_STEELWALL;
2336 // detect custom elements when loading them
2337 level->file_has_custom_elements = FALSE;
2339 // set all bug compatibility flags to "false" => do not emulate this bug
2340 level->use_action_after_change_bug = FALSE;
2342 if (leveldir_current)
2344 // try to determine better author name than 'anonymous'
2345 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2347 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2348 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2352 switch (LEVELCLASS(leveldir_current))
2354 case LEVELCLASS_TUTORIAL:
2355 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2358 case LEVELCLASS_CONTRIB:
2359 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2360 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2363 case LEVELCLASS_PRIVATE:
2364 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2365 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2369 // keep default value
2376 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2378 static boolean clipboard_elements_initialized = FALSE;
2381 InitElementPropertiesStatic();
2383 li = *level; // copy level data into temporary buffer
2384 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2385 *level = li; // copy temporary buffer back to level data
2387 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2390 struct ElementInfo *ei = &element_info[element];
2392 if (element == EL_MM_GRAY_BALL)
2394 struct LevelInfo_MM *level_mm = level->native_mm_level;
2397 for (j = 0; j < level->num_mm_ball_contents; j++)
2398 level->mm_ball_content[j] =
2399 map_element_MM_to_RND(level_mm->ball_content[j]);
2402 // never initialize clipboard elements after the very first time
2403 // (to be able to use clipboard elements between several levels)
2404 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2407 if (IS_ENVELOPE(element))
2409 int envelope_nr = element - EL_ENVELOPE_1;
2411 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2413 level->envelope[envelope_nr] = xx_envelope;
2416 if (IS_CUSTOM_ELEMENT(element) ||
2417 IS_GROUP_ELEMENT(element) ||
2418 IS_INTERNAL_ELEMENT(element))
2420 xx_ei = *ei; // copy element data into temporary buffer
2422 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2427 setElementChangePages(ei, 1);
2428 setElementChangeInfoToDefaults(ei->change);
2430 if (IS_CUSTOM_ELEMENT(element) ||
2431 IS_GROUP_ELEMENT(element))
2433 setElementDescriptionToDefault(ei);
2435 ei->modified_settings = FALSE;
2438 if (IS_CUSTOM_ELEMENT(element) ||
2439 IS_INTERNAL_ELEMENT(element))
2441 // internal values used in level editor
2443 ei->access_type = 0;
2444 ei->access_layer = 0;
2445 ei->access_protected = 0;
2446 ei->walk_to_action = 0;
2447 ei->smash_targets = 0;
2450 ei->can_explode_by_fire = FALSE;
2451 ei->can_explode_smashed = FALSE;
2452 ei->can_explode_impact = FALSE;
2454 ei->current_change_page = 0;
2457 if (IS_GROUP_ELEMENT(element) ||
2458 IS_INTERNAL_ELEMENT(element))
2460 struct ElementGroupInfo *group;
2462 // initialize memory for list of elements in group
2463 if (ei->group == NULL)
2464 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2468 xx_group = *group; // copy group data into temporary buffer
2470 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2475 if (IS_EMPTY_ELEMENT(element) ||
2476 IS_INTERNAL_ELEMENT(element))
2478 xx_ei = *ei; // copy element data into temporary buffer
2480 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2486 clipboard_elements_initialized = TRUE;
2489 static void setLevelInfoToDefaults(struct LevelInfo *level,
2490 boolean level_info_only,
2491 boolean reset_file_status)
2493 setLevelInfoToDefaults_Level(level);
2495 if (!level_info_only)
2496 setLevelInfoToDefaults_Elements(level);
2498 if (reset_file_status)
2500 level->no_valid_file = FALSE;
2501 level->no_level_file = FALSE;
2504 level->changed = FALSE;
2507 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2509 level_file_info->nr = 0;
2510 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2511 level_file_info->packed = FALSE;
2513 setString(&level_file_info->basename, NULL);
2514 setString(&level_file_info->filename, NULL);
2517 int getMappedElement_SB(int, boolean);
2519 static void ActivateLevelTemplate(void)
2523 if (check_special_flags("load_xsb_to_ces"))
2525 // fill smaller playfields with padding "beyond border wall" elements
2526 if (level.fieldx < level_template.fieldx ||
2527 level.fieldy < level_template.fieldy)
2529 short field[level.fieldx][level.fieldy];
2530 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2531 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2532 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2533 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2535 // copy old playfield (which is smaller than the visible area)
2536 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2537 field[x][y] = level.field[x][y];
2539 // fill new, larger playfield with "beyond border wall" elements
2540 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2541 level.field[x][y] = getMappedElement_SB('_', TRUE);
2543 // copy the old playfield to the middle of the new playfield
2544 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2545 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2547 level.fieldx = new_fieldx;
2548 level.fieldy = new_fieldy;
2552 // Currently there is no special action needed to activate the template
2553 // data, because 'element_info' property settings overwrite the original
2554 // level data, while all other variables do not change.
2556 // Exception: 'from_level_template' elements in the original level playfield
2557 // are overwritten with the corresponding elements at the same position in
2558 // playfield from the level template.
2560 for (x = 0; x < level.fieldx; x++)
2561 for (y = 0; y < level.fieldy; y++)
2562 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2563 level.field[x][y] = level_template.field[x][y];
2565 if (check_special_flags("load_xsb_to_ces"))
2567 struct LevelInfo level_backup = level;
2569 // overwrite all individual level settings from template level settings
2570 level = level_template;
2572 // restore level file info
2573 level.file_info = level_backup.file_info;
2575 // restore playfield size
2576 level.fieldx = level_backup.fieldx;
2577 level.fieldy = level_backup.fieldy;
2579 // restore playfield content
2580 for (x = 0; x < level.fieldx; x++)
2581 for (y = 0; y < level.fieldy; y++)
2582 level.field[x][y] = level_backup.field[x][y];
2584 // restore name and author from individual level
2585 strcpy(level.name, level_backup.name);
2586 strcpy(level.author, level_backup.author);
2588 // restore flag "use_custom_template"
2589 level.use_custom_template = level_backup.use_custom_template;
2593 static boolean checkForPackageFromBasename_BD(char *basename)
2595 // check for native BD level file extensions
2596 if (!strSuffixLower(basename, ".bd") &&
2597 !strSuffixLower(basename, ".bdr") &&
2598 !strSuffixLower(basename, ".brc") &&
2599 !strSuffixLower(basename, ".gds"))
2602 // check for standard single-level BD files (like "001.bd")
2603 if (strSuffixLower(basename, ".bd") &&
2604 strlen(basename) == 6 &&
2605 basename[0] >= '0' && basename[0] <= '9' &&
2606 basename[1] >= '0' && basename[1] <= '9' &&
2607 basename[2] >= '0' && basename[2] <= '9')
2610 // this is a level package in native BD file format
2614 static char *getLevelFilenameFromBasename(char *basename)
2616 static char *filename = NULL;
2618 checked_free(filename);
2620 filename = getPath2(getCurrentLevelDir(), basename);
2625 static int getFileTypeFromBasename(char *basename)
2627 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2629 static char *filename = NULL;
2630 struct stat file_status;
2632 // ---------- try to determine file type from filename ----------
2634 // check for typical filename of a Supaplex level package file
2635 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2636 return LEVEL_FILE_TYPE_SP;
2638 // check for typical filename of a Diamond Caves II level package file
2639 if (strSuffixLower(basename, ".dc") ||
2640 strSuffixLower(basename, ".dc2"))
2641 return LEVEL_FILE_TYPE_DC;
2643 // check for typical filename of a Sokoban level package file
2644 if (strSuffixLower(basename, ".xsb") &&
2645 strchr(basename, '%') == NULL)
2646 return LEVEL_FILE_TYPE_SB;
2648 // check for typical filename of a Boulder Dash (GDash) level package file
2649 if (checkForPackageFromBasename_BD(basename))
2650 return LEVEL_FILE_TYPE_BD;
2652 // ---------- try to determine file type from filesize ----------
2654 checked_free(filename);
2655 filename = getPath2(getCurrentLevelDir(), basename);
2657 if (stat(filename, &file_status) == 0)
2659 // check for typical filesize of a Supaplex level package file
2660 if (file_status.st_size == 170496)
2661 return LEVEL_FILE_TYPE_SP;
2664 return LEVEL_FILE_TYPE_UNKNOWN;
2667 static int getFileTypeFromMagicBytes(char *filename, int type)
2671 if ((file = openFile(filename, MODE_READ)))
2673 char chunk_name[CHUNK_ID_LEN + 1];
2675 getFileChunkBE(file, chunk_name, NULL);
2677 if (strEqual(chunk_name, "MMII") ||
2678 strEqual(chunk_name, "MIRR"))
2679 type = LEVEL_FILE_TYPE_MM;
2687 static boolean checkForPackageFromBasename(char *basename)
2689 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2690 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2692 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2695 static char *getSingleLevelBasenameExt(int nr, char *extension)
2697 static char basename[MAX_FILENAME_LEN];
2700 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2702 sprintf(basename, "%03d.%s", nr, extension);
2707 static char *getSingleLevelBasename(int nr)
2709 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2712 static char *getPackedLevelBasename(int type)
2714 static char basename[MAX_FILENAME_LEN];
2715 char *directory = getCurrentLevelDir();
2717 DirectoryEntry *dir_entry;
2719 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2721 if ((dir = openDirectory(directory)) == NULL)
2723 Warn("cannot read current level directory '%s'", directory);
2728 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2730 char *entry_basename = dir_entry->basename;
2731 int entry_type = getFileTypeFromBasename(entry_basename);
2733 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2735 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2738 strcpy(basename, entry_basename);
2745 closeDirectory(dir);
2750 static char *getSingleLevelFilename(int nr)
2752 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2755 #if ENABLE_UNUSED_CODE
2756 static char *getPackedLevelFilename(int type)
2758 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2762 char *getDefaultLevelFilename(int nr)
2764 return getSingleLevelFilename(nr);
2767 #if ENABLE_UNUSED_CODE
2768 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2772 lfi->packed = FALSE;
2774 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2775 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2779 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2780 int type, char *format, ...)
2782 static char basename[MAX_FILENAME_LEN];
2785 va_start(ap, format);
2786 vsprintf(basename, format, ap);
2790 lfi->packed = FALSE;
2792 setString(&lfi->basename, basename);
2793 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2796 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2802 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2803 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2806 static int getFiletypeFromID(char *filetype_id)
2808 char *filetype_id_lower;
2809 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2812 if (filetype_id == NULL)
2813 return LEVEL_FILE_TYPE_UNKNOWN;
2815 filetype_id_lower = getStringToLower(filetype_id);
2817 for (i = 0; filetype_id_list[i].id != NULL; i++)
2819 char *id_lower = getStringToLower(filetype_id_list[i].id);
2821 if (strEqual(filetype_id_lower, id_lower))
2822 filetype = filetype_id_list[i].filetype;
2826 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2830 free(filetype_id_lower);
2835 char *getLocalLevelTemplateFilename(void)
2837 return getDefaultLevelFilename(-1);
2840 char *getGlobalLevelTemplateFilename(void)
2842 // global variable "leveldir_current" must be modified in the loop below
2843 LevelDirTree *leveldir_current_last = leveldir_current;
2844 char *filename = NULL;
2846 // check for template level in path from current to topmost tree node
2848 while (leveldir_current != NULL)
2850 filename = getDefaultLevelFilename(-1);
2852 if (fileExists(filename))
2855 leveldir_current = leveldir_current->node_parent;
2858 // restore global variable "leveldir_current" modified in above loop
2859 leveldir_current = leveldir_current_last;
2864 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2868 // special case: level number is negative => check for level template file
2871 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2872 getSingleLevelBasename(-1));
2874 // replace local level template filename with global template filename
2875 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2877 // no fallback if template file not existing
2881 // special case: check for file name/pattern specified in "levelinfo.conf"
2882 if (leveldir_current->level_filename != NULL)
2884 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2886 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2887 leveldir_current->level_filename, nr);
2889 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2891 if (fileExists(lfi->filename))
2894 else if (leveldir_current->level_filetype != NULL)
2896 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2898 // check for specified native level file with standard file name
2899 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2900 "%03d.%s", nr, LEVELFILE_EXTENSION);
2901 if (fileExists(lfi->filename))
2905 // check for native Rocks'n'Diamonds level file
2906 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2907 "%03d.%s", nr, LEVELFILE_EXTENSION);
2908 if (fileExists(lfi->filename))
2911 // check for native Boulder Dash level file
2912 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2913 if (fileExists(lfi->filename))
2916 // check for Emerald Mine level file (V1)
2917 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2918 'a' + (nr / 10) % 26, '0' + nr % 10);
2919 if (fileExists(lfi->filename))
2921 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2922 'A' + (nr / 10) % 26, '0' + nr % 10);
2923 if (fileExists(lfi->filename))
2926 // check for Emerald Mine level file (V2 to V5)
2927 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2928 if (fileExists(lfi->filename))
2931 // check for Emerald Mine level file (V6 / single mode)
2932 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2933 if (fileExists(lfi->filename))
2935 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2936 if (fileExists(lfi->filename))
2939 // check for Emerald Mine level file (V6 / teamwork mode)
2940 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2941 if (fileExists(lfi->filename))
2943 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2944 if (fileExists(lfi->filename))
2947 // check for various packed level file formats
2948 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2949 if (fileExists(lfi->filename))
2952 // no known level file found -- use default values (and fail later)
2953 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2954 "%03d.%s", nr, LEVELFILE_EXTENSION);
2957 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2959 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2960 lfi->type = getFileTypeFromBasename(lfi->basename);
2962 if (lfi->type == LEVEL_FILE_TYPE_RND)
2963 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2966 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2968 // always start with reliable default values
2969 setFileInfoToDefaults(level_file_info);
2971 level_file_info->nr = nr; // set requested level number
2973 determineLevelFileInfo_Filename(level_file_info);
2974 determineLevelFileInfo_Filetype(level_file_info);
2977 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2978 struct LevelFileInfo *lfi_to)
2980 lfi_to->nr = lfi_from->nr;
2981 lfi_to->type = lfi_from->type;
2982 lfi_to->packed = lfi_from->packed;
2984 setString(&lfi_to->basename, lfi_from->basename);
2985 setString(&lfi_to->filename, lfi_from->filename);
2988 // ----------------------------------------------------------------------------
2989 // functions for loading R'n'D level
2990 // ----------------------------------------------------------------------------
2992 int getMappedElement(int element)
2994 // remap some (historic, now obsolete) elements
2998 case EL_PLAYER_OBSOLETE:
2999 element = EL_PLAYER_1;
3002 case EL_KEY_OBSOLETE:
3006 case EL_EM_KEY_1_FILE_OBSOLETE:
3007 element = EL_EM_KEY_1;
3010 case EL_EM_KEY_2_FILE_OBSOLETE:
3011 element = EL_EM_KEY_2;
3014 case EL_EM_KEY_3_FILE_OBSOLETE:
3015 element = EL_EM_KEY_3;
3018 case EL_EM_KEY_4_FILE_OBSOLETE:
3019 element = EL_EM_KEY_4;
3022 case EL_ENVELOPE_OBSOLETE:
3023 element = EL_ENVELOPE_1;
3031 if (element >= NUM_FILE_ELEMENTS)
3033 Warn("invalid level element %d", element);
3035 element = EL_UNKNOWN;
3043 static int getMappedElementByVersion(int element, int game_version)
3045 // remap some elements due to certain game version
3047 if (game_version <= VERSION_IDENT(2,2,0,0))
3049 // map game font elements
3050 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3051 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3052 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3053 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3056 if (game_version < VERSION_IDENT(3,0,0,0))
3058 // map Supaplex gravity tube elements
3059 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3060 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3061 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3062 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3069 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3071 level->file_version = getFileVersion(file);
3072 level->game_version = getFileVersion(file);
3077 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3079 level->creation_date.year = getFile16BitBE(file);
3080 level->creation_date.month = getFile8Bit(file);
3081 level->creation_date.day = getFile8Bit(file);
3083 level->creation_date.src = DATE_SRC_LEVELFILE;
3088 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3090 int initial_player_stepsize;
3091 int initial_player_gravity;
3094 level->fieldx = getFile8Bit(file);
3095 level->fieldy = getFile8Bit(file);
3097 level->time = getFile16BitBE(file);
3098 level->gems_needed = getFile16BitBE(file);
3100 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3101 level->name[i] = getFile8Bit(file);
3102 level->name[MAX_LEVEL_NAME_LEN] = 0;
3104 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3105 level->score[i] = getFile8Bit(file);
3107 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3108 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3109 for (y = 0; y < 3; y++)
3110 for (x = 0; x < 3; x++)
3111 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3113 level->amoeba_speed = getFile8Bit(file);
3114 level->time_magic_wall = getFile8Bit(file);
3115 level->time_wheel = getFile8Bit(file);
3116 level->amoeba_content = getMappedElement(getFile8Bit(file));
3118 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3121 for (i = 0; i < MAX_PLAYERS; i++)
3122 level->initial_player_stepsize[i] = initial_player_stepsize;
3124 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3126 for (i = 0; i < MAX_PLAYERS; i++)
3127 level->initial_player_gravity[i] = initial_player_gravity;
3129 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3130 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3132 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3134 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3135 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3136 level->can_move_into_acid_bits = getFile32BitBE(file);
3137 level->dont_collide_with_bits = getFile8Bit(file);
3139 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3142 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3143 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3144 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3146 level->game_engine_type = getFile8Bit(file);
3148 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3153 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3157 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3158 level->name[i] = getFile8Bit(file);
3159 level->name[MAX_LEVEL_NAME_LEN] = 0;
3164 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3168 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3169 level->author[i] = getFile8Bit(file);
3170 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3175 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3178 int chunk_size_expected = level->fieldx * level->fieldy;
3180 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3181 stored with 16-bit encoding (and should be twice as big then).
3182 Even worse, playfield data was stored 16-bit when only yamyam content
3183 contained 16-bit elements and vice versa. */
3185 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3186 chunk_size_expected *= 2;
3188 if (chunk_size_expected != chunk_size)
3190 ReadUnusedBytesFromFile(file, chunk_size);
3191 return chunk_size_expected;
3194 for (y = 0; y < level->fieldy; y++)
3195 for (x = 0; x < level->fieldx; x++)
3196 level->field[x][y] =
3197 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3202 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3205 int header_size = 4;
3206 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3207 int chunk_size_expected = header_size + content_size;
3209 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3210 stored with 16-bit encoding (and should be twice as big then).
3211 Even worse, playfield data was stored 16-bit when only yamyam content
3212 contained 16-bit elements and vice versa. */
3214 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3215 chunk_size_expected += content_size;
3217 if (chunk_size_expected != chunk_size)
3219 ReadUnusedBytesFromFile(file, chunk_size);
3220 return chunk_size_expected;
3224 level->num_yamyam_contents = getFile8Bit(file);
3228 // correct invalid number of content fields -- should never happen
3229 if (level->num_yamyam_contents < 1 ||
3230 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3231 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3233 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3234 for (y = 0; y < 3; y++)
3235 for (x = 0; x < 3; x++)
3236 level->yamyam_content[i].e[x][y] =
3237 getMappedElement(level->encoding_16bit_field ?
3238 getFile16BitBE(file) : getFile8Bit(file));
3242 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3247 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3249 element = getMappedElement(getFile16BitBE(file));
3250 num_contents = getFile8Bit(file);
3252 getFile8Bit(file); // content x size (unused)
3253 getFile8Bit(file); // content y size (unused)
3255 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3257 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3258 for (y = 0; y < 3; y++)
3259 for (x = 0; x < 3; x++)
3260 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3262 // correct invalid number of content fields -- should never happen
3263 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3264 num_contents = STD_ELEMENT_CONTENTS;
3266 if (element == EL_YAMYAM)
3268 level->num_yamyam_contents = num_contents;
3270 for (i = 0; i < num_contents; i++)
3271 for (y = 0; y < 3; y++)
3272 for (x = 0; x < 3; x++)
3273 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3275 else if (element == EL_BD_AMOEBA)
3277 level->amoeba_content = content_array[0][0][0];
3281 Warn("cannot load content for element '%d'", element);
3287 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3293 int chunk_size_expected;
3295 element = getMappedElement(getFile16BitBE(file));
3296 if (!IS_ENVELOPE(element))
3297 element = EL_ENVELOPE_1;
3299 envelope_nr = element - EL_ENVELOPE_1;
3301 envelope_len = getFile16BitBE(file);
3303 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3304 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3306 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3308 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3309 if (chunk_size_expected != chunk_size)
3311 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3312 return chunk_size_expected;
3315 for (i = 0; i < envelope_len; i++)
3316 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3321 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3323 int num_changed_custom_elements = getFile16BitBE(file);
3324 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3327 if (chunk_size_expected != chunk_size)
3329 ReadUnusedBytesFromFile(file, chunk_size - 2);
3330 return chunk_size_expected;
3333 for (i = 0; i < num_changed_custom_elements; i++)
3335 int element = getMappedElement(getFile16BitBE(file));
3336 int properties = getFile32BitBE(file);
3338 if (IS_CUSTOM_ELEMENT(element))
3339 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3341 Warn("invalid custom element number %d", element);
3343 // older game versions that wrote level files with CUS1 chunks used
3344 // different default push delay values (not yet stored in level file)
3345 element_info[element].push_delay_fixed = 2;
3346 element_info[element].push_delay_random = 8;
3349 level->file_has_custom_elements = TRUE;
3354 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3356 int num_changed_custom_elements = getFile16BitBE(file);
3357 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3360 if (chunk_size_expected != chunk_size)
3362 ReadUnusedBytesFromFile(file, chunk_size - 2);
3363 return chunk_size_expected;
3366 for (i = 0; i < num_changed_custom_elements; i++)
3368 int element = getMappedElement(getFile16BitBE(file));
3369 int custom_target_element = getMappedElement(getFile16BitBE(file));
3371 if (IS_CUSTOM_ELEMENT(element))
3372 element_info[element].change->target_element = custom_target_element;
3374 Warn("invalid custom element number %d", element);
3377 level->file_has_custom_elements = TRUE;
3382 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3384 int num_changed_custom_elements = getFile16BitBE(file);
3385 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3388 if (chunk_size_expected != chunk_size)
3390 ReadUnusedBytesFromFile(file, chunk_size - 2);
3391 return chunk_size_expected;
3394 for (i = 0; i < num_changed_custom_elements; i++)
3396 int element = getMappedElement(getFile16BitBE(file));
3397 struct ElementInfo *ei = &element_info[element];
3398 unsigned int event_bits;
3400 if (!IS_CUSTOM_ELEMENT(element))
3402 Warn("invalid custom element number %d", element);
3404 element = EL_INTERNAL_DUMMY;
3407 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3408 ei->description[j] = getFile8Bit(file);
3409 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3411 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3413 // some free bytes for future properties and padding
3414 ReadUnusedBytesFromFile(file, 7);
3416 ei->use_gfx_element = getFile8Bit(file);
3417 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3419 ei->collect_score_initial = getFile8Bit(file);
3420 ei->collect_count_initial = getFile8Bit(file);
3422 ei->push_delay_fixed = getFile16BitBE(file);
3423 ei->push_delay_random = getFile16BitBE(file);
3424 ei->move_delay_fixed = getFile16BitBE(file);
3425 ei->move_delay_random = getFile16BitBE(file);
3427 ei->move_pattern = getFile16BitBE(file);
3428 ei->move_direction_initial = getFile8Bit(file);
3429 ei->move_stepsize = getFile8Bit(file);
3431 for (y = 0; y < 3; y++)
3432 for (x = 0; x < 3; x++)
3433 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3435 // bits 0 - 31 of "has_event[]"
3436 event_bits = getFile32BitBE(file);
3437 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3438 if (event_bits & (1u << j))
3439 ei->change->has_event[j] = TRUE;
3441 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3443 ei->change->delay_fixed = getFile16BitBE(file);
3444 ei->change->delay_random = getFile16BitBE(file);
3445 ei->change->delay_frames = getFile16BitBE(file);
3447 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3449 ei->change->explode = getFile8Bit(file);
3450 ei->change->use_target_content = getFile8Bit(file);
3451 ei->change->only_if_complete = getFile8Bit(file);
3452 ei->change->use_random_replace = getFile8Bit(file);
3454 ei->change->random_percentage = getFile8Bit(file);
3455 ei->change->replace_when = getFile8Bit(file);
3457 for (y = 0; y < 3; y++)
3458 for (x = 0; x < 3; x++)
3459 ei->change->target_content.e[x][y] =
3460 getMappedElement(getFile16BitBE(file));
3462 ei->slippery_type = getFile8Bit(file);
3464 // some free bytes for future properties and padding
3465 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3467 // mark that this custom element has been modified
3468 ei->modified_settings = TRUE;
3471 level->file_has_custom_elements = TRUE;
3476 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3478 struct ElementInfo *ei;
3479 int chunk_size_expected;
3483 // ---------- custom element base property values (96 bytes) ----------------
3485 element = getMappedElement(getFile16BitBE(file));
3487 if (!IS_CUSTOM_ELEMENT(element))
3489 Warn("invalid custom element number %d", element);
3491 ReadUnusedBytesFromFile(file, chunk_size - 2);
3496 ei = &element_info[element];
3498 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3499 ei->description[i] = getFile8Bit(file);
3500 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3502 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3504 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3506 ei->num_change_pages = getFile8Bit(file);
3508 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3509 if (chunk_size_expected != chunk_size)
3511 ReadUnusedBytesFromFile(file, chunk_size - 43);
3512 return chunk_size_expected;
3515 ei->ce_value_fixed_initial = getFile16BitBE(file);
3516 ei->ce_value_random_initial = getFile16BitBE(file);
3517 ei->use_last_ce_value = getFile8Bit(file);
3519 ei->use_gfx_element = getFile8Bit(file);
3520 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3522 ei->collect_score_initial = getFile8Bit(file);
3523 ei->collect_count_initial = getFile8Bit(file);
3525 ei->drop_delay_fixed = getFile8Bit(file);
3526 ei->push_delay_fixed = getFile8Bit(file);
3527 ei->drop_delay_random = getFile8Bit(file);
3528 ei->push_delay_random = getFile8Bit(file);
3529 ei->move_delay_fixed = getFile16BitBE(file);
3530 ei->move_delay_random = getFile16BitBE(file);
3532 // bits 0 - 15 of "move_pattern" ...
3533 ei->move_pattern = getFile16BitBE(file);
3534 ei->move_direction_initial = getFile8Bit(file);
3535 ei->move_stepsize = getFile8Bit(file);
3537 ei->slippery_type = getFile8Bit(file);
3539 for (y = 0; y < 3; y++)
3540 for (x = 0; x < 3; x++)
3541 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3543 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3544 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3545 ei->move_leave_type = getFile8Bit(file);
3547 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3548 ei->move_pattern |= (getFile16BitBE(file) << 16);
3550 ei->access_direction = getFile8Bit(file);
3552 ei->explosion_delay = getFile8Bit(file);
3553 ei->ignition_delay = getFile8Bit(file);
3554 ei->explosion_type = getFile8Bit(file);
3556 // some free bytes for future custom property values and padding
3557 ReadUnusedBytesFromFile(file, 1);
3559 // ---------- change page property values (48 bytes) ------------------------
3561 setElementChangePages(ei, ei->num_change_pages);
3563 for (i = 0; i < ei->num_change_pages; i++)
3565 struct ElementChangeInfo *change = &ei->change_page[i];
3566 unsigned int event_bits;
3568 // always start with reliable default values
3569 setElementChangeInfoToDefaults(change);
3571 // bits 0 - 31 of "has_event[]" ...
3572 event_bits = getFile32BitBE(file);
3573 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3574 if (event_bits & (1u << j))
3575 change->has_event[j] = TRUE;
3577 change->target_element = getMappedElement(getFile16BitBE(file));
3579 change->delay_fixed = getFile16BitBE(file);
3580 change->delay_random = getFile16BitBE(file);
3581 change->delay_frames = getFile16BitBE(file);
3583 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3585 change->explode = getFile8Bit(file);
3586 change->use_target_content = getFile8Bit(file);
3587 change->only_if_complete = getFile8Bit(file);
3588 change->use_random_replace = getFile8Bit(file);
3590 change->random_percentage = getFile8Bit(file);
3591 change->replace_when = getFile8Bit(file);
3593 for (y = 0; y < 3; y++)
3594 for (x = 0; x < 3; x++)
3595 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3597 change->can_change = getFile8Bit(file);
3599 change->trigger_side = getFile8Bit(file);
3601 change->trigger_player = getFile8Bit(file);
3602 change->trigger_page = getFile8Bit(file);
3604 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3605 CH_PAGE_ANY : (1 << change->trigger_page));
3607 change->has_action = getFile8Bit(file);
3608 change->action_type = getFile8Bit(file);
3609 change->action_mode = getFile8Bit(file);
3610 change->action_arg = getFile16BitBE(file);
3612 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3613 event_bits = getFile8Bit(file);
3614 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3615 if (event_bits & (1u << (j - 32)))
3616 change->has_event[j] = TRUE;
3619 // mark this custom element as modified
3620 ei->modified_settings = TRUE;
3622 level->file_has_custom_elements = TRUE;
3627 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3629 struct ElementInfo *ei;
3630 struct ElementGroupInfo *group;
3634 element = getMappedElement(getFile16BitBE(file));
3636 if (!IS_GROUP_ELEMENT(element))
3638 Warn("invalid group element number %d", element);
3640 ReadUnusedBytesFromFile(file, chunk_size - 2);
3645 ei = &element_info[element];
3647 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3648 ei->description[i] = getFile8Bit(file);
3649 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3651 group = element_info[element].group;
3653 group->num_elements = getFile8Bit(file);
3655 ei->use_gfx_element = getFile8Bit(file);
3656 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3658 group->choice_mode = getFile8Bit(file);
3660 // some free bytes for future values and padding
3661 ReadUnusedBytesFromFile(file, 3);
3663 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3664 group->element[i] = getMappedElement(getFile16BitBE(file));
3666 // mark this group element as modified
3667 element_info[element].modified_settings = TRUE;
3669 level->file_has_custom_elements = TRUE;
3674 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3675 int element, int real_element)
3677 int micro_chunk_size = 0;
3678 int conf_type = getFile8Bit(file);
3679 int byte_mask = conf_type & CONF_MASK_BYTES;
3680 boolean element_found = FALSE;
3683 micro_chunk_size += 1;
3685 if (byte_mask == CONF_MASK_MULTI_BYTES)
3687 int num_bytes = getFile16BitBE(file);
3688 byte *buffer = checked_malloc(num_bytes);
3690 ReadBytesFromFile(file, buffer, num_bytes);
3692 for (i = 0; conf[i].data_type != -1; i++)
3694 if (conf[i].element == element &&
3695 conf[i].conf_type == conf_type)
3697 int data_type = conf[i].data_type;
3698 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3699 int max_num_entities = conf[i].max_num_entities;
3701 if (num_entities > max_num_entities)
3703 Warn("truncating number of entities for element %d from %d to %d",
3704 element, num_entities, max_num_entities);
3706 num_entities = max_num_entities;
3709 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3710 data_type == TYPE_CONTENT_LIST))
3712 // for element and content lists, zero entities are not allowed
3713 Warn("found empty list of entities for element %d", element);
3715 // do not set "num_entities" here to prevent reading behind buffer
3717 *(int *)(conf[i].num_entities) = 1; // at least one is required
3721 *(int *)(conf[i].num_entities) = num_entities;
3724 element_found = TRUE;
3726 if (data_type == TYPE_STRING)
3728 char *string = (char *)(conf[i].value);
3731 for (j = 0; j < max_num_entities; j++)
3732 string[j] = (j < num_entities ? buffer[j] : '\0');
3734 else if (data_type == TYPE_ELEMENT_LIST)
3736 int *element_array = (int *)(conf[i].value);
3739 for (j = 0; j < num_entities; j++)
3741 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3743 else if (data_type == TYPE_CONTENT_LIST)
3745 struct Content *content= (struct Content *)(conf[i].value);
3748 for (c = 0; c < num_entities; c++)
3749 for (y = 0; y < 3; y++)
3750 for (x = 0; x < 3; x++)
3751 content[c].e[x][y] =
3752 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3755 element_found = FALSE;
3761 checked_free(buffer);
3763 micro_chunk_size += 2 + num_bytes;
3765 else // constant size configuration data (1, 2 or 4 bytes)
3767 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3768 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3769 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3771 for (i = 0; conf[i].data_type != -1; i++)
3773 if (conf[i].element == element &&
3774 conf[i].conf_type == conf_type)
3776 int data_type = conf[i].data_type;
3778 if (data_type == TYPE_ELEMENT)
3779 value = getMappedElement(value);
3781 if (data_type == TYPE_BOOLEAN)
3782 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3784 *(int *) (conf[i].value) = value;
3786 element_found = TRUE;
3792 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3797 char *error_conf_chunk_bytes =
3798 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3799 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3800 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3801 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3802 int error_element = real_element;
3804 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3805 error_conf_chunk_bytes, error_conf_chunk_token,
3806 error_element, EL_NAME(error_element));
3809 return micro_chunk_size;
3812 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3814 int real_chunk_size = 0;
3816 li = *level; // copy level data into temporary buffer
3818 while (!checkEndOfFile(file))
3820 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3822 if (real_chunk_size >= chunk_size)
3826 *level = li; // copy temporary buffer back to level data
3828 return real_chunk_size;
3831 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3833 int real_chunk_size = 0;
3835 li = *level; // copy level data into temporary buffer
3837 while (!checkEndOfFile(file))
3839 int element = getMappedElement(getFile16BitBE(file));
3841 real_chunk_size += 2;
3842 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3844 if (real_chunk_size >= chunk_size)
3848 *level = li; // copy temporary buffer back to level data
3850 return real_chunk_size;
3853 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3855 int real_chunk_size = 0;
3857 li = *level; // copy level data into temporary buffer
3859 while (!checkEndOfFile(file))
3861 int element = getMappedElement(getFile16BitBE(file));
3863 real_chunk_size += 2;
3864 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3866 if (real_chunk_size >= chunk_size)
3870 *level = li; // copy temporary buffer back to level data
3872 return real_chunk_size;
3875 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3877 int element = getMappedElement(getFile16BitBE(file));
3878 int envelope_nr = element - EL_ENVELOPE_1;
3879 int real_chunk_size = 2;
3881 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3883 while (!checkEndOfFile(file))
3885 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3888 if (real_chunk_size >= chunk_size)
3892 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3894 return real_chunk_size;
3897 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3899 int element = getMappedElement(getFile16BitBE(file));
3900 int real_chunk_size = 2;
3901 struct ElementInfo *ei = &element_info[element];
3904 xx_ei = *ei; // copy element data into temporary buffer
3906 xx_ei.num_change_pages = -1;
3908 while (!checkEndOfFile(file))
3910 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3912 if (xx_ei.num_change_pages != -1)
3915 if (real_chunk_size >= chunk_size)
3921 if (ei->num_change_pages == -1)
3923 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3926 ei->num_change_pages = 1;
3928 setElementChangePages(ei, 1);
3929 setElementChangeInfoToDefaults(ei->change);
3931 return real_chunk_size;
3934 // initialize number of change pages stored for this custom element
3935 setElementChangePages(ei, ei->num_change_pages);
3936 for (i = 0; i < ei->num_change_pages; i++)
3937 setElementChangeInfoToDefaults(&ei->change_page[i]);
3939 // start with reading properties for the first change page
3940 xx_current_change_page = 0;
3942 while (!checkEndOfFile(file))
3944 // level file might contain invalid change page number
3945 if (xx_current_change_page >= ei->num_change_pages)
3948 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3950 xx_change = *change; // copy change data into temporary buffer
3952 resetEventBits(); // reset bits; change page might have changed
3954 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3957 *change = xx_change;
3959 setEventFlagsFromEventBits(change);
3961 if (real_chunk_size >= chunk_size)
3965 level->file_has_custom_elements = TRUE;
3967 return real_chunk_size;
3970 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3972 int element = getMappedElement(getFile16BitBE(file));
3973 int real_chunk_size = 2;
3974 struct ElementInfo *ei = &element_info[element];
3975 struct ElementGroupInfo *group = ei->group;
3980 xx_ei = *ei; // copy element data into temporary buffer
3981 xx_group = *group; // copy group data into temporary buffer
3983 while (!checkEndOfFile(file))
3985 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3988 if (real_chunk_size >= chunk_size)
3995 level->file_has_custom_elements = TRUE;
3997 return real_chunk_size;
4000 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4002 int element = getMappedElement(getFile16BitBE(file));
4003 int real_chunk_size = 2;
4004 struct ElementInfo *ei = &element_info[element];
4006 xx_ei = *ei; // copy element data into temporary buffer
4008 while (!checkEndOfFile(file))
4010 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4013 if (real_chunk_size >= chunk_size)
4019 level->file_has_custom_elements = TRUE;
4021 return real_chunk_size;
4024 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4025 struct LevelFileInfo *level_file_info,
4026 boolean level_info_only)
4028 char *filename = level_file_info->filename;
4029 char cookie[MAX_LINE_LEN];
4030 char chunk_name[CHUNK_ID_LEN + 1];
4034 if (!(file = openFile(filename, MODE_READ)))
4036 level->no_valid_file = TRUE;
4037 level->no_level_file = TRUE;
4039 if (level_info_only)
4042 Warn("cannot read level '%s' -- using empty level", filename);
4044 if (!setup.editor.use_template_for_new_levels)
4047 // if level file not found, try to initialize level data from template
4048 filename = getGlobalLevelTemplateFilename();
4050 if (!(file = openFile(filename, MODE_READ)))
4053 // default: for empty levels, use level template for custom elements
4054 level->use_custom_template = TRUE;
4056 level->no_valid_file = FALSE;
4059 getFileChunkBE(file, chunk_name, NULL);
4060 if (strEqual(chunk_name, "RND1"))
4062 getFile32BitBE(file); // not used
4064 getFileChunkBE(file, chunk_name, NULL);
4065 if (!strEqual(chunk_name, "CAVE"))
4067 level->no_valid_file = TRUE;
4069 Warn("unknown format of level file '%s'", filename);
4076 else // check for pre-2.0 file format with cookie string
4078 strcpy(cookie, chunk_name);
4079 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4081 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4082 cookie[strlen(cookie) - 1] = '\0';
4084 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4086 level->no_valid_file = TRUE;
4088 Warn("unknown format of level file '%s'", filename);
4095 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4097 level->no_valid_file = TRUE;
4099 Warn("unsupported version of level file '%s'", filename);
4106 // pre-2.0 level files have no game version, so use file version here
4107 level->game_version = level->file_version;
4110 if (level->file_version < FILE_VERSION_1_2)
4112 // level files from versions before 1.2.0 without chunk structure
4113 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4114 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4122 int (*loader)(File *, int, struct LevelInfo *);
4126 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4127 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4128 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4129 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4130 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4131 { "INFO", -1, LoadLevel_INFO },
4132 { "BODY", -1, LoadLevel_BODY },
4133 { "CONT", -1, LoadLevel_CONT },
4134 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4135 { "CNT3", -1, LoadLevel_CNT3 },
4136 { "CUS1", -1, LoadLevel_CUS1 },
4137 { "CUS2", -1, LoadLevel_CUS2 },
4138 { "CUS3", -1, LoadLevel_CUS3 },
4139 { "CUS4", -1, LoadLevel_CUS4 },
4140 { "GRP1", -1, LoadLevel_GRP1 },
4141 { "CONF", -1, LoadLevel_CONF },
4142 { "ELEM", -1, LoadLevel_ELEM },
4143 { "NOTE", -1, LoadLevel_NOTE },
4144 { "CUSX", -1, LoadLevel_CUSX },
4145 { "GRPX", -1, LoadLevel_GRPX },
4146 { "EMPX", -1, LoadLevel_EMPX },
4151 while (getFileChunkBE(file, chunk_name, &chunk_size))
4155 while (chunk_info[i].name != NULL &&
4156 !strEqual(chunk_name, chunk_info[i].name))
4159 if (chunk_info[i].name == NULL)
4161 Warn("unknown chunk '%s' in level file '%s'",
4162 chunk_name, filename);
4164 ReadUnusedBytesFromFile(file, chunk_size);
4166 else if (chunk_info[i].size != -1 &&
4167 chunk_info[i].size != chunk_size)
4169 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4170 chunk_size, chunk_name, filename);
4172 ReadUnusedBytesFromFile(file, chunk_size);
4176 // call function to load this level chunk
4177 int chunk_size_expected =
4178 (chunk_info[i].loader)(file, chunk_size, level);
4180 if (chunk_size_expected < 0)
4182 Warn("error reading chunk '%s' in level file '%s'",
4183 chunk_name, filename);
4188 // the size of some chunks cannot be checked before reading other
4189 // chunks first (like "HEAD" and "BODY") that contain some header
4190 // information, so check them here
4191 if (chunk_size_expected != chunk_size)
4193 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4194 chunk_size, chunk_name, filename);
4206 // ----------------------------------------------------------------------------
4207 // functions for loading BD level
4208 // ----------------------------------------------------------------------------
4210 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4211 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4213 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4215 struct LevelInfo_BD *level_bd = level->native_bd_level;
4216 GdCave *cave = NULL; // will be changed below
4217 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4218 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4221 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4223 // cave and map newly allocated when set to defaults above
4224 cave = level_bd->cave;
4227 cave->intermission = level->bd_intermission;
4230 cave->level_time[0] = level->time;
4231 cave->level_diamonds[0] = level->gems_needed;
4234 cave->scheduling = level->bd_scheduling_type;
4235 cave->pal_timing = level->bd_pal_timing;
4236 cave->level_speed[0] = level->bd_cycle_delay_ms;
4237 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4238 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4239 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4242 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4243 cave->diamond_value = level->score[SC_EMERALD];
4244 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4246 // compatibility settings
4247 cave->lineshift = level->bd_line_shifting_borders;
4248 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4249 cave->short_explosions = level->bd_short_explosions;
4251 // player properties
4252 cave->diagonal_movements = level->bd_diagonal_movements;
4253 cave->active_is_first_found = level->bd_topmost_player_active;
4254 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4255 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4256 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4257 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4259 // element properties
4260 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4261 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4262 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4263 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4264 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4265 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4266 cave->level_magic_wall_time[0] = level->time_magic_wall;
4267 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4268 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4270 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4271 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4272 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4273 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4274 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4275 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4276 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4278 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4279 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4280 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4281 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4282 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4283 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4284 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4285 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4286 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4287 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4288 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4290 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4291 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4292 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4293 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4294 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4295 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4297 cave->slime_predictable = level->bd_slime_is_predictable;
4298 cave->slime_correct_random = level->bd_slime_correct_random;
4299 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4300 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4301 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4302 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4303 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4304 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4305 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4306 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4307 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4308 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4310 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4311 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4312 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4314 cave->biter_delay_frame = level->bd_biter_move_delay;
4315 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4317 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4319 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4321 cave->replicators_active = level->bd_replicators_active;
4322 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4324 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4325 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4327 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4329 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4331 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4332 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4333 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4335 cave->infinite_rockets = level->bd_infinite_rockets;
4337 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4338 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4340 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4341 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4343 cave->creatures_backwards = level->bd_creatures_start_backwards;
4344 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4345 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4347 cave->gravity = level->bd_gravity_direction;
4348 cave->gravity_switch_active = level->bd_gravity_switch_active;
4349 cave->gravity_change_time = level->bd_gravity_switch_delay;
4350 cave->gravity_affects_all = level->bd_gravity_affects_all;
4352 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4353 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4354 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4355 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4357 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4358 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4359 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4360 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4361 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4362 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4364 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4365 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4366 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4367 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4370 strncpy(cave->name, level->name, sizeof(GdString));
4371 cave->name[sizeof(GdString) - 1] = '\0';
4373 // playfield elements
4374 for (x = 0; x < cave->w; x++)
4375 for (y = 0; y < cave->h; y++)
4376 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4379 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4381 struct LevelInfo_BD *level_bd = level->native_bd_level;
4382 GdCave *cave = level_bd->cave;
4383 int bd_level_nr = level_bd->level_nr;
4386 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4387 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4390 level->bd_intermission = cave->intermission;
4393 level->time = cave->level_time[bd_level_nr];
4394 level->gems_needed = cave->level_diamonds[bd_level_nr];
4397 level->bd_scheduling_type = cave->scheduling;
4398 level->bd_pal_timing = cave->pal_timing;
4399 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4400 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4401 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4402 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4405 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4406 level->score[SC_EMERALD] = cave->diamond_value;
4407 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4409 // compatibility settings
4410 level->bd_line_shifting_borders = cave->lineshift;
4411 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4412 level->bd_short_explosions = cave->short_explosions;
4414 // player properties
4415 level->bd_diagonal_movements = cave->diagonal_movements;
4416 level->bd_topmost_player_active = cave->active_is_first_found;
4417 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4418 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4419 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4420 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4422 // element properties
4423 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4424 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4425 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4426 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4427 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4428 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4429 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4430 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4431 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4433 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4434 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4435 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4436 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4437 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4438 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4439 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4441 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4442 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4443 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4444 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4445 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4446 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4447 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4448 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4449 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4450 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4451 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4453 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4454 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4455 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4456 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4457 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4458 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4460 level->bd_slime_is_predictable = cave->slime_predictable;
4461 level->bd_slime_correct_random = cave->slime_correct_random;
4462 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4463 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4464 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4465 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4466 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4467 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4468 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4469 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4470 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4471 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4473 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4474 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4475 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4477 level->bd_biter_move_delay = cave->biter_delay_frame;
4478 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4480 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4482 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4484 level->bd_replicators_active = cave->replicators_active;
4485 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4487 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4488 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4490 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4492 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4494 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4495 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4496 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4498 level->bd_infinite_rockets = cave->infinite_rockets;
4500 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4501 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4503 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4504 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4506 level->bd_creatures_start_backwards = cave->creatures_backwards;
4507 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4508 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4510 level->bd_gravity_direction = cave->gravity;
4511 level->bd_gravity_switch_active = cave->gravity_switch_active;
4512 level->bd_gravity_switch_delay = cave->gravity_change_time;
4513 level->bd_gravity_affects_all = cave->gravity_affects_all;
4515 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4516 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4517 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4518 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4520 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4521 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4522 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4523 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4524 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4525 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4527 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4528 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4529 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4530 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4533 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4535 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4536 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4538 // playfield elements
4539 for (x = 0; x < level->fieldx; x++)
4540 for (y = 0; y < level->fieldy; y++)
4541 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4543 checked_free(cave_name);
4546 static void setTapeInfoToDefaults(void);
4548 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4550 struct LevelInfo_BD *level_bd = level->native_bd_level;
4551 GdCave *cave = level_bd->cave;
4552 GdReplay *replay = level_bd->replay;
4558 // always start with reliable default values
4559 setTapeInfoToDefaults();
4561 tape.level_nr = level_nr; // (currently not used)
4562 tape.random_seed = replay->seed;
4564 TapeSetDateFromIsoDateString(replay->date);
4567 tape.pos[tape.counter].delay = 0;
4569 tape.bd_replay = TRUE;
4571 // all time calculations only used to display approximate tape time
4572 int cave_speed = cave->speed;
4573 int milliseconds_game = 0;
4574 int milliseconds_elapsed = 20;
4576 for (i = 0; i < replay->movements->len; i++)
4578 int replay_action = replay->movements->data[i];
4579 int tape_action = map_action_BD_to_RND(replay_action);
4580 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4581 boolean success = 0;
4585 success = TapeAddAction(action);
4587 milliseconds_game += milliseconds_elapsed;
4589 if (milliseconds_game >= cave_speed)
4591 milliseconds_game -= cave_speed;
4598 tape.pos[tape.counter].delay = 0;
4599 tape.pos[tape.counter].action[0] = 0;
4603 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4609 TapeHaltRecording();
4611 if (!replay->success)
4612 Warn("BD replay is marked as not successful");
4616 // ----------------------------------------------------------------------------
4617 // functions for loading EM level
4618 // ----------------------------------------------------------------------------
4620 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4622 static int ball_xy[8][2] =
4633 struct LevelInfo_EM *level_em = level->native_em_level;
4634 struct CAVE *cav = level_em->cav;
4637 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4638 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4640 cav->time_seconds = level->time;
4641 cav->gems_needed = level->gems_needed;
4643 cav->emerald_score = level->score[SC_EMERALD];
4644 cav->diamond_score = level->score[SC_DIAMOND];
4645 cav->alien_score = level->score[SC_ROBOT];
4646 cav->tank_score = level->score[SC_SPACESHIP];
4647 cav->bug_score = level->score[SC_BUG];
4648 cav->eater_score = level->score[SC_YAMYAM];
4649 cav->nut_score = level->score[SC_NUT];
4650 cav->dynamite_score = level->score[SC_DYNAMITE];
4651 cav->key_score = level->score[SC_KEY];
4652 cav->exit_score = level->score[SC_TIME_BONUS];
4654 cav->num_eater_arrays = level->num_yamyam_contents;
4656 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4657 for (y = 0; y < 3; y++)
4658 for (x = 0; x < 3; x++)
4659 cav->eater_array[i][y * 3 + x] =
4660 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4662 cav->amoeba_time = level->amoeba_speed;
4663 cav->wonderwall_time = level->time_magic_wall;
4664 cav->wheel_time = level->time_wheel;
4666 cav->android_move_time = level->android_move_time;
4667 cav->android_clone_time = level->android_clone_time;
4668 cav->ball_random = level->ball_random;
4669 cav->ball_active = level->ball_active_initial;
4670 cav->ball_time = level->ball_time;
4671 cav->num_ball_arrays = level->num_ball_contents;
4673 cav->lenses_score = level->lenses_score;
4674 cav->magnify_score = level->magnify_score;
4675 cav->slurp_score = level->slurp_score;
4677 cav->lenses_time = level->lenses_time;
4678 cav->magnify_time = level->magnify_time;
4680 cav->wind_time = 9999;
4681 cav->wind_direction =
4682 map_direction_RND_to_EM(level->wind_direction_initial);
4684 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4685 for (j = 0; j < 8; j++)
4686 cav->ball_array[i][j] =
4687 map_element_RND_to_EM_cave(level->ball_content[i].
4688 e[ball_xy[j][0]][ball_xy[j][1]]);
4690 map_android_clone_elements_RND_to_EM(level);
4692 // first fill the complete playfield with the empty space element
4693 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4694 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4695 cav->cave[x][y] = Cblank;
4697 // then copy the real level contents from level file into the playfield
4698 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4700 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4702 if (level->field[x][y] == EL_AMOEBA_DEAD)
4703 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4705 cav->cave[x][y] = new_element;
4708 for (i = 0; i < MAX_PLAYERS; i++)
4710 cav->player_x[i] = -1;
4711 cav->player_y[i] = -1;
4714 // initialize player positions and delete players from the playfield
4715 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4717 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4719 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4721 cav->player_x[player_nr] = x;
4722 cav->player_y[player_nr] = y;
4724 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4729 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4731 static int ball_xy[8][2] =
4742 struct LevelInfo_EM *level_em = level->native_em_level;
4743 struct CAVE *cav = level_em->cav;
4746 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4747 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4749 level->time = cav->time_seconds;
4750 level->gems_needed = cav->gems_needed;
4752 sprintf(level->name, "Level %d", level->file_info.nr);
4754 level->score[SC_EMERALD] = cav->emerald_score;
4755 level->score[SC_DIAMOND] = cav->diamond_score;
4756 level->score[SC_ROBOT] = cav->alien_score;
4757 level->score[SC_SPACESHIP] = cav->tank_score;
4758 level->score[SC_BUG] = cav->bug_score;
4759 level->score[SC_YAMYAM] = cav->eater_score;
4760 level->score[SC_NUT] = cav->nut_score;
4761 level->score[SC_DYNAMITE] = cav->dynamite_score;
4762 level->score[SC_KEY] = cav->key_score;
4763 level->score[SC_TIME_BONUS] = cav->exit_score;
4765 level->num_yamyam_contents = cav->num_eater_arrays;
4767 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4768 for (y = 0; y < 3; y++)
4769 for (x = 0; x < 3; x++)
4770 level->yamyam_content[i].e[x][y] =
4771 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4773 level->amoeba_speed = cav->amoeba_time;
4774 level->time_magic_wall = cav->wonderwall_time;
4775 level->time_wheel = cav->wheel_time;
4777 level->android_move_time = cav->android_move_time;
4778 level->android_clone_time = cav->android_clone_time;
4779 level->ball_random = cav->ball_random;
4780 level->ball_active_initial = cav->ball_active;
4781 level->ball_time = cav->ball_time;
4782 level->num_ball_contents = cav->num_ball_arrays;
4784 level->lenses_score = cav->lenses_score;
4785 level->magnify_score = cav->magnify_score;
4786 level->slurp_score = cav->slurp_score;
4788 level->lenses_time = cav->lenses_time;
4789 level->magnify_time = cav->magnify_time;
4791 level->wind_direction_initial =
4792 map_direction_EM_to_RND(cav->wind_direction);
4794 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4795 for (j = 0; j < 8; j++)
4796 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4797 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4799 map_android_clone_elements_EM_to_RND(level);
4801 // convert the playfield (some elements need special treatment)
4802 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4804 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4806 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4807 new_element = EL_AMOEBA_DEAD;
4809 level->field[x][y] = new_element;
4812 for (i = 0; i < MAX_PLAYERS; i++)
4814 // in case of all players set to the same field, use the first player
4815 int nr = MAX_PLAYERS - i - 1;
4816 int jx = cav->player_x[nr];
4817 int jy = cav->player_y[nr];
4819 if (jx != -1 && jy != -1)
4820 level->field[jx][jy] = EL_PLAYER_1 + nr;
4823 // time score is counted for each 10 seconds left in Emerald Mine levels
4824 level->time_score_base = 10;
4828 // ----------------------------------------------------------------------------
4829 // functions for loading SP level
4830 // ----------------------------------------------------------------------------
4832 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4834 struct LevelInfo_SP *level_sp = level->native_sp_level;
4835 LevelInfoType *header = &level_sp->header;
4838 level_sp->width = level->fieldx;
4839 level_sp->height = level->fieldy;
4841 for (x = 0; x < level->fieldx; x++)
4842 for (y = 0; y < level->fieldy; y++)
4843 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4845 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4847 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4848 header->LevelTitle[i] = level->name[i];
4849 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4851 header->InfotronsNeeded = level->gems_needed;
4853 header->SpecialPortCount = 0;
4855 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4857 boolean gravity_port_found = FALSE;
4858 boolean gravity_port_valid = FALSE;
4859 int gravity_port_flag;
4860 int gravity_port_base_element;
4861 int element = level->field[x][y];
4863 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4864 element <= EL_SP_GRAVITY_ON_PORT_UP)
4866 gravity_port_found = TRUE;
4867 gravity_port_valid = TRUE;
4868 gravity_port_flag = 1;
4869 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4871 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4872 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4874 gravity_port_found = TRUE;
4875 gravity_port_valid = TRUE;
4876 gravity_port_flag = 0;
4877 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4879 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4880 element <= EL_SP_GRAVITY_PORT_UP)
4882 // change R'n'D style gravity inverting special port to normal port
4883 // (there are no gravity inverting ports in native Supaplex engine)
4885 gravity_port_found = TRUE;
4886 gravity_port_valid = FALSE;
4887 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4890 if (gravity_port_found)
4892 if (gravity_port_valid &&
4893 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4895 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4897 port->PortLocation = (y * level->fieldx + x) * 2;
4898 port->Gravity = gravity_port_flag;
4900 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4902 header->SpecialPortCount++;
4906 // change special gravity port to normal port
4908 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4911 level_sp->playfield[x][y] = element - EL_SP_START;
4916 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4918 struct LevelInfo_SP *level_sp = level->native_sp_level;
4919 LevelInfoType *header = &level_sp->header;
4920 boolean num_invalid_elements = 0;
4923 level->fieldx = level_sp->width;
4924 level->fieldy = level_sp->height;
4926 for (x = 0; x < level->fieldx; x++)
4928 for (y = 0; y < level->fieldy; y++)
4930 int element_old = level_sp->playfield[x][y];
4931 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4933 if (element_new == EL_UNKNOWN)
4935 num_invalid_elements++;
4937 Debug("level:native:SP", "invalid element %d at position %d, %d",
4941 level->field[x][y] = element_new;
4945 if (num_invalid_elements > 0)
4946 Warn("found %d invalid elements%s", num_invalid_elements,
4947 (!options.debug ? " (use '--debug' for more details)" : ""));
4949 for (i = 0; i < MAX_PLAYERS; i++)
4950 level->initial_player_gravity[i] =
4951 (header->InitialGravity == 1 ? TRUE : FALSE);
4953 // skip leading spaces
4954 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4955 if (header->LevelTitle[i] != ' ')
4959 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4960 level->name[j] = header->LevelTitle[i];
4961 level->name[j] = '\0';
4963 // cut trailing spaces
4965 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4966 level->name[j - 1] = '\0';
4968 level->gems_needed = header->InfotronsNeeded;
4970 for (i = 0; i < header->SpecialPortCount; i++)
4972 SpecialPortType *port = &header->SpecialPort[i];
4973 int port_location = port->PortLocation;
4974 int gravity = port->Gravity;
4975 int port_x, port_y, port_element;
4977 port_x = (port_location / 2) % level->fieldx;
4978 port_y = (port_location / 2) / level->fieldx;
4980 if (port_x < 0 || port_x >= level->fieldx ||
4981 port_y < 0 || port_y >= level->fieldy)
4983 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4988 port_element = level->field[port_x][port_y];
4990 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4991 port_element > EL_SP_GRAVITY_PORT_UP)
4993 Warn("no special port at position (%d, %d)", port_x, port_y);
4998 // change previous (wrong) gravity inverting special port to either
4999 // gravity enabling special port or gravity disabling special port
5000 level->field[port_x][port_y] +=
5001 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5002 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5005 // change special gravity ports without database entries to normal ports
5006 for (x = 0; x < level->fieldx; x++)
5007 for (y = 0; y < level->fieldy; y++)
5008 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5009 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5010 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5012 level->time = 0; // no time limit
5013 level->amoeba_speed = 0;
5014 level->time_magic_wall = 0;
5015 level->time_wheel = 0;
5016 level->amoeba_content = EL_EMPTY;
5018 // original Supaplex does not use score values -- rate by playing time
5019 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5020 level->score[i] = 0;
5022 level->rate_time_over_score = TRUE;
5024 // there are no yamyams in supaplex levels
5025 for (i = 0; i < level->num_yamyam_contents; i++)
5026 for (x = 0; x < 3; x++)
5027 for (y = 0; y < 3; y++)
5028 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5031 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5033 struct LevelInfo_SP *level_sp = level->native_sp_level;
5034 struct DemoInfo_SP *demo = &level_sp->demo;
5037 // always start with reliable default values
5038 demo->is_available = FALSE;
5041 if (TAPE_IS_EMPTY(tape))
5044 demo->level_nr = tape.level_nr; // (currently not used)
5046 level_sp->header.DemoRandomSeed = tape.random_seed;
5050 for (i = 0; i < tape.length; i++)
5052 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5053 int demo_repeat = tape.pos[i].delay;
5054 int demo_entries = (demo_repeat + 15) / 16;
5056 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5058 Warn("tape truncated: size exceeds maximum SP demo size %d",
5064 for (j = 0; j < demo_repeat / 16; j++)
5065 demo->data[demo->length++] = 0xf0 | demo_action;
5067 if (demo_repeat % 16)
5068 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5071 demo->is_available = TRUE;
5074 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5076 struct LevelInfo_SP *level_sp = level->native_sp_level;
5077 struct DemoInfo_SP *demo = &level_sp->demo;
5078 char *filename = level->file_info.filename;
5081 // always start with reliable default values
5082 setTapeInfoToDefaults();
5084 if (!demo->is_available)
5087 tape.level_nr = demo->level_nr; // (currently not used)
5088 tape.random_seed = level_sp->header.DemoRandomSeed;
5090 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5093 tape.pos[tape.counter].delay = 0;
5095 for (i = 0; i < demo->length; i++)
5097 int demo_action = demo->data[i] & 0x0f;
5098 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5099 int tape_action = map_key_SP_to_RND(demo_action);
5100 int tape_repeat = demo_repeat + 1;
5101 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5102 boolean success = 0;
5105 for (j = 0; j < tape_repeat; j++)
5106 success = TapeAddAction(action);
5110 Warn("SP demo truncated: size exceeds maximum tape size %d",
5117 TapeHaltRecording();
5121 // ----------------------------------------------------------------------------
5122 // functions for loading MM level
5123 // ----------------------------------------------------------------------------
5125 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5127 struct LevelInfo_MM *level_mm = level->native_mm_level;
5130 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5131 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5133 level_mm->time = level->time;
5134 level_mm->kettles_needed = level->gems_needed;
5135 level_mm->auto_count_kettles = level->auto_count_gems;
5137 level_mm->mm_laser_red = level->mm_laser_red;
5138 level_mm->mm_laser_green = level->mm_laser_green;
5139 level_mm->mm_laser_blue = level->mm_laser_blue;
5141 level_mm->df_laser_red = level->df_laser_red;
5142 level_mm->df_laser_green = level->df_laser_green;
5143 level_mm->df_laser_blue = level->df_laser_blue;
5145 strcpy(level_mm->name, level->name);
5146 strcpy(level_mm->author, level->author);
5148 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5149 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5150 level_mm->score[SC_KEY] = level->score[SC_KEY];
5151 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5152 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5154 level_mm->amoeba_speed = level->amoeba_speed;
5155 level_mm->time_fuse = level->mm_time_fuse;
5156 level_mm->time_bomb = level->mm_time_bomb;
5157 level_mm->time_ball = level->mm_time_ball;
5158 level_mm->time_block = level->mm_time_block;
5160 level_mm->num_ball_contents = level->num_mm_ball_contents;
5161 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5162 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5163 level_mm->explode_ball = level->explode_mm_ball;
5165 for (i = 0; i < level->num_mm_ball_contents; i++)
5166 level_mm->ball_content[i] =
5167 map_element_RND_to_MM(level->mm_ball_content[i]);
5169 for (x = 0; x < level->fieldx; x++)
5170 for (y = 0; y < level->fieldy; y++)
5172 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5175 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5177 struct LevelInfo_MM *level_mm = level->native_mm_level;
5180 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5181 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5183 level->time = level_mm->time;
5184 level->gems_needed = level_mm->kettles_needed;
5185 level->auto_count_gems = level_mm->auto_count_kettles;
5187 level->mm_laser_red = level_mm->mm_laser_red;
5188 level->mm_laser_green = level_mm->mm_laser_green;
5189 level->mm_laser_blue = level_mm->mm_laser_blue;
5191 level->df_laser_red = level_mm->df_laser_red;
5192 level->df_laser_green = level_mm->df_laser_green;
5193 level->df_laser_blue = level_mm->df_laser_blue;
5195 strcpy(level->name, level_mm->name);
5197 // only overwrite author from 'levelinfo.conf' if author defined in level
5198 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5199 strcpy(level->author, level_mm->author);
5201 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5202 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5203 level->score[SC_KEY] = level_mm->score[SC_KEY];
5204 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5205 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5207 level->amoeba_speed = level_mm->amoeba_speed;
5208 level->mm_time_fuse = level_mm->time_fuse;
5209 level->mm_time_bomb = level_mm->time_bomb;
5210 level->mm_time_ball = level_mm->time_ball;
5211 level->mm_time_block = level_mm->time_block;
5213 level->num_mm_ball_contents = level_mm->num_ball_contents;
5214 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5215 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5216 level->explode_mm_ball = level_mm->explode_ball;
5218 for (i = 0; i < level->num_mm_ball_contents; i++)
5219 level->mm_ball_content[i] =
5220 map_element_MM_to_RND(level_mm->ball_content[i]);
5222 for (x = 0; x < level->fieldx; x++)
5223 for (y = 0; y < level->fieldy; y++)
5224 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5228 // ----------------------------------------------------------------------------
5229 // functions for loading DC level
5230 // ----------------------------------------------------------------------------
5232 #define DC_LEVEL_HEADER_SIZE 344
5234 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5237 static int last_data_encoded;
5241 int diff_hi, diff_lo;
5242 int data_hi, data_lo;
5243 unsigned short data_decoded;
5247 last_data_encoded = 0;
5254 diff = data_encoded - last_data_encoded;
5255 diff_hi = diff & ~0xff;
5256 diff_lo = diff & 0xff;
5260 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5261 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5262 data_hi = data_hi & 0xff00;
5264 data_decoded = data_hi | data_lo;
5266 last_data_encoded = data_encoded;
5268 offset1 = (offset1 + 1) % 31;
5269 offset2 = offset2 & 0xff;
5271 return data_decoded;
5274 static int getMappedElement_DC(int element)
5282 // 0x0117 - 0x036e: (?)
5285 // 0x042d - 0x0684: (?)
5301 element = EL_CRYSTAL;
5304 case 0x0e77: // quicksand (boulder)
5305 element = EL_QUICKSAND_FAST_FULL;
5308 case 0x0e99: // slow quicksand (boulder)
5309 element = EL_QUICKSAND_FULL;
5313 element = EL_EM_EXIT_OPEN;
5317 element = EL_EM_EXIT_CLOSED;
5321 element = EL_EM_STEEL_EXIT_OPEN;
5325 element = EL_EM_STEEL_EXIT_CLOSED;
5328 case 0x0f4f: // dynamite (lit 1)
5329 element = EL_EM_DYNAMITE_ACTIVE;
5332 case 0x0f57: // dynamite (lit 2)
5333 element = EL_EM_DYNAMITE_ACTIVE;
5336 case 0x0f5f: // dynamite (lit 3)
5337 element = EL_EM_DYNAMITE_ACTIVE;
5340 case 0x0f67: // dynamite (lit 4)
5341 element = EL_EM_DYNAMITE_ACTIVE;
5348 element = EL_AMOEBA_WET;
5352 element = EL_AMOEBA_DROP;
5356 element = EL_DC_MAGIC_WALL;
5360 element = EL_SPACESHIP_UP;
5364 element = EL_SPACESHIP_DOWN;
5368 element = EL_SPACESHIP_LEFT;
5372 element = EL_SPACESHIP_RIGHT;
5376 element = EL_BUG_UP;
5380 element = EL_BUG_DOWN;
5384 element = EL_BUG_LEFT;
5388 element = EL_BUG_RIGHT;
5392 element = EL_MOLE_UP;
5396 element = EL_MOLE_DOWN;
5400 element = EL_MOLE_LEFT;
5404 element = EL_MOLE_RIGHT;
5412 element = EL_YAMYAM_UP;
5416 element = EL_SWITCHGATE_OPEN;
5420 element = EL_SWITCHGATE_CLOSED;
5424 element = EL_DC_SWITCHGATE_SWITCH_UP;
5428 element = EL_TIMEGATE_CLOSED;
5431 case 0x144c: // conveyor belt switch (green)
5432 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5435 case 0x144f: // conveyor belt switch (red)
5436 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5439 case 0x1452: // conveyor belt switch (blue)
5440 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5444 element = EL_CONVEYOR_BELT_3_MIDDLE;
5448 element = EL_CONVEYOR_BELT_3_LEFT;
5452 element = EL_CONVEYOR_BELT_3_RIGHT;
5456 element = EL_CONVEYOR_BELT_1_MIDDLE;
5460 element = EL_CONVEYOR_BELT_1_LEFT;
5464 element = EL_CONVEYOR_BELT_1_RIGHT;
5468 element = EL_CONVEYOR_BELT_4_MIDDLE;
5472 element = EL_CONVEYOR_BELT_4_LEFT;
5476 element = EL_CONVEYOR_BELT_4_RIGHT;
5480 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5484 element = EL_EXPANDABLE_WALL_VERTICAL;
5488 element = EL_EXPANDABLE_WALL_ANY;
5491 case 0x14ce: // growing steel wall (left/right)
5492 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5495 case 0x14df: // growing steel wall (up/down)
5496 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5499 case 0x14e8: // growing steel wall (up/down/left/right)
5500 element = EL_EXPANDABLE_STEELWALL_ANY;
5504 element = EL_SHIELD_DEADLY;
5508 element = EL_EXTRA_TIME;
5516 element = EL_EMPTY_SPACE;
5519 case 0x1578: // quicksand (empty)
5520 element = EL_QUICKSAND_FAST_EMPTY;
5523 case 0x1579: // slow quicksand (empty)
5524 element = EL_QUICKSAND_EMPTY;
5534 element = EL_EM_DYNAMITE;
5537 case 0x15a1: // key (red)
5538 element = EL_EM_KEY_1;
5541 case 0x15a2: // key (yellow)
5542 element = EL_EM_KEY_2;
5545 case 0x15a3: // key (blue)
5546 element = EL_EM_KEY_4;
5549 case 0x15a4: // key (green)
5550 element = EL_EM_KEY_3;
5553 case 0x15a5: // key (white)
5554 element = EL_DC_KEY_WHITE;
5558 element = EL_WALL_SLIPPERY;
5565 case 0x15a8: // wall (not round)
5569 case 0x15a9: // (blue)
5570 element = EL_CHAR_A;
5573 case 0x15aa: // (blue)
5574 element = EL_CHAR_B;
5577 case 0x15ab: // (blue)
5578 element = EL_CHAR_C;
5581 case 0x15ac: // (blue)
5582 element = EL_CHAR_D;
5585 case 0x15ad: // (blue)
5586 element = EL_CHAR_E;
5589 case 0x15ae: // (blue)
5590 element = EL_CHAR_F;
5593 case 0x15af: // (blue)
5594 element = EL_CHAR_G;
5597 case 0x15b0: // (blue)
5598 element = EL_CHAR_H;
5601 case 0x15b1: // (blue)
5602 element = EL_CHAR_I;
5605 case 0x15b2: // (blue)
5606 element = EL_CHAR_J;
5609 case 0x15b3: // (blue)
5610 element = EL_CHAR_K;
5613 case 0x15b4: // (blue)
5614 element = EL_CHAR_L;
5617 case 0x15b5: // (blue)
5618 element = EL_CHAR_M;
5621 case 0x15b6: // (blue)
5622 element = EL_CHAR_N;
5625 case 0x15b7: // (blue)
5626 element = EL_CHAR_O;
5629 case 0x15b8: // (blue)
5630 element = EL_CHAR_P;
5633 case 0x15b9: // (blue)
5634 element = EL_CHAR_Q;
5637 case 0x15ba: // (blue)
5638 element = EL_CHAR_R;
5641 case 0x15bb: // (blue)
5642 element = EL_CHAR_S;
5645 case 0x15bc: // (blue)
5646 element = EL_CHAR_T;
5649 case 0x15bd: // (blue)
5650 element = EL_CHAR_U;
5653 case 0x15be: // (blue)
5654 element = EL_CHAR_V;
5657 case 0x15bf: // (blue)
5658 element = EL_CHAR_W;
5661 case 0x15c0: // (blue)
5662 element = EL_CHAR_X;
5665 case 0x15c1: // (blue)
5666 element = EL_CHAR_Y;
5669 case 0x15c2: // (blue)
5670 element = EL_CHAR_Z;
5673 case 0x15c3: // (blue)
5674 element = EL_CHAR_AUMLAUT;
5677 case 0x15c4: // (blue)
5678 element = EL_CHAR_OUMLAUT;
5681 case 0x15c5: // (blue)
5682 element = EL_CHAR_UUMLAUT;
5685 case 0x15c6: // (blue)
5686 element = EL_CHAR_0;
5689 case 0x15c7: // (blue)
5690 element = EL_CHAR_1;
5693 case 0x15c8: // (blue)
5694 element = EL_CHAR_2;
5697 case 0x15c9: // (blue)
5698 element = EL_CHAR_3;
5701 case 0x15ca: // (blue)
5702 element = EL_CHAR_4;
5705 case 0x15cb: // (blue)
5706 element = EL_CHAR_5;
5709 case 0x15cc: // (blue)
5710 element = EL_CHAR_6;
5713 case 0x15cd: // (blue)
5714 element = EL_CHAR_7;
5717 case 0x15ce: // (blue)
5718 element = EL_CHAR_8;
5721 case 0x15cf: // (blue)
5722 element = EL_CHAR_9;
5725 case 0x15d0: // (blue)
5726 element = EL_CHAR_PERIOD;
5729 case 0x15d1: // (blue)
5730 element = EL_CHAR_EXCLAM;
5733 case 0x15d2: // (blue)
5734 element = EL_CHAR_COLON;
5737 case 0x15d3: // (blue)
5738 element = EL_CHAR_LESS;
5741 case 0x15d4: // (blue)
5742 element = EL_CHAR_GREATER;
5745 case 0x15d5: // (blue)
5746 element = EL_CHAR_QUESTION;
5749 case 0x15d6: // (blue)
5750 element = EL_CHAR_COPYRIGHT;
5753 case 0x15d7: // (blue)
5754 element = EL_CHAR_UP;
5757 case 0x15d8: // (blue)
5758 element = EL_CHAR_DOWN;
5761 case 0x15d9: // (blue)
5762 element = EL_CHAR_BUTTON;
5765 case 0x15da: // (blue)
5766 element = EL_CHAR_PLUS;
5769 case 0x15db: // (blue)
5770 element = EL_CHAR_MINUS;
5773 case 0x15dc: // (blue)
5774 element = EL_CHAR_APOSTROPHE;
5777 case 0x15dd: // (blue)
5778 element = EL_CHAR_PARENLEFT;
5781 case 0x15de: // (blue)
5782 element = EL_CHAR_PARENRIGHT;
5785 case 0x15df: // (green)
5786 element = EL_CHAR_A;
5789 case 0x15e0: // (green)
5790 element = EL_CHAR_B;
5793 case 0x15e1: // (green)
5794 element = EL_CHAR_C;
5797 case 0x15e2: // (green)
5798 element = EL_CHAR_D;
5801 case 0x15e3: // (green)
5802 element = EL_CHAR_E;
5805 case 0x15e4: // (green)
5806 element = EL_CHAR_F;
5809 case 0x15e5: // (green)
5810 element = EL_CHAR_G;
5813 case 0x15e6: // (green)
5814 element = EL_CHAR_H;
5817 case 0x15e7: // (green)
5818 element = EL_CHAR_I;
5821 case 0x15e8: // (green)
5822 element = EL_CHAR_J;
5825 case 0x15e9: // (green)
5826 element = EL_CHAR_K;
5829 case 0x15ea: // (green)
5830 element = EL_CHAR_L;
5833 case 0x15eb: // (green)
5834 element = EL_CHAR_M;
5837 case 0x15ec: // (green)
5838 element = EL_CHAR_N;
5841 case 0x15ed: // (green)
5842 element = EL_CHAR_O;
5845 case 0x15ee: // (green)
5846 element = EL_CHAR_P;
5849 case 0x15ef: // (green)
5850 element = EL_CHAR_Q;
5853 case 0x15f0: // (green)
5854 element = EL_CHAR_R;
5857 case 0x15f1: // (green)
5858 element = EL_CHAR_S;
5861 case 0x15f2: // (green)
5862 element = EL_CHAR_T;
5865 case 0x15f3: // (green)
5866 element = EL_CHAR_U;
5869 case 0x15f4: // (green)
5870 element = EL_CHAR_V;
5873 case 0x15f5: // (green)
5874 element = EL_CHAR_W;
5877 case 0x15f6: // (green)
5878 element = EL_CHAR_X;
5881 case 0x15f7: // (green)
5882 element = EL_CHAR_Y;
5885 case 0x15f8: // (green)
5886 element = EL_CHAR_Z;
5889 case 0x15f9: // (green)
5890 element = EL_CHAR_AUMLAUT;
5893 case 0x15fa: // (green)
5894 element = EL_CHAR_OUMLAUT;
5897 case 0x15fb: // (green)
5898 element = EL_CHAR_UUMLAUT;
5901 case 0x15fc: // (green)
5902 element = EL_CHAR_0;
5905 case 0x15fd: // (green)
5906 element = EL_CHAR_1;
5909 case 0x15fe: // (green)
5910 element = EL_CHAR_2;
5913 case 0x15ff: // (green)
5914 element = EL_CHAR_3;
5917 case 0x1600: // (green)
5918 element = EL_CHAR_4;
5921 case 0x1601: // (green)
5922 element = EL_CHAR_5;
5925 case 0x1602: // (green)
5926 element = EL_CHAR_6;
5929 case 0x1603: // (green)
5930 element = EL_CHAR_7;
5933 case 0x1604: // (green)
5934 element = EL_CHAR_8;
5937 case 0x1605: // (green)
5938 element = EL_CHAR_9;
5941 case 0x1606: // (green)
5942 element = EL_CHAR_PERIOD;
5945 case 0x1607: // (green)
5946 element = EL_CHAR_EXCLAM;
5949 case 0x1608: // (green)
5950 element = EL_CHAR_COLON;
5953 case 0x1609: // (green)
5954 element = EL_CHAR_LESS;
5957 case 0x160a: // (green)
5958 element = EL_CHAR_GREATER;
5961 case 0x160b: // (green)
5962 element = EL_CHAR_QUESTION;
5965 case 0x160c: // (green)
5966 element = EL_CHAR_COPYRIGHT;
5969 case 0x160d: // (green)
5970 element = EL_CHAR_UP;
5973 case 0x160e: // (green)
5974 element = EL_CHAR_DOWN;
5977 case 0x160f: // (green)
5978 element = EL_CHAR_BUTTON;
5981 case 0x1610: // (green)
5982 element = EL_CHAR_PLUS;
5985 case 0x1611: // (green)
5986 element = EL_CHAR_MINUS;
5989 case 0x1612: // (green)
5990 element = EL_CHAR_APOSTROPHE;
5993 case 0x1613: // (green)
5994 element = EL_CHAR_PARENLEFT;
5997 case 0x1614: // (green)
5998 element = EL_CHAR_PARENRIGHT;
6001 case 0x1615: // (blue steel)
6002 element = EL_STEEL_CHAR_A;
6005 case 0x1616: // (blue steel)
6006 element = EL_STEEL_CHAR_B;
6009 case 0x1617: // (blue steel)
6010 element = EL_STEEL_CHAR_C;
6013 case 0x1618: // (blue steel)
6014 element = EL_STEEL_CHAR_D;
6017 case 0x1619: // (blue steel)
6018 element = EL_STEEL_CHAR_E;
6021 case 0x161a: // (blue steel)
6022 element = EL_STEEL_CHAR_F;
6025 case 0x161b: // (blue steel)
6026 element = EL_STEEL_CHAR_G;
6029 case 0x161c: // (blue steel)
6030 element = EL_STEEL_CHAR_H;
6033 case 0x161d: // (blue steel)
6034 element = EL_STEEL_CHAR_I;
6037 case 0x161e: // (blue steel)
6038 element = EL_STEEL_CHAR_J;
6041 case 0x161f: // (blue steel)
6042 element = EL_STEEL_CHAR_K;
6045 case 0x1620: // (blue steel)
6046 element = EL_STEEL_CHAR_L;
6049 case 0x1621: // (blue steel)
6050 element = EL_STEEL_CHAR_M;
6053 case 0x1622: // (blue steel)
6054 element = EL_STEEL_CHAR_N;
6057 case 0x1623: // (blue steel)
6058 element = EL_STEEL_CHAR_O;
6061 case 0x1624: // (blue steel)
6062 element = EL_STEEL_CHAR_P;
6065 case 0x1625: // (blue steel)
6066 element = EL_STEEL_CHAR_Q;
6069 case 0x1626: // (blue steel)
6070 element = EL_STEEL_CHAR_R;
6073 case 0x1627: // (blue steel)
6074 element = EL_STEEL_CHAR_S;
6077 case 0x1628: // (blue steel)
6078 element = EL_STEEL_CHAR_T;
6081 case 0x1629: // (blue steel)
6082 element = EL_STEEL_CHAR_U;
6085 case 0x162a: // (blue steel)
6086 element = EL_STEEL_CHAR_V;
6089 case 0x162b: // (blue steel)
6090 element = EL_STEEL_CHAR_W;
6093 case 0x162c: // (blue steel)
6094 element = EL_STEEL_CHAR_X;
6097 case 0x162d: // (blue steel)
6098 element = EL_STEEL_CHAR_Y;
6101 case 0x162e: // (blue steel)
6102 element = EL_STEEL_CHAR_Z;
6105 case 0x162f: // (blue steel)
6106 element = EL_STEEL_CHAR_AUMLAUT;
6109 case 0x1630: // (blue steel)
6110 element = EL_STEEL_CHAR_OUMLAUT;
6113 case 0x1631: // (blue steel)
6114 element = EL_STEEL_CHAR_UUMLAUT;
6117 case 0x1632: // (blue steel)
6118 element = EL_STEEL_CHAR_0;
6121 case 0x1633: // (blue steel)
6122 element = EL_STEEL_CHAR_1;
6125 case 0x1634: // (blue steel)
6126 element = EL_STEEL_CHAR_2;
6129 case 0x1635: // (blue steel)
6130 element = EL_STEEL_CHAR_3;
6133 case 0x1636: // (blue steel)
6134 element = EL_STEEL_CHAR_4;
6137 case 0x1637: // (blue steel)
6138 element = EL_STEEL_CHAR_5;
6141 case 0x1638: // (blue steel)
6142 element = EL_STEEL_CHAR_6;
6145 case 0x1639: // (blue steel)
6146 element = EL_STEEL_CHAR_7;
6149 case 0x163a: // (blue steel)
6150 element = EL_STEEL_CHAR_8;
6153 case 0x163b: // (blue steel)
6154 element = EL_STEEL_CHAR_9;
6157 case 0x163c: // (blue steel)
6158 element = EL_STEEL_CHAR_PERIOD;
6161 case 0x163d: // (blue steel)
6162 element = EL_STEEL_CHAR_EXCLAM;
6165 case 0x163e: // (blue steel)
6166 element = EL_STEEL_CHAR_COLON;
6169 case 0x163f: // (blue steel)
6170 element = EL_STEEL_CHAR_LESS;
6173 case 0x1640: // (blue steel)
6174 element = EL_STEEL_CHAR_GREATER;
6177 case 0x1641: // (blue steel)
6178 element = EL_STEEL_CHAR_QUESTION;
6181 case 0x1642: // (blue steel)
6182 element = EL_STEEL_CHAR_COPYRIGHT;
6185 case 0x1643: // (blue steel)
6186 element = EL_STEEL_CHAR_UP;
6189 case 0x1644: // (blue steel)
6190 element = EL_STEEL_CHAR_DOWN;
6193 case 0x1645: // (blue steel)
6194 element = EL_STEEL_CHAR_BUTTON;
6197 case 0x1646: // (blue steel)
6198 element = EL_STEEL_CHAR_PLUS;
6201 case 0x1647: // (blue steel)
6202 element = EL_STEEL_CHAR_MINUS;
6205 case 0x1648: // (blue steel)
6206 element = EL_STEEL_CHAR_APOSTROPHE;
6209 case 0x1649: // (blue steel)
6210 element = EL_STEEL_CHAR_PARENLEFT;
6213 case 0x164a: // (blue steel)
6214 element = EL_STEEL_CHAR_PARENRIGHT;
6217 case 0x164b: // (green steel)
6218 element = EL_STEEL_CHAR_A;
6221 case 0x164c: // (green steel)
6222 element = EL_STEEL_CHAR_B;
6225 case 0x164d: // (green steel)
6226 element = EL_STEEL_CHAR_C;
6229 case 0x164e: // (green steel)
6230 element = EL_STEEL_CHAR_D;
6233 case 0x164f: // (green steel)
6234 element = EL_STEEL_CHAR_E;
6237 case 0x1650: // (green steel)
6238 element = EL_STEEL_CHAR_F;
6241 case 0x1651: // (green steel)
6242 element = EL_STEEL_CHAR_G;
6245 case 0x1652: // (green steel)
6246 element = EL_STEEL_CHAR_H;
6249 case 0x1653: // (green steel)
6250 element = EL_STEEL_CHAR_I;
6253 case 0x1654: // (green steel)
6254 element = EL_STEEL_CHAR_J;
6257 case 0x1655: // (green steel)
6258 element = EL_STEEL_CHAR_K;
6261 case 0x1656: // (green steel)
6262 element = EL_STEEL_CHAR_L;
6265 case 0x1657: // (green steel)
6266 element = EL_STEEL_CHAR_M;
6269 case 0x1658: // (green steel)
6270 element = EL_STEEL_CHAR_N;
6273 case 0x1659: // (green steel)
6274 element = EL_STEEL_CHAR_O;
6277 case 0x165a: // (green steel)
6278 element = EL_STEEL_CHAR_P;
6281 case 0x165b: // (green steel)
6282 element = EL_STEEL_CHAR_Q;
6285 case 0x165c: // (green steel)
6286 element = EL_STEEL_CHAR_R;
6289 case 0x165d: // (green steel)
6290 element = EL_STEEL_CHAR_S;
6293 case 0x165e: // (green steel)
6294 element = EL_STEEL_CHAR_T;
6297 case 0x165f: // (green steel)
6298 element = EL_STEEL_CHAR_U;
6301 case 0x1660: // (green steel)
6302 element = EL_STEEL_CHAR_V;
6305 case 0x1661: // (green steel)
6306 element = EL_STEEL_CHAR_W;
6309 case 0x1662: // (green steel)
6310 element = EL_STEEL_CHAR_X;
6313 case 0x1663: // (green steel)
6314 element = EL_STEEL_CHAR_Y;
6317 case 0x1664: // (green steel)
6318 element = EL_STEEL_CHAR_Z;
6321 case 0x1665: // (green steel)
6322 element = EL_STEEL_CHAR_AUMLAUT;
6325 case 0x1666: // (green steel)
6326 element = EL_STEEL_CHAR_OUMLAUT;
6329 case 0x1667: // (green steel)
6330 element = EL_STEEL_CHAR_UUMLAUT;
6333 case 0x1668: // (green steel)
6334 element = EL_STEEL_CHAR_0;
6337 case 0x1669: // (green steel)
6338 element = EL_STEEL_CHAR_1;
6341 case 0x166a: // (green steel)
6342 element = EL_STEEL_CHAR_2;
6345 case 0x166b: // (green steel)
6346 element = EL_STEEL_CHAR_3;
6349 case 0x166c: // (green steel)
6350 element = EL_STEEL_CHAR_4;
6353 case 0x166d: // (green steel)
6354 element = EL_STEEL_CHAR_5;
6357 case 0x166e: // (green steel)
6358 element = EL_STEEL_CHAR_6;
6361 case 0x166f: // (green steel)
6362 element = EL_STEEL_CHAR_7;
6365 case 0x1670: // (green steel)
6366 element = EL_STEEL_CHAR_8;
6369 case 0x1671: // (green steel)
6370 element = EL_STEEL_CHAR_9;
6373 case 0x1672: // (green steel)
6374 element = EL_STEEL_CHAR_PERIOD;
6377 case 0x1673: // (green steel)
6378 element = EL_STEEL_CHAR_EXCLAM;
6381 case 0x1674: // (green steel)
6382 element = EL_STEEL_CHAR_COLON;
6385 case 0x1675: // (green steel)
6386 element = EL_STEEL_CHAR_LESS;
6389 case 0x1676: // (green steel)
6390 element = EL_STEEL_CHAR_GREATER;
6393 case 0x1677: // (green steel)
6394 element = EL_STEEL_CHAR_QUESTION;
6397 case 0x1678: // (green steel)
6398 element = EL_STEEL_CHAR_COPYRIGHT;
6401 case 0x1679: // (green steel)
6402 element = EL_STEEL_CHAR_UP;
6405 case 0x167a: // (green steel)
6406 element = EL_STEEL_CHAR_DOWN;
6409 case 0x167b: // (green steel)
6410 element = EL_STEEL_CHAR_BUTTON;
6413 case 0x167c: // (green steel)
6414 element = EL_STEEL_CHAR_PLUS;
6417 case 0x167d: // (green steel)
6418 element = EL_STEEL_CHAR_MINUS;
6421 case 0x167e: // (green steel)
6422 element = EL_STEEL_CHAR_APOSTROPHE;
6425 case 0x167f: // (green steel)
6426 element = EL_STEEL_CHAR_PARENLEFT;
6429 case 0x1680: // (green steel)
6430 element = EL_STEEL_CHAR_PARENRIGHT;
6433 case 0x1681: // gate (red)
6434 element = EL_EM_GATE_1;
6437 case 0x1682: // secret gate (red)
6438 element = EL_EM_GATE_1_GRAY;
6441 case 0x1683: // gate (yellow)
6442 element = EL_EM_GATE_2;
6445 case 0x1684: // secret gate (yellow)
6446 element = EL_EM_GATE_2_GRAY;
6449 case 0x1685: // gate (blue)
6450 element = EL_EM_GATE_4;
6453 case 0x1686: // secret gate (blue)
6454 element = EL_EM_GATE_4_GRAY;
6457 case 0x1687: // gate (green)
6458 element = EL_EM_GATE_3;
6461 case 0x1688: // secret gate (green)
6462 element = EL_EM_GATE_3_GRAY;
6465 case 0x1689: // gate (white)
6466 element = EL_DC_GATE_WHITE;
6469 case 0x168a: // secret gate (white)
6470 element = EL_DC_GATE_WHITE_GRAY;
6473 case 0x168b: // secret gate (no key)
6474 element = EL_DC_GATE_FAKE_GRAY;
6478 element = EL_ROBOT_WHEEL;
6482 element = EL_DC_TIMEGATE_SWITCH;
6486 element = EL_ACID_POOL_BOTTOM;
6490 element = EL_ACID_POOL_TOPLEFT;
6494 element = EL_ACID_POOL_TOPRIGHT;
6498 element = EL_ACID_POOL_BOTTOMLEFT;
6502 element = EL_ACID_POOL_BOTTOMRIGHT;
6506 element = EL_STEELWALL;
6510 element = EL_STEELWALL_SLIPPERY;
6513 case 0x1695: // steel wall (not round)
6514 element = EL_STEELWALL;
6517 case 0x1696: // steel wall (left)
6518 element = EL_DC_STEELWALL_1_LEFT;
6521 case 0x1697: // steel wall (bottom)
6522 element = EL_DC_STEELWALL_1_BOTTOM;
6525 case 0x1698: // steel wall (right)
6526 element = EL_DC_STEELWALL_1_RIGHT;
6529 case 0x1699: // steel wall (top)
6530 element = EL_DC_STEELWALL_1_TOP;
6533 case 0x169a: // steel wall (left/bottom)
6534 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6537 case 0x169b: // steel wall (right/bottom)
6538 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6541 case 0x169c: // steel wall (right/top)
6542 element = EL_DC_STEELWALL_1_TOPRIGHT;
6545 case 0x169d: // steel wall (left/top)
6546 element = EL_DC_STEELWALL_1_TOPLEFT;
6549 case 0x169e: // steel wall (right/bottom small)
6550 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6553 case 0x169f: // steel wall (left/bottom small)
6554 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6557 case 0x16a0: // steel wall (right/top small)
6558 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6561 case 0x16a1: // steel wall (left/top small)
6562 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6565 case 0x16a2: // steel wall (left/right)
6566 element = EL_DC_STEELWALL_1_VERTICAL;
6569 case 0x16a3: // steel wall (top/bottom)
6570 element = EL_DC_STEELWALL_1_HORIZONTAL;
6573 case 0x16a4: // steel wall 2 (left end)
6574 element = EL_DC_STEELWALL_2_LEFT;
6577 case 0x16a5: // steel wall 2 (right end)
6578 element = EL_DC_STEELWALL_2_RIGHT;
6581 case 0x16a6: // steel wall 2 (top end)
6582 element = EL_DC_STEELWALL_2_TOP;
6585 case 0x16a7: // steel wall 2 (bottom end)
6586 element = EL_DC_STEELWALL_2_BOTTOM;
6589 case 0x16a8: // steel wall 2 (left/right)
6590 element = EL_DC_STEELWALL_2_HORIZONTAL;
6593 case 0x16a9: // steel wall 2 (up/down)
6594 element = EL_DC_STEELWALL_2_VERTICAL;
6597 case 0x16aa: // steel wall 2 (mid)
6598 element = EL_DC_STEELWALL_2_MIDDLE;
6602 element = EL_SIGN_EXCLAMATION;
6606 element = EL_SIGN_RADIOACTIVITY;
6610 element = EL_SIGN_STOP;
6614 element = EL_SIGN_WHEELCHAIR;
6618 element = EL_SIGN_PARKING;
6622 element = EL_SIGN_NO_ENTRY;
6626 element = EL_SIGN_HEART;
6630 element = EL_SIGN_GIVE_WAY;
6634 element = EL_SIGN_ENTRY_FORBIDDEN;
6638 element = EL_SIGN_EMERGENCY_EXIT;
6642 element = EL_SIGN_YIN_YANG;
6646 element = EL_WALL_EMERALD;
6650 element = EL_WALL_DIAMOND;
6654 element = EL_WALL_PEARL;
6658 element = EL_WALL_CRYSTAL;
6662 element = EL_INVISIBLE_WALL;
6666 element = EL_INVISIBLE_STEELWALL;
6670 // EL_INVISIBLE_SAND
6673 element = EL_LIGHT_SWITCH;
6677 element = EL_ENVELOPE_1;
6681 if (element >= 0x0117 && element <= 0x036e) // (?)
6682 element = EL_DIAMOND;
6683 else if (element >= 0x042d && element <= 0x0684) // (?)
6684 element = EL_EMERALD;
6685 else if (element >= 0x157c && element <= 0x158b)
6687 else if (element >= 0x1590 && element <= 0x159f)
6688 element = EL_DC_LANDMINE;
6689 else if (element >= 0x16bc && element <= 0x16cb)
6690 element = EL_INVISIBLE_SAND;
6693 Warn("unknown Diamond Caves element 0x%04x", element);
6695 element = EL_UNKNOWN;
6700 return getMappedElement(element);
6703 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6705 byte header[DC_LEVEL_HEADER_SIZE];
6707 int envelope_header_pos = 62;
6708 int envelope_content_pos = 94;
6709 int level_name_pos = 251;
6710 int level_author_pos = 292;
6711 int envelope_header_len;
6712 int envelope_content_len;
6714 int level_author_len;
6716 int num_yamyam_contents;
6719 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6721 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6723 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6725 header[i * 2 + 0] = header_word >> 8;
6726 header[i * 2 + 1] = header_word & 0xff;
6729 // read some values from level header to check level decoding integrity
6730 fieldx = header[6] | (header[7] << 8);
6731 fieldy = header[8] | (header[9] << 8);
6732 num_yamyam_contents = header[60] | (header[61] << 8);
6734 // do some simple sanity checks to ensure that level was correctly decoded
6735 if (fieldx < 1 || fieldx > 256 ||
6736 fieldy < 1 || fieldy > 256 ||
6737 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6739 level->no_valid_file = TRUE;
6741 Warn("cannot decode level from stream -- using empty level");
6746 // maximum envelope header size is 31 bytes
6747 envelope_header_len = header[envelope_header_pos];
6748 // maximum envelope content size is 110 (156?) bytes
6749 envelope_content_len = header[envelope_content_pos];
6751 // maximum level title size is 40 bytes
6752 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6753 // maximum level author size is 30 (51?) bytes
6754 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6758 for (i = 0; i < envelope_header_len; i++)
6759 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6760 level->envelope[0].text[envelope_size++] =
6761 header[envelope_header_pos + 1 + i];
6763 if (envelope_header_len > 0 && envelope_content_len > 0)
6765 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6766 level->envelope[0].text[envelope_size++] = '\n';
6767 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6768 level->envelope[0].text[envelope_size++] = '\n';
6771 for (i = 0; i < envelope_content_len; i++)
6772 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6773 level->envelope[0].text[envelope_size++] =
6774 header[envelope_content_pos + 1 + i];
6776 level->envelope[0].text[envelope_size] = '\0';
6778 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6779 level->envelope[0].ysize = 10;
6780 level->envelope[0].autowrap = TRUE;
6781 level->envelope[0].centered = TRUE;
6783 for (i = 0; i < level_name_len; i++)
6784 level->name[i] = header[level_name_pos + 1 + i];
6785 level->name[level_name_len] = '\0';
6787 for (i = 0; i < level_author_len; i++)
6788 level->author[i] = header[level_author_pos + 1 + i];
6789 level->author[level_author_len] = '\0';
6791 num_yamyam_contents = header[60] | (header[61] << 8);
6792 level->num_yamyam_contents =
6793 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6795 for (i = 0; i < num_yamyam_contents; i++)
6797 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6799 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6800 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6802 if (i < MAX_ELEMENT_CONTENTS)
6803 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6807 fieldx = header[6] | (header[7] << 8);
6808 fieldy = header[8] | (header[9] << 8);
6809 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6810 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6812 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6814 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6815 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6817 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6818 level->field[x][y] = getMappedElement_DC(element_dc);
6821 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6822 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6823 level->field[x][y] = EL_PLAYER_1;
6825 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6826 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6827 level->field[x][y] = EL_PLAYER_2;
6829 level->gems_needed = header[18] | (header[19] << 8);
6831 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6832 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6833 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6834 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6835 level->score[SC_NUT] = header[28] | (header[29] << 8);
6836 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6837 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6838 level->score[SC_BUG] = header[34] | (header[35] << 8);
6839 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6840 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6841 level->score[SC_KEY] = header[40] | (header[41] << 8);
6842 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6844 level->time = header[44] | (header[45] << 8);
6846 level->amoeba_speed = header[46] | (header[47] << 8);
6847 level->time_light = header[48] | (header[49] << 8);
6848 level->time_timegate = header[50] | (header[51] << 8);
6849 level->time_wheel = header[52] | (header[53] << 8);
6850 level->time_magic_wall = header[54] | (header[55] << 8);
6851 level->extra_time = header[56] | (header[57] << 8);
6852 level->shield_normal_time = header[58] | (header[59] << 8);
6854 // shield and extra time elements do not have a score
6855 level->score[SC_SHIELD] = 0;
6856 level->extra_time_score = 0;
6858 // set time for normal and deadly shields to the same value
6859 level->shield_deadly_time = level->shield_normal_time;
6861 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6862 // can slip down from flat walls, like normal walls and steel walls
6863 level->em_slippery_gems = TRUE;
6865 // time score is counted for each 10 seconds left in Diamond Caves levels
6866 level->time_score_base = 10;
6869 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6870 struct LevelFileInfo *level_file_info,
6871 boolean level_info_only)
6873 char *filename = level_file_info->filename;
6875 int num_magic_bytes = 8;
6876 char magic_bytes[num_magic_bytes + 1];
6877 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6879 if (!(file = openFile(filename, MODE_READ)))
6881 level->no_valid_file = TRUE;
6883 if (!level_info_only)
6884 Warn("cannot read level '%s' -- using empty level", filename);
6889 // fseek(file, 0x0000, SEEK_SET);
6891 if (level_file_info->packed)
6893 // read "magic bytes" from start of file
6894 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6895 magic_bytes[0] = '\0';
6897 // check "magic bytes" for correct file format
6898 if (!strPrefix(magic_bytes, "DC2"))
6900 level->no_valid_file = TRUE;
6902 Warn("unknown DC level file '%s' -- using empty level", filename);
6907 if (strPrefix(magic_bytes, "DC2Win95") ||
6908 strPrefix(magic_bytes, "DC2Win98"))
6910 int position_first_level = 0x00fa;
6911 int extra_bytes = 4;
6914 // advance file stream to first level inside the level package
6915 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6917 // each block of level data is followed by block of non-level data
6918 num_levels_to_skip *= 2;
6920 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6921 while (num_levels_to_skip >= 0)
6923 // advance file stream to next level inside the level package
6924 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6926 level->no_valid_file = TRUE;
6928 Warn("cannot fseek in file '%s' -- using empty level", filename);
6933 // skip apparently unused extra bytes following each level
6934 ReadUnusedBytesFromFile(file, extra_bytes);
6936 // read size of next level in level package
6937 skip_bytes = getFile32BitLE(file);
6939 num_levels_to_skip--;
6944 level->no_valid_file = TRUE;
6946 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6952 LoadLevelFromFileStream_DC(file, level);
6958 // ----------------------------------------------------------------------------
6959 // functions for loading SB level
6960 // ----------------------------------------------------------------------------
6962 int getMappedElement_SB(int element_ascii, boolean use_ces)
6970 sb_element_mapping[] =
6972 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6973 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6974 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6975 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6976 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6977 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6978 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6979 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6986 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6987 if (element_ascii == sb_element_mapping[i].ascii)
6988 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6990 return EL_UNDEFINED;
6993 static void SetLevelSettings_SB(struct LevelInfo *level)
6997 level->use_step_counter = TRUE;
7000 level->score[SC_TIME_BONUS] = 0;
7001 level->time_score_base = 1;
7002 level->rate_time_over_score = TRUE;
7005 level->auto_exit_sokoban = TRUE;
7008 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7009 struct LevelFileInfo *level_file_info,
7010 boolean level_info_only)
7012 char *filename = level_file_info->filename;
7013 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7014 char last_comment[MAX_LINE_LEN];
7015 char level_name[MAX_LINE_LEN];
7018 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7019 boolean read_continued_line = FALSE;
7020 boolean reading_playfield = FALSE;
7021 boolean got_valid_playfield_line = FALSE;
7022 boolean invalid_playfield_char = FALSE;
7023 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7024 int file_level_nr = 0;
7025 int x = 0, y = 0; // initialized to make compilers happy
7027 last_comment[0] = '\0';
7028 level_name[0] = '\0';
7030 if (!(file = openFile(filename, MODE_READ)))
7032 level->no_valid_file = TRUE;
7034 if (!level_info_only)
7035 Warn("cannot read level '%s' -- using empty level", filename);
7040 while (!checkEndOfFile(file))
7042 // level successfully read, but next level may follow here
7043 if (!got_valid_playfield_line && reading_playfield)
7045 // read playfield from single level file -- skip remaining file
7046 if (!level_file_info->packed)
7049 if (file_level_nr >= num_levels_to_skip)
7054 last_comment[0] = '\0';
7055 level_name[0] = '\0';
7057 reading_playfield = FALSE;
7060 got_valid_playfield_line = FALSE;
7062 // read next line of input file
7063 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7066 // cut trailing line break (this can be newline and/or carriage return)
7067 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7068 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7071 // copy raw input line for later use (mainly debugging output)
7072 strcpy(line_raw, line);
7074 if (read_continued_line)
7076 // append new line to existing line, if there is enough space
7077 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7078 strcat(previous_line, line_ptr);
7080 strcpy(line, previous_line); // copy storage buffer to line
7082 read_continued_line = FALSE;
7085 // if the last character is '\', continue at next line
7086 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7088 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7089 strcpy(previous_line, line); // copy line to storage buffer
7091 read_continued_line = TRUE;
7097 if (line[0] == '\0')
7100 // extract comment text from comment line
7103 for (line_ptr = line; *line_ptr; line_ptr++)
7104 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7107 strcpy(last_comment, line_ptr);
7112 // extract level title text from line containing level title
7113 if (line[0] == '\'')
7115 strcpy(level_name, &line[1]);
7117 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7118 level_name[strlen(level_name) - 1] = '\0';
7123 // skip lines containing only spaces (or empty lines)
7124 for (line_ptr = line; *line_ptr; line_ptr++)
7125 if (*line_ptr != ' ')
7127 if (*line_ptr == '\0')
7130 // at this point, we have found a line containing part of a playfield
7132 got_valid_playfield_line = TRUE;
7134 if (!reading_playfield)
7136 reading_playfield = TRUE;
7137 invalid_playfield_char = FALSE;
7139 for (x = 0; x < MAX_LEV_FIELDX; x++)
7140 for (y = 0; y < MAX_LEV_FIELDY; y++)
7141 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7146 // start with topmost tile row
7150 // skip playfield line if larger row than allowed
7151 if (y >= MAX_LEV_FIELDY)
7154 // start with leftmost tile column
7157 // read playfield elements from line
7158 for (line_ptr = line; *line_ptr; line_ptr++)
7160 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7162 // stop parsing playfield line if larger column than allowed
7163 if (x >= MAX_LEV_FIELDX)
7166 if (mapped_sb_element == EL_UNDEFINED)
7168 invalid_playfield_char = TRUE;
7173 level->field[x][y] = mapped_sb_element;
7175 // continue with next tile column
7178 level->fieldx = MAX(x, level->fieldx);
7181 if (invalid_playfield_char)
7183 // if first playfield line, treat invalid lines as comment lines
7185 reading_playfield = FALSE;
7190 // continue with next tile row
7198 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7199 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7201 if (!reading_playfield)
7203 level->no_valid_file = TRUE;
7205 Warn("cannot read level '%s' -- using empty level", filename);
7210 if (*level_name != '\0')
7212 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7213 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7215 else if (*last_comment != '\0')
7217 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7218 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7222 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7225 // set all empty fields beyond the border walls to invisible steel wall
7226 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7228 if ((x == 0 || x == level->fieldx - 1 ||
7229 y == 0 || y == level->fieldy - 1) &&
7230 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7231 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7232 level->field, level->fieldx, level->fieldy);
7235 // set special level settings for Sokoban levels
7236 SetLevelSettings_SB(level);
7238 if (load_xsb_to_ces)
7240 // special global settings can now be set in level template
7241 level->use_custom_template = TRUE;
7246 // -------------------------------------------------------------------------
7247 // functions for handling native levels
7248 // -------------------------------------------------------------------------
7250 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7251 struct LevelFileInfo *level_file_info,
7252 boolean level_info_only)
7256 // determine position of requested level inside level package
7257 if (level_file_info->packed)
7258 pos = level_file_info->nr - leveldir_current->first_level;
7260 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7261 level->no_valid_file = TRUE;
7264 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7265 struct LevelFileInfo *level_file_info,
7266 boolean level_info_only)
7268 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7269 level->no_valid_file = TRUE;
7272 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7273 struct LevelFileInfo *level_file_info,
7274 boolean level_info_only)
7278 // determine position of requested level inside level package
7279 if (level_file_info->packed)
7280 pos = level_file_info->nr - leveldir_current->first_level;
7282 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7283 level->no_valid_file = TRUE;
7286 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7287 struct LevelFileInfo *level_file_info,
7288 boolean level_info_only)
7290 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7291 level->no_valid_file = TRUE;
7294 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7296 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7297 CopyNativeLevel_RND_to_BD(level);
7298 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7299 CopyNativeLevel_RND_to_EM(level);
7300 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7301 CopyNativeLevel_RND_to_SP(level);
7302 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7303 CopyNativeLevel_RND_to_MM(level);
7306 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7308 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7309 CopyNativeLevel_BD_to_RND(level);
7310 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7311 CopyNativeLevel_EM_to_RND(level);
7312 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7313 CopyNativeLevel_SP_to_RND(level);
7314 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7315 CopyNativeLevel_MM_to_RND(level);
7318 void SaveNativeLevel(struct LevelInfo *level)
7320 // saving native level files only supported for some game engines
7321 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7322 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7325 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7326 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7327 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7328 char *filename = getLevelFilenameFromBasename(basename);
7330 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7333 boolean success = FALSE;
7335 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7337 CopyNativeLevel_RND_to_BD(level);
7338 // CopyNativeTape_RND_to_BD(level);
7340 success = SaveNativeLevel_BD(filename);
7342 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7344 CopyNativeLevel_RND_to_SP(level);
7345 CopyNativeTape_RND_to_SP(level);
7347 success = SaveNativeLevel_SP(filename);
7351 Request("Native level file saved!", REQ_CONFIRM);
7353 Request("Failed to save native level file!", REQ_CONFIRM);
7357 // ----------------------------------------------------------------------------
7358 // functions for loading generic level
7359 // ----------------------------------------------------------------------------
7361 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7362 struct LevelFileInfo *level_file_info,
7363 boolean level_info_only)
7365 // always start with reliable default values
7366 setLevelInfoToDefaults(level, level_info_only, TRUE);
7368 switch (level_file_info->type)
7370 case LEVEL_FILE_TYPE_RND:
7371 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7374 case LEVEL_FILE_TYPE_BD:
7375 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7376 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7379 case LEVEL_FILE_TYPE_EM:
7380 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7381 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7384 case LEVEL_FILE_TYPE_SP:
7385 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7386 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7389 case LEVEL_FILE_TYPE_MM:
7390 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7391 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7394 case LEVEL_FILE_TYPE_DC:
7395 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7398 case LEVEL_FILE_TYPE_SB:
7399 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7403 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7407 // if level file is invalid, restore level structure to default values
7408 if (level->no_valid_file)
7409 setLevelInfoToDefaults(level, level_info_only, FALSE);
7411 if (check_special_flags("use_native_bd_game_engine"))
7412 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7414 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7415 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7417 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7418 CopyNativeLevel_Native_to_RND(level);
7421 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7423 static struct LevelFileInfo level_file_info;
7425 // always start with reliable default values
7426 setFileInfoToDefaults(&level_file_info);
7428 level_file_info.nr = 0; // unknown level number
7429 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7431 setString(&level_file_info.filename, filename);
7433 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7436 static void LoadLevel_InitVersion(struct LevelInfo *level)
7440 if (leveldir_current == NULL) // only when dumping level
7443 // all engine modifications also valid for levels which use latest engine
7444 if (level->game_version < VERSION_IDENT(3,2,0,5))
7446 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7447 level->time_score_base = 10;
7450 if (leveldir_current->latest_engine)
7452 // ---------- use latest game engine --------------------------------------
7454 /* For all levels which are forced to use the latest game engine version
7455 (normally all but user contributed, private and undefined levels), set
7456 the game engine version to the actual version; this allows for actual
7457 corrections in the game engine to take effect for existing, converted
7458 levels (from "classic" or other existing games) to make the emulation
7459 of the corresponding game more accurate, while (hopefully) not breaking
7460 existing levels created from other players. */
7462 level->game_version = GAME_VERSION_ACTUAL;
7464 /* Set special EM style gems behaviour: EM style gems slip down from
7465 normal, steel and growing wall. As this is a more fundamental change,
7466 it seems better to set the default behaviour to "off" (as it is more
7467 natural) and make it configurable in the level editor (as a property
7468 of gem style elements). Already existing converted levels (neither
7469 private nor contributed levels) are changed to the new behaviour. */
7471 if (level->file_version < FILE_VERSION_2_0)
7472 level->em_slippery_gems = TRUE;
7477 // ---------- use game engine the level was created with --------------------
7479 /* For all levels which are not forced to use the latest game engine
7480 version (normally user contributed, private and undefined levels),
7481 use the version of the game engine the levels were created for.
7483 Since 2.0.1, the game engine version is now directly stored
7484 in the level file (chunk "VERS"), so there is no need anymore
7485 to set the game version from the file version (except for old,
7486 pre-2.0 levels, where the game version is still taken from the
7487 file format version used to store the level -- see above). */
7489 // player was faster than enemies in 1.0.0 and before
7490 if (level->file_version == FILE_VERSION_1_0)
7491 for (i = 0; i < MAX_PLAYERS; i++)
7492 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7494 // default behaviour for EM style gems was "slippery" only in 2.0.1
7495 if (level->game_version == VERSION_IDENT(2,0,1,0))
7496 level->em_slippery_gems = TRUE;
7498 // springs could be pushed over pits before (pre-release version) 2.2.0
7499 if (level->game_version < VERSION_IDENT(2,2,0,0))
7500 level->use_spring_bug = TRUE;
7502 if (level->game_version < VERSION_IDENT(3,2,0,5))
7504 // time orb caused limited time in endless time levels before 3.2.0-5
7505 level->use_time_orb_bug = TRUE;
7507 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7508 level->block_snap_field = FALSE;
7510 // extra time score was same value as time left score before 3.2.0-5
7511 level->extra_time_score = level->score[SC_TIME_BONUS];
7514 if (level->game_version < VERSION_IDENT(3,2,0,7))
7516 // default behaviour for snapping was "not continuous" before 3.2.0-7
7517 level->continuous_snapping = FALSE;
7520 // only few elements were able to actively move into acid before 3.1.0
7521 // trigger settings did not exist before 3.1.0; set to default "any"
7522 if (level->game_version < VERSION_IDENT(3,1,0,0))
7524 // correct "can move into acid" settings (all zero in old levels)
7526 level->can_move_into_acid_bits = 0; // nothing can move into acid
7527 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7529 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7530 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7531 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7532 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7534 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7535 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7537 // correct trigger settings (stored as zero == "none" in old levels)
7539 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7541 int element = EL_CUSTOM_START + i;
7542 struct ElementInfo *ei = &element_info[element];
7544 for (j = 0; j < ei->num_change_pages; j++)
7546 struct ElementChangeInfo *change = &ei->change_page[j];
7548 change->trigger_player = CH_PLAYER_ANY;
7549 change->trigger_page = CH_PAGE_ANY;
7554 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7556 int element = EL_CUSTOM_256;
7557 struct ElementInfo *ei = &element_info[element];
7558 struct ElementChangeInfo *change = &ei->change_page[0];
7560 /* This is needed to fix a problem that was caused by a bugfix in function
7561 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7562 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7563 not replace walkable elements, but instead just placed the player on it,
7564 without placing the Sokoban field under the player). Unfortunately, this
7565 breaks "Snake Bite" style levels when the snake is halfway through a door
7566 that just closes (the snake head is still alive and can be moved in this
7567 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7568 player (without Sokoban element) which then gets killed as designed). */
7570 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7571 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7572 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7573 change->target_element = EL_PLAYER_1;
7576 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7577 if (level->game_version < VERSION_IDENT(3,2,5,0))
7579 /* This is needed to fix a problem that was caused by a bugfix in function
7580 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7581 corrects the behaviour when a custom element changes to another custom
7582 element with a higher element number that has change actions defined.
7583 Normally, only one change per frame is allowed for custom elements.
7584 Therefore, it is checked if a custom element already changed in the
7585 current frame; if it did, subsequent changes are suppressed.
7586 Unfortunately, this is only checked for element changes, but not for
7587 change actions, which are still executed. As the function above loops
7588 through all custom elements from lower to higher, an element change
7589 resulting in a lower CE number won't be checked again, while a target
7590 element with a higher number will also be checked, and potential change
7591 actions will get executed for this CE, too (which is wrong), while
7592 further changes are ignored (which is correct). As this bugfix breaks
7593 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7594 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7595 behaviour for existing levels and tapes that make use of this bug */
7597 level->use_action_after_change_bug = TRUE;
7600 // not centering level after relocating player was default only in 3.2.3
7601 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7602 level->shifted_relocation = TRUE;
7604 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7605 if (level->game_version < VERSION_IDENT(3,2,6,0))
7606 level->em_explodes_by_fire = TRUE;
7608 // levels were solved by the first player entering an exit up to 4.1.0.0
7609 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7610 level->solved_by_one_player = TRUE;
7612 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7613 if (level->game_version < VERSION_IDENT(4,1,1,1))
7614 level->use_life_bugs = TRUE;
7616 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7617 if (level->game_version < VERSION_IDENT(4,1,1,1))
7618 level->sb_objects_needed = FALSE;
7620 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7621 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7622 level->finish_dig_collect = FALSE;
7624 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7625 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7626 level->keep_walkable_ce = TRUE;
7629 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7631 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7634 // check if this level is (not) a Sokoban level
7635 for (y = 0; y < level->fieldy; y++)
7636 for (x = 0; x < level->fieldx; x++)
7637 if (!IS_SB_ELEMENT(Tile[x][y]))
7638 is_sokoban_level = FALSE;
7640 if (is_sokoban_level)
7642 // set special level settings for Sokoban levels
7643 SetLevelSettings_SB(level);
7647 static void LoadLevel_InitSettings(struct LevelInfo *level)
7649 // adjust level settings for (non-native) Sokoban-style levels
7650 LoadLevel_InitSettings_SB(level);
7652 // rename levels with title "nameless level" or if renaming is forced
7653 if (leveldir_current->empty_level_name != NULL &&
7654 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7655 leveldir_current->force_level_name))
7656 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7657 leveldir_current->empty_level_name, level_nr);
7660 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7664 // map elements that have changed in newer versions
7665 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7666 level->game_version);
7667 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7668 for (x = 0; x < 3; x++)
7669 for (y = 0; y < 3; y++)
7670 level->yamyam_content[i].e[x][y] =
7671 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7672 level->game_version);
7676 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7680 // map custom element change events that have changed in newer versions
7681 // (these following values were accidentally changed in version 3.0.1)
7682 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7683 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7685 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7687 int element = EL_CUSTOM_START + i;
7689 // order of checking and copying events to be mapped is important
7690 // (do not change the start and end value -- they are constant)
7691 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7693 if (HAS_CHANGE_EVENT(element, j - 2))
7695 SET_CHANGE_EVENT(element, j - 2, FALSE);
7696 SET_CHANGE_EVENT(element, j, TRUE);
7700 // order of checking and copying events to be mapped is important
7701 // (do not change the start and end value -- they are constant)
7702 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7704 if (HAS_CHANGE_EVENT(element, j - 1))
7706 SET_CHANGE_EVENT(element, j - 1, FALSE);
7707 SET_CHANGE_EVENT(element, j, TRUE);
7713 // initialize "can_change" field for old levels with only one change page
7714 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7716 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7718 int element = EL_CUSTOM_START + i;
7720 if (CAN_CHANGE(element))
7721 element_info[element].change->can_change = TRUE;
7725 // correct custom element values (for old levels without these options)
7726 if (level->game_version < VERSION_IDENT(3,1,1,0))
7728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7730 int element = EL_CUSTOM_START + i;
7731 struct ElementInfo *ei = &element_info[element];
7733 if (ei->access_direction == MV_NO_DIRECTION)
7734 ei->access_direction = MV_ALL_DIRECTIONS;
7738 // correct custom element values (fix invalid values for all versions)
7741 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7743 int element = EL_CUSTOM_START + i;
7744 struct ElementInfo *ei = &element_info[element];
7746 for (j = 0; j < ei->num_change_pages; j++)
7748 struct ElementChangeInfo *change = &ei->change_page[j];
7750 if (change->trigger_player == CH_PLAYER_NONE)
7751 change->trigger_player = CH_PLAYER_ANY;
7753 if (change->trigger_side == CH_SIDE_NONE)
7754 change->trigger_side = CH_SIDE_ANY;
7759 // initialize "can_explode" field for old levels which did not store this
7760 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7761 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7763 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7765 int element = EL_CUSTOM_START + i;
7767 if (EXPLODES_1X1_OLD(element))
7768 element_info[element].explosion_type = EXPLODES_1X1;
7770 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7771 EXPLODES_SMASHED(element) ||
7772 EXPLODES_IMPACT(element)));
7776 // correct previously hard-coded move delay values for maze runner style
7777 if (level->game_version < VERSION_IDENT(3,1,1,0))
7779 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7781 int element = EL_CUSTOM_START + i;
7783 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7785 // previously hard-coded and therefore ignored
7786 element_info[element].move_delay_fixed = 9;
7787 element_info[element].move_delay_random = 0;
7792 // set some other uninitialized values of custom elements in older levels
7793 if (level->game_version < VERSION_IDENT(3,1,0,0))
7795 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7797 int element = EL_CUSTOM_START + i;
7799 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7801 element_info[element].explosion_delay = 17;
7802 element_info[element].ignition_delay = 8;
7806 // set mouse click change events to work for left/middle/right mouse button
7807 if (level->game_version < VERSION_IDENT(4,2,3,0))
7809 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7811 int element = EL_CUSTOM_START + i;
7812 struct ElementInfo *ei = &element_info[element];
7814 for (j = 0; j < ei->num_change_pages; j++)
7816 struct ElementChangeInfo *change = &ei->change_page[j];
7818 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7819 change->has_event[CE_PRESSED_BY_MOUSE] ||
7820 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7821 change->has_event[CE_MOUSE_PRESSED_ON_X])
7822 change->trigger_side = CH_SIDE_ANY;
7828 static void LoadLevel_InitElements(struct LevelInfo *level)
7830 LoadLevel_InitStandardElements(level);
7832 if (level->file_has_custom_elements)
7833 LoadLevel_InitCustomElements(level);
7835 // initialize element properties for level editor etc.
7836 InitElementPropertiesEngine(level->game_version);
7837 InitElementPropertiesGfxElement();
7840 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7844 // map elements that have changed in newer versions
7845 for (y = 0; y < level->fieldy; y++)
7846 for (x = 0; x < level->fieldx; x++)
7847 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7848 level->game_version);
7850 // clear unused playfield data (nicer if level gets resized in editor)
7851 for (x = 0; x < MAX_LEV_FIELDX; x++)
7852 for (y = 0; y < MAX_LEV_FIELDY; y++)
7853 if (x >= level->fieldx || y >= level->fieldy)
7854 level->field[x][y] = EL_EMPTY;
7856 // copy elements to runtime playfield array
7857 for (x = 0; x < MAX_LEV_FIELDX; x++)
7858 for (y = 0; y < MAX_LEV_FIELDY; y++)
7859 Tile[x][y] = level->field[x][y];
7861 // initialize level size variables for faster access
7862 lev_fieldx = level->fieldx;
7863 lev_fieldy = level->fieldy;
7865 // determine border element for this level
7866 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7867 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7872 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7874 struct LevelFileInfo *level_file_info = &level->file_info;
7876 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7877 CopyNativeLevel_RND_to_Native(level);
7880 static void LoadLevelTemplate_LoadAndInit(void)
7882 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7884 LoadLevel_InitVersion(&level_template);
7885 LoadLevel_InitElements(&level_template);
7886 LoadLevel_InitSettings(&level_template);
7888 ActivateLevelTemplate();
7891 void LoadLevelTemplate(int nr)
7893 if (!fileExists(getGlobalLevelTemplateFilename()))
7895 Warn("no level template found for this level");
7900 setLevelFileInfo(&level_template.file_info, nr);
7902 LoadLevelTemplate_LoadAndInit();
7905 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7907 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7909 LoadLevelTemplate_LoadAndInit();
7912 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7914 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7916 if (level.use_custom_template)
7918 if (network_level != NULL)
7919 LoadNetworkLevelTemplate(network_level);
7921 LoadLevelTemplate(-1);
7924 LoadLevel_InitVersion(&level);
7925 LoadLevel_InitElements(&level);
7926 LoadLevel_InitPlayfield(&level);
7927 LoadLevel_InitSettings(&level);
7929 LoadLevel_InitNativeEngines(&level);
7932 void LoadLevel(int nr)
7934 SetLevelSetInfo(leveldir_current->identifier, nr);
7936 setLevelFileInfo(&level.file_info, nr);
7938 LoadLevel_LoadAndInit(NULL);
7941 void LoadLevelInfoOnly(int nr)
7943 setLevelFileInfo(&level.file_info, nr);
7945 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7948 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7950 SetLevelSetInfo(network_level->leveldir_identifier,
7951 network_level->file_info.nr);
7953 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7955 LoadLevel_LoadAndInit(network_level);
7958 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7962 chunk_size += putFileVersion(file, level->file_version);
7963 chunk_size += putFileVersion(file, level->game_version);
7968 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7972 chunk_size += putFile16BitBE(file, level->creation_date.year);
7973 chunk_size += putFile8Bit(file, level->creation_date.month);
7974 chunk_size += putFile8Bit(file, level->creation_date.day);
7979 #if ENABLE_HISTORIC_CHUNKS
7980 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7984 putFile8Bit(file, level->fieldx);
7985 putFile8Bit(file, level->fieldy);
7987 putFile16BitBE(file, level->time);
7988 putFile16BitBE(file, level->gems_needed);
7990 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7991 putFile8Bit(file, level->name[i]);
7993 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7994 putFile8Bit(file, level->score[i]);
7996 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7997 for (y = 0; y < 3; y++)
7998 for (x = 0; x < 3; x++)
7999 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8000 level->yamyam_content[i].e[x][y]));
8001 putFile8Bit(file, level->amoeba_speed);
8002 putFile8Bit(file, level->time_magic_wall);
8003 putFile8Bit(file, level->time_wheel);
8004 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8005 level->amoeba_content));
8006 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8007 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8008 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8009 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8011 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8013 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8014 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8015 putFile32BitBE(file, level->can_move_into_acid_bits);
8016 putFile8Bit(file, level->dont_collide_with_bits);
8018 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8019 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8021 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8022 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8023 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8025 putFile8Bit(file, level->game_engine_type);
8027 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8031 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8036 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8037 chunk_size += putFile8Bit(file, level->name[i]);
8042 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8047 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8048 chunk_size += putFile8Bit(file, level->author[i]);
8053 #if ENABLE_HISTORIC_CHUNKS
8054 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8059 for (y = 0; y < level->fieldy; y++)
8060 for (x = 0; x < level->fieldx; x++)
8061 if (level->encoding_16bit_field)
8062 chunk_size += putFile16BitBE(file, level->field[x][y]);
8064 chunk_size += putFile8Bit(file, level->field[x][y]);
8070 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8075 for (y = 0; y < level->fieldy; y++)
8076 for (x = 0; x < level->fieldx; x++)
8077 chunk_size += putFile16BitBE(file, level->field[x][y]);
8082 #if ENABLE_HISTORIC_CHUNKS
8083 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8087 putFile8Bit(file, EL_YAMYAM);
8088 putFile8Bit(file, level->num_yamyam_contents);
8089 putFile8Bit(file, 0);
8090 putFile8Bit(file, 0);
8092 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8093 for (y = 0; y < 3; y++)
8094 for (x = 0; x < 3; x++)
8095 if (level->encoding_16bit_field)
8096 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8098 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8102 #if ENABLE_HISTORIC_CHUNKS
8103 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8106 int num_contents, content_xsize, content_ysize;
8107 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8109 if (element == EL_YAMYAM)
8111 num_contents = level->num_yamyam_contents;
8115 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8116 for (y = 0; y < 3; y++)
8117 for (x = 0; x < 3; x++)
8118 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8120 else if (element == EL_BD_AMOEBA)
8126 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8127 for (y = 0; y < 3; y++)
8128 for (x = 0; x < 3; x++)
8129 content_array[i][x][y] = EL_EMPTY;
8130 content_array[0][0][0] = level->amoeba_content;
8134 // chunk header already written -- write empty chunk data
8135 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8137 Warn("cannot save content for element '%d'", element);
8142 putFile16BitBE(file, element);
8143 putFile8Bit(file, num_contents);
8144 putFile8Bit(file, content_xsize);
8145 putFile8Bit(file, content_ysize);
8147 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8149 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8150 for (y = 0; y < 3; y++)
8151 for (x = 0; x < 3; x++)
8152 putFile16BitBE(file, content_array[i][x][y]);
8156 #if ENABLE_HISTORIC_CHUNKS
8157 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8159 int envelope_nr = element - EL_ENVELOPE_1;
8160 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8164 chunk_size += putFile16BitBE(file, element);
8165 chunk_size += putFile16BitBE(file, envelope_len);
8166 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8167 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8169 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8170 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8172 for (i = 0; i < envelope_len; i++)
8173 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8179 #if ENABLE_HISTORIC_CHUNKS
8180 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8181 int num_changed_custom_elements)
8185 putFile16BitBE(file, num_changed_custom_elements);
8187 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8189 int element = EL_CUSTOM_START + i;
8191 struct ElementInfo *ei = &element_info[element];
8193 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8195 if (check < num_changed_custom_elements)
8197 putFile16BitBE(file, element);
8198 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8205 if (check != num_changed_custom_elements) // should not happen
8206 Warn("inconsistent number of custom element properties");
8210 #if ENABLE_HISTORIC_CHUNKS
8211 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8212 int num_changed_custom_elements)
8216 putFile16BitBE(file, num_changed_custom_elements);
8218 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8220 int element = EL_CUSTOM_START + i;
8222 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8224 if (check < num_changed_custom_elements)
8226 putFile16BitBE(file, element);
8227 putFile16BitBE(file, element_info[element].change->target_element);
8234 if (check != num_changed_custom_elements) // should not happen
8235 Warn("inconsistent number of custom target elements");
8239 #if ENABLE_HISTORIC_CHUNKS
8240 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8241 int num_changed_custom_elements)
8243 int i, j, x, y, check = 0;
8245 putFile16BitBE(file, num_changed_custom_elements);
8247 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8249 int element = EL_CUSTOM_START + i;
8250 struct ElementInfo *ei = &element_info[element];
8252 if (ei->modified_settings)
8254 if (check < num_changed_custom_elements)
8256 putFile16BitBE(file, element);
8258 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8259 putFile8Bit(file, ei->description[j]);
8261 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8263 // some free bytes for future properties and padding
8264 WriteUnusedBytesToFile(file, 7);
8266 putFile8Bit(file, ei->use_gfx_element);
8267 putFile16BitBE(file, ei->gfx_element_initial);
8269 putFile8Bit(file, ei->collect_score_initial);
8270 putFile8Bit(file, ei->collect_count_initial);
8272 putFile16BitBE(file, ei->push_delay_fixed);
8273 putFile16BitBE(file, ei->push_delay_random);
8274 putFile16BitBE(file, ei->move_delay_fixed);
8275 putFile16BitBE(file, ei->move_delay_random);
8277 putFile16BitBE(file, ei->move_pattern);
8278 putFile8Bit(file, ei->move_direction_initial);
8279 putFile8Bit(file, ei->move_stepsize);
8281 for (y = 0; y < 3; y++)
8282 for (x = 0; x < 3; x++)
8283 putFile16BitBE(file, ei->content.e[x][y]);
8285 putFile32BitBE(file, ei->change->events);
8287 putFile16BitBE(file, ei->change->target_element);
8289 putFile16BitBE(file, ei->change->delay_fixed);
8290 putFile16BitBE(file, ei->change->delay_random);
8291 putFile16BitBE(file, ei->change->delay_frames);
8293 putFile16BitBE(file, ei->change->initial_trigger_element);
8295 putFile8Bit(file, ei->change->explode);
8296 putFile8Bit(file, ei->change->use_target_content);
8297 putFile8Bit(file, ei->change->only_if_complete);
8298 putFile8Bit(file, ei->change->use_random_replace);
8300 putFile8Bit(file, ei->change->random_percentage);
8301 putFile8Bit(file, ei->change->replace_when);
8303 for (y = 0; y < 3; y++)
8304 for (x = 0; x < 3; x++)
8305 putFile16BitBE(file, ei->change->content.e[x][y]);
8307 putFile8Bit(file, ei->slippery_type);
8309 // some free bytes for future properties and padding
8310 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8317 if (check != num_changed_custom_elements) // should not happen
8318 Warn("inconsistent number of custom element properties");
8322 #if ENABLE_HISTORIC_CHUNKS
8323 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8325 struct ElementInfo *ei = &element_info[element];
8328 // ---------- custom element base property values (96 bytes) ----------------
8330 putFile16BitBE(file, element);
8332 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8333 putFile8Bit(file, ei->description[i]);
8335 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8337 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8339 putFile8Bit(file, ei->num_change_pages);
8341 putFile16BitBE(file, ei->ce_value_fixed_initial);
8342 putFile16BitBE(file, ei->ce_value_random_initial);
8343 putFile8Bit(file, ei->use_last_ce_value);
8345 putFile8Bit(file, ei->use_gfx_element);
8346 putFile16BitBE(file, ei->gfx_element_initial);
8348 putFile8Bit(file, ei->collect_score_initial);
8349 putFile8Bit(file, ei->collect_count_initial);
8351 putFile8Bit(file, ei->drop_delay_fixed);
8352 putFile8Bit(file, ei->push_delay_fixed);
8353 putFile8Bit(file, ei->drop_delay_random);
8354 putFile8Bit(file, ei->push_delay_random);
8355 putFile16BitBE(file, ei->move_delay_fixed);
8356 putFile16BitBE(file, ei->move_delay_random);
8358 // bits 0 - 15 of "move_pattern" ...
8359 putFile16BitBE(file, ei->move_pattern & 0xffff);
8360 putFile8Bit(file, ei->move_direction_initial);
8361 putFile8Bit(file, ei->move_stepsize);
8363 putFile8Bit(file, ei->slippery_type);
8365 for (y = 0; y < 3; y++)
8366 for (x = 0; x < 3; x++)
8367 putFile16BitBE(file, ei->content.e[x][y]);
8369 putFile16BitBE(file, ei->move_enter_element);
8370 putFile16BitBE(file, ei->move_leave_element);
8371 putFile8Bit(file, ei->move_leave_type);
8373 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8374 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8376 putFile8Bit(file, ei->access_direction);
8378 putFile8Bit(file, ei->explosion_delay);
8379 putFile8Bit(file, ei->ignition_delay);
8380 putFile8Bit(file, ei->explosion_type);
8382 // some free bytes for future custom property values and padding
8383 WriteUnusedBytesToFile(file, 1);
8385 // ---------- change page property values (48 bytes) ------------------------
8387 for (i = 0; i < ei->num_change_pages; i++)
8389 struct ElementChangeInfo *change = &ei->change_page[i];
8390 unsigned int event_bits;
8392 // bits 0 - 31 of "has_event[]" ...
8394 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8395 if (change->has_event[j])
8396 event_bits |= (1u << j);
8397 putFile32BitBE(file, event_bits);
8399 putFile16BitBE(file, change->target_element);
8401 putFile16BitBE(file, change->delay_fixed);
8402 putFile16BitBE(file, change->delay_random);
8403 putFile16BitBE(file, change->delay_frames);
8405 putFile16BitBE(file, change->initial_trigger_element);
8407 putFile8Bit(file, change->explode);
8408 putFile8Bit(file, change->use_target_content);
8409 putFile8Bit(file, change->only_if_complete);
8410 putFile8Bit(file, change->use_random_replace);
8412 putFile8Bit(file, change->random_percentage);
8413 putFile8Bit(file, change->replace_when);
8415 for (y = 0; y < 3; y++)
8416 for (x = 0; x < 3; x++)
8417 putFile16BitBE(file, change->target_content.e[x][y]);
8419 putFile8Bit(file, change->can_change);
8421 putFile8Bit(file, change->trigger_side);
8423 putFile8Bit(file, change->trigger_player);
8424 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8425 log_2(change->trigger_page)));
8427 putFile8Bit(file, change->has_action);
8428 putFile8Bit(file, change->action_type);
8429 putFile8Bit(file, change->action_mode);
8430 putFile16BitBE(file, change->action_arg);
8432 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8434 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8435 if (change->has_event[j])
8436 event_bits |= (1u << (j - 32));
8437 putFile8Bit(file, event_bits);
8442 #if ENABLE_HISTORIC_CHUNKS
8443 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8445 struct ElementInfo *ei = &element_info[element];
8446 struct ElementGroupInfo *group = ei->group;
8449 putFile16BitBE(file, element);
8451 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8452 putFile8Bit(file, ei->description[i]);
8454 putFile8Bit(file, group->num_elements);
8456 putFile8Bit(file, ei->use_gfx_element);
8457 putFile16BitBE(file, ei->gfx_element_initial);
8459 putFile8Bit(file, group->choice_mode);
8461 // some free bytes for future values and padding
8462 WriteUnusedBytesToFile(file, 3);
8464 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8465 putFile16BitBE(file, group->element[i]);
8469 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8470 boolean write_element)
8472 int save_type = entry->save_type;
8473 int data_type = entry->data_type;
8474 int conf_type = entry->conf_type;
8475 int byte_mask = conf_type & CONF_MASK_BYTES;
8476 int element = entry->element;
8477 int default_value = entry->default_value;
8479 boolean modified = FALSE;
8481 if (byte_mask != CONF_MASK_MULTI_BYTES)
8483 void *value_ptr = entry->value;
8484 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8487 // check if any settings have been modified before saving them
8488 if (value != default_value)
8491 // do not save if explicitly told or if unmodified default settings
8492 if ((save_type == SAVE_CONF_NEVER) ||
8493 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8497 num_bytes += putFile16BitBE(file, element);
8499 num_bytes += putFile8Bit(file, conf_type);
8500 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8501 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8502 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8505 else if (data_type == TYPE_STRING)
8507 char *default_string = entry->default_string;
8508 char *string = (char *)(entry->value);
8509 int string_length = strlen(string);
8512 // check if any settings have been modified before saving them
8513 if (!strEqual(string, default_string))
8516 // do not save if explicitly told or if unmodified default settings
8517 if ((save_type == SAVE_CONF_NEVER) ||
8518 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8522 num_bytes += putFile16BitBE(file, element);
8524 num_bytes += putFile8Bit(file, conf_type);
8525 num_bytes += putFile16BitBE(file, string_length);
8527 for (i = 0; i < string_length; i++)
8528 num_bytes += putFile8Bit(file, string[i]);
8530 else if (data_type == TYPE_ELEMENT_LIST)
8532 int *element_array = (int *)(entry->value);
8533 int num_elements = *(int *)(entry->num_entities);
8536 // check if any settings have been modified before saving them
8537 for (i = 0; i < num_elements; i++)
8538 if (element_array[i] != default_value)
8541 // do not save if explicitly told or if unmodified default settings
8542 if ((save_type == SAVE_CONF_NEVER) ||
8543 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8547 num_bytes += putFile16BitBE(file, element);
8549 num_bytes += putFile8Bit(file, conf_type);
8550 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8552 for (i = 0; i < num_elements; i++)
8553 num_bytes += putFile16BitBE(file, element_array[i]);
8555 else if (data_type == TYPE_CONTENT_LIST)
8557 struct Content *content = (struct Content *)(entry->value);
8558 int num_contents = *(int *)(entry->num_entities);
8561 // check if any settings have been modified before saving them
8562 for (i = 0; i < num_contents; i++)
8563 for (y = 0; y < 3; y++)
8564 for (x = 0; x < 3; x++)
8565 if (content[i].e[x][y] != default_value)
8568 // do not save if explicitly told or if unmodified default settings
8569 if ((save_type == SAVE_CONF_NEVER) ||
8570 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8574 num_bytes += putFile16BitBE(file, element);
8576 num_bytes += putFile8Bit(file, conf_type);
8577 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8579 for (i = 0; i < num_contents; i++)
8580 for (y = 0; y < 3; y++)
8581 for (x = 0; x < 3; x++)
8582 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8588 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8593 li = *level; // copy level data into temporary buffer
8595 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8596 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8601 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8606 li = *level; // copy level data into temporary buffer
8608 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8609 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8614 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8616 int envelope_nr = element - EL_ENVELOPE_1;
8620 chunk_size += putFile16BitBE(file, element);
8622 // copy envelope data into temporary buffer
8623 xx_envelope = level->envelope[envelope_nr];
8625 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8626 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8631 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8633 struct ElementInfo *ei = &element_info[element];
8637 chunk_size += putFile16BitBE(file, element);
8639 xx_ei = *ei; // copy element data into temporary buffer
8641 // set default description string for this specific element
8642 strcpy(xx_default_description, getDefaultElementDescription(ei));
8644 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8645 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8647 for (i = 0; i < ei->num_change_pages; i++)
8649 struct ElementChangeInfo *change = &ei->change_page[i];
8651 xx_current_change_page = i;
8653 xx_change = *change; // copy change data into temporary buffer
8656 setEventBitsFromEventFlags(change);
8658 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8659 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8666 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8668 struct ElementInfo *ei = &element_info[element];
8669 struct ElementGroupInfo *group = ei->group;
8673 chunk_size += putFile16BitBE(file, element);
8675 xx_ei = *ei; // copy element data into temporary buffer
8676 xx_group = *group; // copy group data into temporary buffer
8678 // set default description string for this specific element
8679 strcpy(xx_default_description, getDefaultElementDescription(ei));
8681 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8682 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8687 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8689 struct ElementInfo *ei = &element_info[element];
8693 chunk_size += putFile16BitBE(file, element);
8695 xx_ei = *ei; // copy element data into temporary buffer
8697 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8698 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8703 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8704 boolean save_as_template)
8710 if (!(file = fopen(filename, MODE_WRITE)))
8712 Warn("cannot save level file '%s'", filename);
8717 level->file_version = FILE_VERSION_ACTUAL;
8718 level->game_version = GAME_VERSION_ACTUAL;
8720 level->creation_date = getCurrentDate();
8722 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8723 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8725 chunk_size = SaveLevel_VERS(NULL, level);
8726 putFileChunkBE(file, "VERS", chunk_size);
8727 SaveLevel_VERS(file, level);
8729 chunk_size = SaveLevel_DATE(NULL, level);
8730 putFileChunkBE(file, "DATE", chunk_size);
8731 SaveLevel_DATE(file, level);
8733 chunk_size = SaveLevel_NAME(NULL, level);
8734 putFileChunkBE(file, "NAME", chunk_size);
8735 SaveLevel_NAME(file, level);
8737 chunk_size = SaveLevel_AUTH(NULL, level);
8738 putFileChunkBE(file, "AUTH", chunk_size);
8739 SaveLevel_AUTH(file, level);
8741 chunk_size = SaveLevel_INFO(NULL, level);
8742 putFileChunkBE(file, "INFO", chunk_size);
8743 SaveLevel_INFO(file, level);
8745 chunk_size = SaveLevel_BODY(NULL, level);
8746 putFileChunkBE(file, "BODY", chunk_size);
8747 SaveLevel_BODY(file, level);
8749 chunk_size = SaveLevel_ELEM(NULL, level);
8750 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8752 putFileChunkBE(file, "ELEM", chunk_size);
8753 SaveLevel_ELEM(file, level);
8756 for (i = 0; i < NUM_ENVELOPES; i++)
8758 int element = EL_ENVELOPE_1 + i;
8760 chunk_size = SaveLevel_NOTE(NULL, level, element);
8761 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8763 putFileChunkBE(file, "NOTE", chunk_size);
8764 SaveLevel_NOTE(file, level, element);
8768 // if not using template level, check for non-default custom/group elements
8769 if (!level->use_custom_template || save_as_template)
8771 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8773 int element = EL_CUSTOM_START + i;
8775 chunk_size = SaveLevel_CUSX(NULL, level, element);
8776 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8778 putFileChunkBE(file, "CUSX", chunk_size);
8779 SaveLevel_CUSX(file, level, element);
8783 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8785 int element = EL_GROUP_START + i;
8787 chunk_size = SaveLevel_GRPX(NULL, level, element);
8788 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8790 putFileChunkBE(file, "GRPX", chunk_size);
8791 SaveLevel_GRPX(file, level, element);
8795 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8797 int element = GET_EMPTY_ELEMENT(i);
8799 chunk_size = SaveLevel_EMPX(NULL, level, element);
8800 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8802 putFileChunkBE(file, "EMPX", chunk_size);
8803 SaveLevel_EMPX(file, level, element);
8810 SetFilePermissions(filename, PERMS_PRIVATE);
8813 void SaveLevel(int nr)
8815 char *filename = getDefaultLevelFilename(nr);
8817 SaveLevelFromFilename(&level, filename, FALSE);
8820 void SaveLevelTemplate(void)
8822 char *filename = getLocalLevelTemplateFilename();
8824 SaveLevelFromFilename(&level, filename, TRUE);
8827 boolean SaveLevelChecked(int nr)
8829 char *filename = getDefaultLevelFilename(nr);
8830 boolean new_level = !fileExists(filename);
8831 boolean level_saved = FALSE;
8833 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8838 Request("Level saved!", REQ_CONFIRM);
8846 void DumpLevel(struct LevelInfo *level)
8848 if (level->no_level_file || level->no_valid_file)
8850 Warn("cannot dump -- no valid level file found");
8856 Print("Level xxx (file version %08d, game version %08d)\n",
8857 level->file_version, level->game_version);
8860 Print("Level author: '%s'\n", level->author);
8861 Print("Level title: '%s'\n", level->name);
8863 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8865 Print("Level time: %d seconds\n", level->time);
8866 Print("Gems needed: %d\n", level->gems_needed);
8868 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8869 Print("Time for wheel: %d seconds\n", level->time_wheel);
8870 Print("Time for light: %d seconds\n", level->time_light);
8871 Print("Time for timegate: %d seconds\n", level->time_timegate);
8873 Print("Amoeba speed: %d\n", level->amoeba_speed);
8876 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8877 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8878 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8879 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8880 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8881 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8887 for (i = 0; i < NUM_ENVELOPES; i++)
8889 char *text = level->envelope[i].text;
8890 int text_len = strlen(text);
8891 boolean has_text = FALSE;
8893 for (j = 0; j < text_len; j++)
8894 if (text[j] != ' ' && text[j] != '\n')
8900 Print("Envelope %d:\n'%s'\n", i + 1, text);
8908 void DumpLevels(void)
8910 static LevelDirTree *dumplevel_leveldir = NULL;
8912 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8913 global.dumplevel_leveldir);
8915 if (dumplevel_leveldir == NULL)
8916 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8918 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8919 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8920 Fail("no such level number: %d", global.dumplevel_level_nr);
8922 leveldir_current = dumplevel_leveldir;
8924 LoadLevel(global.dumplevel_level_nr);
8931 // ============================================================================
8932 // tape file functions
8933 // ============================================================================
8935 static void setTapeInfoToDefaults(void)
8939 // always start with reliable default values (empty tape)
8942 // default values (also for pre-1.2 tapes) with only the first player
8943 tape.player_participates[0] = TRUE;
8944 for (i = 1; i < MAX_PLAYERS; i++)
8945 tape.player_participates[i] = FALSE;
8947 // at least one (default: the first) player participates in every tape
8948 tape.num_participating_players = 1;
8950 tape.property_bits = TAPE_PROPERTY_NONE;
8952 tape.level_nr = level_nr;
8954 tape.changed = FALSE;
8955 tape.solved = FALSE;
8957 tape.recording = FALSE;
8958 tape.playing = FALSE;
8959 tape.pausing = FALSE;
8961 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8962 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8964 tape.no_info_chunk = TRUE;
8965 tape.no_valid_file = FALSE;
8968 static int getTapePosSize(struct TapeInfo *tape)
8970 int tape_pos_size = 0;
8972 if (tape->use_key_actions)
8973 tape_pos_size += tape->num_participating_players;
8975 if (tape->use_mouse_actions)
8976 tape_pos_size += 3; // x and y position and mouse button mask
8978 tape_pos_size += 1; // tape action delay value
8980 return tape_pos_size;
8983 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8985 tape->use_key_actions = FALSE;
8986 tape->use_mouse_actions = FALSE;
8988 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8989 tape->use_key_actions = TRUE;
8991 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8992 tape->use_mouse_actions = TRUE;
8995 static int getTapeActionValue(struct TapeInfo *tape)
8997 return (tape->use_key_actions &&
8998 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8999 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9000 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9001 TAPE_ACTIONS_DEFAULT);
9004 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9006 tape->file_version = getFileVersion(file);
9007 tape->game_version = getFileVersion(file);
9012 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9016 tape->random_seed = getFile32BitBE(file);
9017 tape->date = getFile32BitBE(file);
9018 tape->length = getFile32BitBE(file);
9020 // read header fields that are new since version 1.2
9021 if (tape->file_version >= FILE_VERSION_1_2)
9023 byte store_participating_players = getFile8Bit(file);
9026 // since version 1.2, tapes store which players participate in the tape
9027 tape->num_participating_players = 0;
9028 for (i = 0; i < MAX_PLAYERS; i++)
9030 tape->player_participates[i] = FALSE;
9032 if (store_participating_players & (1 << i))
9034 tape->player_participates[i] = TRUE;
9035 tape->num_participating_players++;
9039 setTapeActionFlags(tape, getFile8Bit(file));
9041 tape->property_bits = getFile8Bit(file);
9042 tape->solved = getFile8Bit(file);
9044 engine_version = getFileVersion(file);
9045 if (engine_version > 0)
9046 tape->engine_version = engine_version;
9048 tape->engine_version = tape->game_version;
9054 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9056 tape->scr_fieldx = getFile8Bit(file);
9057 tape->scr_fieldy = getFile8Bit(file);
9062 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9064 char *level_identifier = NULL;
9065 int level_identifier_size;
9068 tape->no_info_chunk = FALSE;
9070 level_identifier_size = getFile16BitBE(file);
9072 level_identifier = checked_malloc(level_identifier_size);
9074 for (i = 0; i < level_identifier_size; i++)
9075 level_identifier[i] = getFile8Bit(file);
9077 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9078 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9080 checked_free(level_identifier);
9082 tape->level_nr = getFile16BitBE(file);
9084 chunk_size = 2 + level_identifier_size + 2;
9089 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9092 int tape_pos_size = getTapePosSize(tape);
9093 int chunk_size_expected = tape_pos_size * tape->length;
9095 if (chunk_size_expected != chunk_size)
9097 ReadUnusedBytesFromFile(file, chunk_size);
9098 return chunk_size_expected;
9101 for (i = 0; i < tape->length; i++)
9103 if (i >= MAX_TAPE_LEN)
9105 Warn("tape truncated -- size exceeds maximum tape size %d",
9108 // tape too large; read and ignore remaining tape data from this chunk
9109 for (;i < tape->length; i++)
9110 ReadUnusedBytesFromFile(file, tape_pos_size);
9115 if (tape->use_key_actions)
9117 for (j = 0; j < MAX_PLAYERS; j++)
9119 tape->pos[i].action[j] = MV_NONE;
9121 if (tape->player_participates[j])
9122 tape->pos[i].action[j] = getFile8Bit(file);
9126 if (tape->use_mouse_actions)
9128 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9129 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9130 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9133 tape->pos[i].delay = getFile8Bit(file);
9135 if (tape->file_version == FILE_VERSION_1_0)
9137 // eliminate possible diagonal moves in old tapes
9138 // this is only for backward compatibility
9140 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9141 byte action = tape->pos[i].action[0];
9142 int k, num_moves = 0;
9144 for (k = 0; k < 4; k++)
9146 if (action & joy_dir[k])
9148 tape->pos[i + num_moves].action[0] = joy_dir[k];
9150 tape->pos[i + num_moves].delay = 0;
9159 tape->length += num_moves;
9162 else if (tape->file_version < FILE_VERSION_2_0)
9164 // convert pre-2.0 tapes to new tape format
9166 if (tape->pos[i].delay > 1)
9169 tape->pos[i + 1] = tape->pos[i];
9170 tape->pos[i + 1].delay = 1;
9173 for (j = 0; j < MAX_PLAYERS; j++)
9174 tape->pos[i].action[j] = MV_NONE;
9175 tape->pos[i].delay--;
9182 if (checkEndOfFile(file))
9186 if (i != tape->length)
9187 chunk_size = tape_pos_size * i;
9192 static void LoadTape_SokobanSolution(char *filename)
9195 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9197 if (!(file = openFile(filename, MODE_READ)))
9199 tape.no_valid_file = TRUE;
9204 while (!checkEndOfFile(file))
9206 unsigned char c = getByteFromFile(file);
9208 if (checkEndOfFile(file))
9215 tape.pos[tape.length].action[0] = MV_UP;
9216 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9222 tape.pos[tape.length].action[0] = MV_DOWN;
9223 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9229 tape.pos[tape.length].action[0] = MV_LEFT;
9230 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9236 tape.pos[tape.length].action[0] = MV_RIGHT;
9237 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9245 // ignore white-space characters
9249 tape.no_valid_file = TRUE;
9251 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9259 if (tape.no_valid_file)
9262 tape.length_frames = GetTapeLengthFrames();
9263 tape.length_seconds = GetTapeLengthSeconds();
9266 void LoadTapeFromFilename(char *filename)
9268 char cookie[MAX_LINE_LEN];
9269 char chunk_name[CHUNK_ID_LEN + 1];
9273 // always start with reliable default values
9274 setTapeInfoToDefaults();
9276 if (strSuffix(filename, ".sln"))
9278 LoadTape_SokobanSolution(filename);
9283 if (!(file = openFile(filename, MODE_READ)))
9285 tape.no_valid_file = TRUE;
9290 getFileChunkBE(file, chunk_name, NULL);
9291 if (strEqual(chunk_name, "RND1"))
9293 getFile32BitBE(file); // not used
9295 getFileChunkBE(file, chunk_name, NULL);
9296 if (!strEqual(chunk_name, "TAPE"))
9298 tape.no_valid_file = TRUE;
9300 Warn("unknown format of tape file '%s'", filename);
9307 else // check for pre-2.0 file format with cookie string
9309 strcpy(cookie, chunk_name);
9310 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9312 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9313 cookie[strlen(cookie) - 1] = '\0';
9315 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9317 tape.no_valid_file = TRUE;
9319 Warn("unknown format of tape file '%s'", filename);
9326 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9328 tape.no_valid_file = TRUE;
9330 Warn("unsupported version of tape file '%s'", filename);
9337 // pre-2.0 tape files have no game version, so use file version here
9338 tape.game_version = tape.file_version;
9341 if (tape.file_version < FILE_VERSION_1_2)
9343 // tape files from versions before 1.2.0 without chunk structure
9344 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9345 LoadTape_BODY(file, 2 * tape.length, &tape);
9353 int (*loader)(File *, int, struct TapeInfo *);
9357 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9358 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9359 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9360 { "INFO", -1, LoadTape_INFO },
9361 { "BODY", -1, LoadTape_BODY },
9365 while (getFileChunkBE(file, chunk_name, &chunk_size))
9369 while (chunk_info[i].name != NULL &&
9370 !strEqual(chunk_name, chunk_info[i].name))
9373 if (chunk_info[i].name == NULL)
9375 Warn("unknown chunk '%s' in tape file '%s'",
9376 chunk_name, filename);
9378 ReadUnusedBytesFromFile(file, chunk_size);
9380 else if (chunk_info[i].size != -1 &&
9381 chunk_info[i].size != chunk_size)
9383 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9384 chunk_size, chunk_name, filename);
9386 ReadUnusedBytesFromFile(file, chunk_size);
9390 // call function to load this tape chunk
9391 int chunk_size_expected =
9392 (chunk_info[i].loader)(file, chunk_size, &tape);
9394 // the size of some chunks cannot be checked before reading other
9395 // chunks first (like "HEAD" and "BODY") that contain some header
9396 // information, so check them here
9397 if (chunk_size_expected != chunk_size)
9399 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9400 chunk_size, chunk_name, filename);
9408 tape.length_frames = GetTapeLengthFrames();
9409 tape.length_seconds = GetTapeLengthSeconds();
9412 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9414 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9416 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9417 tape.engine_version);
9421 void LoadTape(int nr)
9423 char *filename = getTapeFilename(nr);
9425 LoadTapeFromFilename(filename);
9428 void LoadSolutionTape(int nr)
9430 char *filename = getSolutionTapeFilename(nr);
9432 LoadTapeFromFilename(filename);
9434 if (TAPE_IS_EMPTY(tape))
9436 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9437 level.native_bd_level->replay != NULL)
9438 CopyNativeTape_BD_to_RND(&level);
9439 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9440 level.native_sp_level->demo.is_available)
9441 CopyNativeTape_SP_to_RND(&level);
9445 void LoadScoreTape(char *score_tape_basename, int nr)
9447 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9449 LoadTapeFromFilename(filename);
9452 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9454 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9456 LoadTapeFromFilename(filename);
9459 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9461 // chunk required for team mode tapes with non-default screen size
9462 return (tape->num_participating_players > 1 &&
9463 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9464 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9467 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9469 putFileVersion(file, tape->file_version);
9470 putFileVersion(file, tape->game_version);
9473 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9476 byte store_participating_players = 0;
9478 // set bits for participating players for compact storage
9479 for (i = 0; i < MAX_PLAYERS; i++)
9480 if (tape->player_participates[i])
9481 store_participating_players |= (1 << i);
9483 putFile32BitBE(file, tape->random_seed);
9484 putFile32BitBE(file, tape->date);
9485 putFile32BitBE(file, tape->length);
9487 putFile8Bit(file, store_participating_players);
9489 putFile8Bit(file, getTapeActionValue(tape));
9491 putFile8Bit(file, tape->property_bits);
9492 putFile8Bit(file, tape->solved);
9494 putFileVersion(file, tape->engine_version);
9497 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9499 putFile8Bit(file, tape->scr_fieldx);
9500 putFile8Bit(file, tape->scr_fieldy);
9503 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9505 int level_identifier_size = strlen(tape->level_identifier) + 1;
9508 putFile16BitBE(file, level_identifier_size);
9510 for (i = 0; i < level_identifier_size; i++)
9511 putFile8Bit(file, tape->level_identifier[i]);
9513 putFile16BitBE(file, tape->level_nr);
9516 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9520 for (i = 0; i < tape->length; i++)
9522 if (tape->use_key_actions)
9524 for (j = 0; j < MAX_PLAYERS; j++)
9525 if (tape->player_participates[j])
9526 putFile8Bit(file, tape->pos[i].action[j]);
9529 if (tape->use_mouse_actions)
9531 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9532 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9533 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9536 putFile8Bit(file, tape->pos[i].delay);
9540 void SaveTapeToFilename(char *filename)
9544 int info_chunk_size;
9545 int body_chunk_size;
9547 if (!(file = fopen(filename, MODE_WRITE)))
9549 Warn("cannot save level recording file '%s'", filename);
9554 tape_pos_size = getTapePosSize(&tape);
9556 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9557 body_chunk_size = tape_pos_size * tape.length;
9559 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9560 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9562 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9563 SaveTape_VERS(file, &tape);
9565 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9566 SaveTape_HEAD(file, &tape);
9568 if (checkSaveTape_SCRN(&tape))
9570 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9571 SaveTape_SCRN(file, &tape);
9574 putFileChunkBE(file, "INFO", info_chunk_size);
9575 SaveTape_INFO(file, &tape);
9577 putFileChunkBE(file, "BODY", body_chunk_size);
9578 SaveTape_BODY(file, &tape);
9582 SetFilePermissions(filename, PERMS_PRIVATE);
9585 static void SaveTapeExt(char *filename)
9589 tape.file_version = FILE_VERSION_ACTUAL;
9590 tape.game_version = GAME_VERSION_ACTUAL;
9592 tape.num_participating_players = 0;
9594 // count number of participating players
9595 for (i = 0; i < MAX_PLAYERS; i++)
9596 if (tape.player_participates[i])
9597 tape.num_participating_players++;
9599 SaveTapeToFilename(filename);
9601 tape.changed = FALSE;
9604 void SaveTape(int nr)
9606 char *filename = getTapeFilename(nr);
9608 InitTapeDirectory(leveldir_current->subdir);
9610 SaveTapeExt(filename);
9613 void SaveScoreTape(int nr)
9615 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9617 // used instead of "leveldir_current->subdir" (for network games)
9618 InitScoreTapeDirectory(levelset.identifier, nr);
9620 SaveTapeExt(filename);
9623 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9624 unsigned int req_state_added)
9626 char *filename = getTapeFilename(nr);
9627 boolean new_tape = !fileExists(filename);
9628 boolean tape_saved = FALSE;
9630 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9635 Request(msg_saved, REQ_CONFIRM | req_state_added);
9643 boolean SaveTapeChecked(int nr)
9645 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9648 boolean SaveTapeChecked_LevelSolved(int nr)
9650 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9651 "Level solved! Tape saved!", REQ_STAY_OPEN);
9654 void DumpTape(struct TapeInfo *tape)
9656 int tape_frame_counter;
9659 if (tape->no_valid_file)
9661 Warn("cannot dump -- no valid tape file found");
9668 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9669 tape->level_nr, tape->file_version, tape->game_version);
9670 Print(" (effective engine version %08d)\n",
9671 tape->engine_version);
9672 Print("Level series identifier: '%s'\n", tape->level_identifier);
9674 Print("Solution tape: %s\n",
9675 tape->solved ? "yes" :
9676 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9678 Print("Special tape properties: ");
9679 if (tape->property_bits == TAPE_PROPERTY_NONE)
9681 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9682 Print("[em_random_bug]");
9683 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9684 Print("[game_speed]");
9685 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9687 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9688 Print("[single_step]");
9689 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9690 Print("[snapshot]");
9691 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9692 Print("[replayed]");
9693 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9694 Print("[tas_keys]");
9695 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9696 Print("[small_graphics]");
9699 int year2 = tape->date / 10000;
9700 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9701 int month_index_raw = (tape->date / 100) % 100;
9702 int month_index = month_index_raw % 12; // prevent invalid index
9703 int month = month_index + 1;
9704 int day = tape->date % 100;
9706 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9710 tape_frame_counter = 0;
9712 for (i = 0; i < tape->length; i++)
9714 if (i >= MAX_TAPE_LEN)
9719 for (j = 0; j < MAX_PLAYERS; j++)
9721 if (tape->player_participates[j])
9723 int action = tape->pos[i].action[j];
9725 Print("%d:%02x ", j, action);
9726 Print("[%c%c%c%c|%c%c] - ",
9727 (action & JOY_LEFT ? '<' : ' '),
9728 (action & JOY_RIGHT ? '>' : ' '),
9729 (action & JOY_UP ? '^' : ' '),
9730 (action & JOY_DOWN ? 'v' : ' '),
9731 (action & JOY_BUTTON_1 ? '1' : ' '),
9732 (action & JOY_BUTTON_2 ? '2' : ' '));
9736 Print("(%03d) ", tape->pos[i].delay);
9737 Print("[%05d]\n", tape_frame_counter);
9739 tape_frame_counter += tape->pos[i].delay;
9745 void DumpTapes(void)
9747 static LevelDirTree *dumptape_leveldir = NULL;
9749 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9750 global.dumptape_leveldir);
9752 if (dumptape_leveldir == NULL)
9753 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9755 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9756 global.dumptape_level_nr > dumptape_leveldir->last_level)
9757 Fail("no such level number: %d", global.dumptape_level_nr);
9759 leveldir_current = dumptape_leveldir;
9761 if (options.mytapes)
9762 LoadTape(global.dumptape_level_nr);
9764 LoadSolutionTape(global.dumptape_level_nr);
9772 // ============================================================================
9773 // score file functions
9774 // ============================================================================
9776 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9780 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9782 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9783 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9784 scores->entry[i].score = 0;
9785 scores->entry[i].time = 0;
9787 scores->entry[i].id = -1;
9788 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9789 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9790 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9791 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9792 strcpy(scores->entry[i].country_code, "??");
9795 scores->num_entries = 0;
9796 scores->last_added = -1;
9797 scores->last_added_local = -1;
9799 scores->updated = FALSE;
9800 scores->uploaded = FALSE;
9801 scores->tape_downloaded = FALSE;
9802 scores->force_last_added = FALSE;
9804 // The following values are intentionally not reset here:
9808 // - continue_playing
9809 // - continue_on_return
9812 static void setScoreInfoToDefaults(void)
9814 setScoreInfoToDefaultsExt(&scores);
9817 static void setServerScoreInfoToDefaults(void)
9819 setScoreInfoToDefaultsExt(&server_scores);
9822 static void LoadScore_OLD(int nr)
9825 char *filename = getScoreFilename(nr);
9826 char cookie[MAX_LINE_LEN];
9827 char line[MAX_LINE_LEN];
9831 if (!(file = fopen(filename, MODE_READ)))
9834 // check file identifier
9835 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9837 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9838 cookie[strlen(cookie) - 1] = '\0';
9840 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9842 Warn("unknown format of score file '%s'", filename);
9849 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9851 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9852 Warn("fscanf() failed; %s", strerror(errno));
9854 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9857 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9858 line[strlen(line) - 1] = '\0';
9860 for (line_ptr = line; *line_ptr; line_ptr++)
9862 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9864 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9865 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9874 static void ConvertScore_OLD(void)
9876 // only convert score to time for levels that rate playing time over score
9877 if (!level.rate_time_over_score)
9880 // convert old score to playing time for score-less levels (like Supaplex)
9881 int time_final_max = 999;
9884 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9886 int score = scores.entry[i].score;
9888 if (score > 0 && score < time_final_max)
9889 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9893 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9895 scores->file_version = getFileVersion(file);
9896 scores->game_version = getFileVersion(file);
9901 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9903 char *level_identifier = NULL;
9904 int level_identifier_size;
9907 level_identifier_size = getFile16BitBE(file);
9909 level_identifier = checked_malloc(level_identifier_size);
9911 for (i = 0; i < level_identifier_size; i++)
9912 level_identifier[i] = getFile8Bit(file);
9914 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9915 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9917 checked_free(level_identifier);
9919 scores->level_nr = getFile16BitBE(file);
9920 scores->num_entries = getFile16BitBE(file);
9922 chunk_size = 2 + level_identifier_size + 2 + 2;
9927 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9931 for (i = 0; i < scores->num_entries; i++)
9933 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9934 scores->entry[i].name[j] = getFile8Bit(file);
9936 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9939 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9944 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9948 for (i = 0; i < scores->num_entries; i++)
9949 scores->entry[i].score = getFile16BitBE(file);
9951 chunk_size = scores->num_entries * 2;
9956 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9960 for (i = 0; i < scores->num_entries; i++)
9961 scores->entry[i].score = getFile32BitBE(file);
9963 chunk_size = scores->num_entries * 4;
9968 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9972 for (i = 0; i < scores->num_entries; i++)
9973 scores->entry[i].time = getFile32BitBE(file);
9975 chunk_size = scores->num_entries * 4;
9980 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9984 for (i = 0; i < scores->num_entries; i++)
9986 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9987 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9989 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9992 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9997 void LoadScore(int nr)
9999 char *filename = getScoreFilename(nr);
10000 char cookie[MAX_LINE_LEN];
10001 char chunk_name[CHUNK_ID_LEN + 1];
10003 boolean old_score_file_format = FALSE;
10006 // always start with reliable default values
10007 setScoreInfoToDefaults();
10009 if (!(file = openFile(filename, MODE_READ)))
10012 getFileChunkBE(file, chunk_name, NULL);
10013 if (strEqual(chunk_name, "RND1"))
10015 getFile32BitBE(file); // not used
10017 getFileChunkBE(file, chunk_name, NULL);
10018 if (!strEqual(chunk_name, "SCOR"))
10020 Warn("unknown format of score file '%s'", filename);
10027 else // check for old file format with cookie string
10029 strcpy(cookie, chunk_name);
10030 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10032 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10033 cookie[strlen(cookie) - 1] = '\0';
10035 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10037 Warn("unknown format of score file '%s'", filename);
10044 old_score_file_format = TRUE;
10047 if (old_score_file_format)
10049 // score files from versions before 4.2.4.0 without chunk structure
10052 // convert score to time, if possible (mainly for Supaplex levels)
10053 ConvertScore_OLD();
10061 int (*loader)(File *, int, struct ScoreInfo *);
10065 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10066 { "INFO", -1, LoadScore_INFO },
10067 { "NAME", -1, LoadScore_NAME },
10068 { "SCOR", -1, LoadScore_SCOR },
10069 { "SC4R", -1, LoadScore_SC4R },
10070 { "TIME", -1, LoadScore_TIME },
10071 { "TAPE", -1, LoadScore_TAPE },
10076 while (getFileChunkBE(file, chunk_name, &chunk_size))
10080 while (chunk_info[i].name != NULL &&
10081 !strEqual(chunk_name, chunk_info[i].name))
10084 if (chunk_info[i].name == NULL)
10086 Warn("unknown chunk '%s' in score file '%s'",
10087 chunk_name, filename);
10089 ReadUnusedBytesFromFile(file, chunk_size);
10091 else if (chunk_info[i].size != -1 &&
10092 chunk_info[i].size != chunk_size)
10094 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10095 chunk_size, chunk_name, filename);
10097 ReadUnusedBytesFromFile(file, chunk_size);
10101 // call function to load this score chunk
10102 int chunk_size_expected =
10103 (chunk_info[i].loader)(file, chunk_size, &scores);
10105 // the size of some chunks cannot be checked before reading other
10106 // chunks first (like "HEAD" and "BODY") that contain some header
10107 // information, so check them here
10108 if (chunk_size_expected != chunk_size)
10110 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10111 chunk_size, chunk_name, filename);
10120 #if ENABLE_HISTORIC_CHUNKS
10121 void SaveScore_OLD(int nr)
10124 char *filename = getScoreFilename(nr);
10127 // used instead of "leveldir_current->subdir" (for network games)
10128 InitScoreDirectory(levelset.identifier);
10130 if (!(file = fopen(filename, MODE_WRITE)))
10132 Warn("cannot save score for level %d", nr);
10137 fprintf(file, "%s\n\n", SCORE_COOKIE);
10139 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10140 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10144 SetFilePermissions(filename, PERMS_PRIVATE);
10148 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10150 putFileVersion(file, scores->file_version);
10151 putFileVersion(file, scores->game_version);
10154 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10156 int level_identifier_size = strlen(scores->level_identifier) + 1;
10159 putFile16BitBE(file, level_identifier_size);
10161 for (i = 0; i < level_identifier_size; i++)
10162 putFile8Bit(file, scores->level_identifier[i]);
10164 putFile16BitBE(file, scores->level_nr);
10165 putFile16BitBE(file, scores->num_entries);
10168 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10172 for (i = 0; i < scores->num_entries; i++)
10174 int name_size = strlen(scores->entry[i].name);
10176 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10177 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10181 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10185 for (i = 0; i < scores->num_entries; i++)
10186 putFile16BitBE(file, scores->entry[i].score);
10189 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10193 for (i = 0; i < scores->num_entries; i++)
10194 putFile32BitBE(file, scores->entry[i].score);
10197 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10201 for (i = 0; i < scores->num_entries; i++)
10202 putFile32BitBE(file, scores->entry[i].time);
10205 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10209 for (i = 0; i < scores->num_entries; i++)
10211 int size = strlen(scores->entry[i].tape_basename);
10213 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10214 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10218 static void SaveScoreToFilename(char *filename)
10221 int info_chunk_size;
10222 int name_chunk_size;
10223 int scor_chunk_size;
10224 int sc4r_chunk_size;
10225 int time_chunk_size;
10226 int tape_chunk_size;
10227 boolean has_large_score_values;
10230 if (!(file = fopen(filename, MODE_WRITE)))
10232 Warn("cannot save score file '%s'", filename);
10237 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10238 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10239 scor_chunk_size = scores.num_entries * 2;
10240 sc4r_chunk_size = scores.num_entries * 4;
10241 time_chunk_size = scores.num_entries * 4;
10242 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10244 has_large_score_values = FALSE;
10245 for (i = 0; i < scores.num_entries; i++)
10246 if (scores.entry[i].score > 0xffff)
10247 has_large_score_values = TRUE;
10249 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10250 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10252 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10253 SaveScore_VERS(file, &scores);
10255 putFileChunkBE(file, "INFO", info_chunk_size);
10256 SaveScore_INFO(file, &scores);
10258 putFileChunkBE(file, "NAME", name_chunk_size);
10259 SaveScore_NAME(file, &scores);
10261 if (has_large_score_values)
10263 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10264 SaveScore_SC4R(file, &scores);
10268 putFileChunkBE(file, "SCOR", scor_chunk_size);
10269 SaveScore_SCOR(file, &scores);
10272 putFileChunkBE(file, "TIME", time_chunk_size);
10273 SaveScore_TIME(file, &scores);
10275 putFileChunkBE(file, "TAPE", tape_chunk_size);
10276 SaveScore_TAPE(file, &scores);
10280 SetFilePermissions(filename, PERMS_PRIVATE);
10283 void SaveScore(int nr)
10285 char *filename = getScoreFilename(nr);
10288 // used instead of "leveldir_current->subdir" (for network games)
10289 InitScoreDirectory(levelset.identifier);
10291 scores.file_version = FILE_VERSION_ACTUAL;
10292 scores.game_version = GAME_VERSION_ACTUAL;
10294 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10295 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10296 scores.level_nr = level_nr;
10298 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10299 if (scores.entry[i].score == 0 &&
10300 scores.entry[i].time == 0 &&
10301 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10304 scores.num_entries = i;
10306 if (scores.num_entries == 0)
10309 SaveScoreToFilename(filename);
10312 static void LoadServerScoreFromCache(int nr)
10314 struct ScoreEntry score_entry;
10323 { &score_entry.score, FALSE, 0 },
10324 { &score_entry.time, FALSE, 0 },
10325 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10326 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10327 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10328 { &score_entry.id, FALSE, 0 },
10329 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10330 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10331 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10332 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10336 char *filename = getScoreCacheFilename(nr);
10337 SetupFileHash *score_hash = loadSetupFileHash(filename);
10340 server_scores.num_entries = 0;
10342 if (score_hash == NULL)
10345 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10347 score_entry = server_scores.entry[i];
10349 for (j = 0; score_mapping[j].value != NULL; j++)
10353 sprintf(token, "%02d.%d", i, j);
10355 char *value = getHashEntry(score_hash, token);
10360 if (score_mapping[j].is_string)
10362 char *score_value = (char *)score_mapping[j].value;
10363 int value_size = score_mapping[j].string_size;
10365 strncpy(score_value, value, value_size);
10366 score_value[value_size] = '\0';
10370 int *score_value = (int *)score_mapping[j].value;
10372 *score_value = atoi(value);
10375 server_scores.num_entries = i + 1;
10378 server_scores.entry[i] = score_entry;
10381 freeSetupFileHash(score_hash);
10384 void LoadServerScore(int nr, boolean download_score)
10386 if (!setup.use_api_server)
10389 // always start with reliable default values
10390 setServerScoreInfoToDefaults();
10392 // 1st step: load server scores from cache file (which may not exist)
10393 // (this should prevent reading it while the thread is writing to it)
10394 LoadServerScoreFromCache(nr);
10396 if (download_score && runtime.use_api_server)
10398 // 2nd step: download server scores from score server to cache file
10399 // (as thread, as it might time out if the server is not reachable)
10400 ApiGetScoreAsThread(nr);
10404 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10406 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10408 // if score tape not uploaded, ask for uploading missing tapes later
10409 if (!setup.has_remaining_tapes)
10410 setup.ask_for_remaining_tapes = TRUE;
10412 setup.provide_uploading_tapes = TRUE;
10413 setup.has_remaining_tapes = TRUE;
10415 SaveSetup_ServerSetup();
10418 void SaveServerScore(int nr, boolean tape_saved)
10420 if (!runtime.use_api_server)
10422 PrepareScoreTapesForUpload(leveldir_current->subdir);
10427 ApiAddScoreAsThread(nr, tape_saved, NULL);
10430 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10431 char *score_tape_filename)
10433 if (!runtime.use_api_server)
10436 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10439 void LoadLocalAndServerScore(int nr, boolean download_score)
10441 int last_added_local = scores.last_added_local;
10442 boolean force_last_added = scores.force_last_added;
10444 // needed if only showing server scores
10445 setScoreInfoToDefaults();
10447 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10450 // restore last added local score entry (before merging server scores)
10451 scores.last_added = scores.last_added_local = last_added_local;
10453 if (setup.use_api_server &&
10454 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10456 // load server scores from cache file and trigger update from server
10457 LoadServerScore(nr, download_score);
10459 // merge local scores with scores from server
10460 MergeServerScore();
10463 if (force_last_added)
10464 scores.force_last_added = force_last_added;
10468 // ============================================================================
10469 // setup file functions
10470 // ============================================================================
10472 #define TOKEN_STR_PLAYER_PREFIX "player_"
10475 static struct TokenInfo global_setup_tokens[] =
10479 &setup.player_name, "player_name"
10483 &setup.multiple_users, "multiple_users"
10487 &setup.sound, "sound"
10491 &setup.sound_loops, "repeating_sound_loops"
10495 &setup.sound_music, "background_music"
10499 &setup.sound_simple, "simple_sound_effects"
10503 &setup.toons, "toons"
10507 &setup.global_animations, "global_animations"
10511 &setup.scroll_delay, "scroll_delay"
10515 &setup.forced_scroll_delay, "forced_scroll_delay"
10519 &setup.scroll_delay_value, "scroll_delay_value"
10523 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10527 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10531 &setup.fade_screens, "fade_screens"
10535 &setup.autorecord, "automatic_tape_recording"
10539 &setup.autorecord_after_replay, "autorecord_after_replay"
10543 &setup.auto_pause_on_start, "auto_pause_on_start"
10547 &setup.show_titlescreen, "show_titlescreen"
10551 &setup.quick_doors, "quick_doors"
10555 &setup.team_mode, "team_mode"
10559 &setup.handicap, "handicap"
10563 &setup.skip_levels, "skip_levels"
10567 &setup.increment_levels, "increment_levels"
10571 &setup.auto_play_next_level, "auto_play_next_level"
10575 &setup.count_score_after_game, "count_score_after_game"
10579 &setup.show_scores_after_game, "show_scores_after_game"
10583 &setup.time_limit, "time_limit"
10587 &setup.fullscreen, "fullscreen"
10591 &setup.window_scaling_percent, "window_scaling_percent"
10595 &setup.window_scaling_quality, "window_scaling_quality"
10599 &setup.screen_rendering_mode, "screen_rendering_mode"
10603 &setup.vsync_mode, "vsync_mode"
10607 &setup.ask_on_escape, "ask_on_escape"
10611 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10615 &setup.ask_on_game_over, "ask_on_game_over"
10619 &setup.ask_on_quit_game, "ask_on_quit_game"
10623 &setup.ask_on_quit_program, "ask_on_quit_program"
10627 &setup.quick_switch, "quick_player_switch"
10631 &setup.input_on_focus, "input_on_focus"
10635 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10639 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10643 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10647 &setup.game_speed_extended, "game_speed_extended"
10651 &setup.game_frame_delay, "game_frame_delay"
10655 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10659 &setup.bd_skip_hatching, "bd_skip_hatching"
10663 &setup.bd_scroll_delay, "bd_scroll_delay"
10667 &setup.bd_smooth_movements, "bd_smooth_movements"
10671 &setup.sp_show_border_elements, "sp_show_border_elements"
10675 &setup.small_game_graphics, "small_game_graphics"
10679 &setup.show_load_save_buttons, "show_load_save_buttons"
10683 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10687 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10691 &setup.graphics_set, "graphics_set"
10695 &setup.sounds_set, "sounds_set"
10699 &setup.music_set, "music_set"
10703 &setup.override_level_graphics, "override_level_graphics"
10707 &setup.override_level_sounds, "override_level_sounds"
10711 &setup.override_level_music, "override_level_music"
10715 &setup.volume_simple, "volume_simple"
10719 &setup.volume_loops, "volume_loops"
10723 &setup.volume_music, "volume_music"
10727 &setup.network_mode, "network_mode"
10731 &setup.network_player_nr, "network_player"
10735 &setup.network_server_hostname, "network_server_hostname"
10739 &setup.touch.control_type, "touch.control_type"
10743 &setup.touch.move_distance, "touch.move_distance"
10747 &setup.touch.drop_distance, "touch.drop_distance"
10751 &setup.touch.transparency, "touch.transparency"
10755 &setup.touch.draw_outlined, "touch.draw_outlined"
10759 &setup.touch.draw_pressed, "touch.draw_pressed"
10763 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10767 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10771 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10775 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10779 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10783 static struct TokenInfo auto_setup_tokens[] =
10787 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10791 static struct TokenInfo server_setup_tokens[] =
10795 &setup.player_uuid, "player_uuid"
10799 &setup.player_version, "player_version"
10803 &setup.use_api_server, TEST_PREFIX "use_api_server"
10807 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10811 &setup.api_server_password, TEST_PREFIX "api_server_password"
10815 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10819 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10823 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10827 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10831 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10835 static struct TokenInfo editor_setup_tokens[] =
10839 &setup.editor.el_classic, "editor.el_classic"
10843 &setup.editor.el_custom, "editor.el_custom"
10847 &setup.editor.el_user_defined, "editor.el_user_defined"
10851 &setup.editor.el_dynamic, "editor.el_dynamic"
10855 &setup.editor.el_headlines, "editor.el_headlines"
10859 &setup.editor.show_element_token, "editor.show_element_token"
10863 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10867 static struct TokenInfo editor_cascade_setup_tokens[] =
10871 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10875 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10879 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10883 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10887 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10891 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10895 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10899 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10903 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10907 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10911 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10915 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10919 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10923 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10927 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10931 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10935 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10939 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10943 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10947 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10951 static struct TokenInfo shortcut_setup_tokens[] =
10955 &setup.shortcut.save_game, "shortcut.save_game"
10959 &setup.shortcut.load_game, "shortcut.load_game"
10963 &setup.shortcut.restart_game, "shortcut.restart_game"
10967 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10971 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10975 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10979 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10983 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10987 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10991 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10995 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10999 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11003 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11007 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11011 &setup.shortcut.tape_record, "shortcut.tape_record"
11015 &setup.shortcut.tape_play, "shortcut.tape_play"
11019 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11023 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11027 &setup.shortcut.sound_music, "shortcut.sound_music"
11031 &setup.shortcut.snap_left, "shortcut.snap_left"
11035 &setup.shortcut.snap_right, "shortcut.snap_right"
11039 &setup.shortcut.snap_up, "shortcut.snap_up"
11043 &setup.shortcut.snap_down, "shortcut.snap_down"
11047 static struct SetupInputInfo setup_input;
11048 static struct TokenInfo player_setup_tokens[] =
11052 &setup_input.use_joystick, ".use_joystick"
11056 &setup_input.joy.device_name, ".joy.device_name"
11060 &setup_input.joy.xleft, ".joy.xleft"
11064 &setup_input.joy.xmiddle, ".joy.xmiddle"
11068 &setup_input.joy.xright, ".joy.xright"
11072 &setup_input.joy.yupper, ".joy.yupper"
11076 &setup_input.joy.ymiddle, ".joy.ymiddle"
11080 &setup_input.joy.ylower, ".joy.ylower"
11084 &setup_input.joy.snap, ".joy.snap_field"
11088 &setup_input.joy.drop, ".joy.place_bomb"
11092 &setup_input.key.left, ".key.move_left"
11096 &setup_input.key.right, ".key.move_right"
11100 &setup_input.key.up, ".key.move_up"
11104 &setup_input.key.down, ".key.move_down"
11108 &setup_input.key.snap, ".key.snap_field"
11112 &setup_input.key.drop, ".key.place_bomb"
11116 static struct TokenInfo system_setup_tokens[] =
11120 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11124 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11128 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11132 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11136 static struct TokenInfo internal_setup_tokens[] =
11140 &setup.internal.program_title, "program_title"
11144 &setup.internal.program_version, "program_version"
11148 &setup.internal.program_author, "program_author"
11152 &setup.internal.program_email, "program_email"
11156 &setup.internal.program_website, "program_website"
11160 &setup.internal.program_copyright, "program_copyright"
11164 &setup.internal.program_company, "program_company"
11168 &setup.internal.program_icon_file, "program_icon_file"
11172 &setup.internal.default_graphics_set, "default_graphics_set"
11176 &setup.internal.default_sounds_set, "default_sounds_set"
11180 &setup.internal.default_music_set, "default_music_set"
11184 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11188 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11192 &setup.internal.fallback_music_file, "fallback_music_file"
11196 &setup.internal.default_level_series, "default_level_series"
11200 &setup.internal.default_window_width, "default_window_width"
11204 &setup.internal.default_window_height, "default_window_height"
11208 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11212 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11216 &setup.internal.create_user_levelset, "create_user_levelset"
11220 &setup.internal.info_screens_from_main, "info_screens_from_main"
11224 &setup.internal.menu_game, "menu_game"
11228 &setup.internal.menu_engines, "menu_engines"
11232 &setup.internal.menu_editor, "menu_editor"
11236 &setup.internal.menu_graphics, "menu_graphics"
11240 &setup.internal.menu_sound, "menu_sound"
11244 &setup.internal.menu_artwork, "menu_artwork"
11248 &setup.internal.menu_input, "menu_input"
11252 &setup.internal.menu_touch, "menu_touch"
11256 &setup.internal.menu_shortcuts, "menu_shortcuts"
11260 &setup.internal.menu_exit, "menu_exit"
11264 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11268 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11272 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11276 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11280 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11284 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11288 &setup.internal.info_title, "info_title"
11292 &setup.internal.info_elements, "info_elements"
11296 &setup.internal.info_music, "info_music"
11300 &setup.internal.info_credits, "info_credits"
11304 &setup.internal.info_program, "info_program"
11308 &setup.internal.info_version, "info_version"
11312 &setup.internal.info_levelset, "info_levelset"
11316 &setup.internal.info_exit, "info_exit"
11320 static struct TokenInfo debug_setup_tokens[] =
11324 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11328 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11332 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11336 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11340 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11344 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11348 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11352 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11356 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11360 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11364 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11368 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11372 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11376 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11380 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11384 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11388 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11392 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11396 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11400 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11404 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11407 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11411 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11415 &setup.debug.xsn_mode, "debug.xsn_mode"
11419 &setup.debug.xsn_percent, "debug.xsn_percent"
11423 static struct TokenInfo options_setup_tokens[] =
11427 &setup.options.verbose, "options.verbose"
11431 &setup.options.debug, "options.debug"
11435 &setup.options.debug_mode, "options.debug_mode"
11439 static void setSetupInfoToDefaults(struct SetupInfo *si)
11443 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11445 si->multiple_users = TRUE;
11448 si->sound_loops = TRUE;
11449 si->sound_music = TRUE;
11450 si->sound_simple = TRUE;
11452 si->global_animations = TRUE;
11453 si->scroll_delay = TRUE;
11454 si->forced_scroll_delay = FALSE;
11455 si->scroll_delay_value = STD_SCROLL_DELAY;
11456 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11457 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11458 si->fade_screens = TRUE;
11459 si->autorecord = TRUE;
11460 si->autorecord_after_replay = TRUE;
11461 si->auto_pause_on_start = FALSE;
11462 si->show_titlescreen = TRUE;
11463 si->quick_doors = FALSE;
11464 si->team_mode = FALSE;
11465 si->handicap = TRUE;
11466 si->skip_levels = TRUE;
11467 si->increment_levels = TRUE;
11468 si->auto_play_next_level = TRUE;
11469 si->count_score_after_game = TRUE;
11470 si->show_scores_after_game = TRUE;
11471 si->time_limit = TRUE;
11472 si->fullscreen = FALSE;
11473 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11474 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11475 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11476 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11477 si->ask_on_escape = TRUE;
11478 si->ask_on_escape_editor = TRUE;
11479 si->ask_on_game_over = TRUE;
11480 si->ask_on_quit_game = TRUE;
11481 si->ask_on_quit_program = TRUE;
11482 si->quick_switch = FALSE;
11483 si->input_on_focus = FALSE;
11484 si->prefer_aga_graphics = TRUE;
11485 si->prefer_lowpass_sounds = FALSE;
11486 si->prefer_extra_panel_items = TRUE;
11487 si->game_speed_extended = FALSE;
11488 si->game_frame_delay = GAME_FRAME_DELAY;
11489 si->bd_skip_uncovering = FALSE;
11490 si->bd_skip_hatching = FALSE;
11491 si->bd_scroll_delay = TRUE;
11492 si->bd_smooth_movements = AUTO;
11493 si->sp_show_border_elements = FALSE;
11494 si->small_game_graphics = FALSE;
11495 si->show_load_save_buttons = FALSE;
11496 si->show_undo_redo_buttons = FALSE;
11497 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11499 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11500 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11501 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11503 si->override_level_graphics = FALSE;
11504 si->override_level_sounds = FALSE;
11505 si->override_level_music = FALSE;
11507 si->volume_simple = 100; // percent
11508 si->volume_loops = 100; // percent
11509 si->volume_music = 100; // percent
11511 si->network_mode = FALSE;
11512 si->network_player_nr = 0; // first player
11513 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11515 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11516 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11517 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11518 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11519 si->touch.draw_outlined = TRUE;
11520 si->touch.draw_pressed = TRUE;
11522 for (i = 0; i < 2; i++)
11524 char *default_grid_button[6][2] =
11530 { "111222", " vv " },
11531 { "111222", " vv " }
11533 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11534 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11535 int min_xsize = MIN(6, grid_xsize);
11536 int min_ysize = MIN(6, grid_ysize);
11537 int startx = grid_xsize - min_xsize;
11538 int starty = grid_ysize - min_ysize;
11541 // virtual buttons grid can only be set to defaults if video is initialized
11542 // (this will be repeated if virtual buttons are not loaded from setup file)
11543 if (video.initialized)
11545 si->touch.grid_xsize[i] = grid_xsize;
11546 si->touch.grid_ysize[i] = grid_ysize;
11550 si->touch.grid_xsize[i] = -1;
11551 si->touch.grid_ysize[i] = -1;
11554 for (x = 0; x < MAX_GRID_XSIZE; x++)
11555 for (y = 0; y < MAX_GRID_YSIZE; y++)
11556 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11558 for (x = 0; x < min_xsize; x++)
11559 for (y = 0; y < min_ysize; y++)
11560 si->touch.grid_button[i][x][starty + y] =
11561 default_grid_button[y][0][x];
11563 for (x = 0; x < min_xsize; x++)
11564 for (y = 0; y < min_ysize; y++)
11565 si->touch.grid_button[i][startx + x][starty + y] =
11566 default_grid_button[y][1][x];
11569 si->touch.grid_initialized = video.initialized;
11571 si->touch.overlay_buttons = FALSE;
11573 si->editor.el_boulderdash = TRUE;
11574 si->editor.el_boulderdash_native = TRUE;
11575 si->editor.el_boulderdash_effects = TRUE;
11576 si->editor.el_emerald_mine = TRUE;
11577 si->editor.el_emerald_mine_club = TRUE;
11578 si->editor.el_more = TRUE;
11579 si->editor.el_sokoban = TRUE;
11580 si->editor.el_supaplex = TRUE;
11581 si->editor.el_diamond_caves = TRUE;
11582 si->editor.el_dx_boulderdash = TRUE;
11584 si->editor.el_mirror_magic = TRUE;
11585 si->editor.el_deflektor = TRUE;
11587 si->editor.el_chars = TRUE;
11588 si->editor.el_steel_chars = TRUE;
11590 si->editor.el_classic = TRUE;
11591 si->editor.el_custom = TRUE;
11593 si->editor.el_user_defined = FALSE;
11594 si->editor.el_dynamic = TRUE;
11596 si->editor.el_headlines = TRUE;
11598 si->editor.show_element_token = FALSE;
11600 si->editor.show_read_only_warning = TRUE;
11602 si->editor.use_template_for_new_levels = TRUE;
11604 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11605 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11606 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11607 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11608 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11610 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11611 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11612 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11613 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11614 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11616 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11617 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11618 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11619 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11620 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11621 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11623 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11624 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11625 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11627 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11628 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11629 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11630 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11632 for (i = 0; i < MAX_PLAYERS; i++)
11634 si->input[i].use_joystick = FALSE;
11635 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11636 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11637 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11638 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11639 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11640 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11641 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11642 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11643 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11644 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11645 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11646 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11647 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11648 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11649 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11652 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11653 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11654 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11655 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11657 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11658 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11659 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11660 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11661 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11662 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11663 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11665 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11667 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11668 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11669 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11671 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11672 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11673 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11675 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11676 si->internal.choose_from_top_leveldir = FALSE;
11677 si->internal.show_scaling_in_title = TRUE;
11678 si->internal.create_user_levelset = TRUE;
11679 si->internal.info_screens_from_main = FALSE;
11681 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11682 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11684 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11685 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11686 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11687 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11688 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11689 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11690 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11691 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11692 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11693 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11695 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11696 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11697 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11698 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11699 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11700 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11701 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11702 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11703 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11704 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11706 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11707 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11709 si->debug.show_frames_per_second = FALSE;
11711 si->debug.xsn_mode = AUTO;
11712 si->debug.xsn_percent = 0;
11714 si->options.verbose = FALSE;
11715 si->options.debug = FALSE;
11716 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11718 #if defined(PLATFORM_ANDROID)
11719 si->fullscreen = TRUE;
11720 si->touch.overlay_buttons = TRUE;
11723 setHideSetupEntry(&setup.debug.xsn_mode);
11726 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11728 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11731 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11733 si->player_uuid = NULL; // (will be set later)
11734 si->player_version = 1; // (will be set later)
11736 si->use_api_server = TRUE;
11737 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11738 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11739 si->ask_for_uploading_tapes = TRUE;
11740 si->ask_for_remaining_tapes = FALSE;
11741 si->provide_uploading_tapes = TRUE;
11742 si->ask_for_using_api_server = TRUE;
11743 si->has_remaining_tapes = FALSE;
11746 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11748 si->editor_cascade.el_bd = TRUE;
11749 si->editor_cascade.el_bd_native = TRUE;
11750 si->editor_cascade.el_bd_effects = FALSE;
11751 si->editor_cascade.el_em = TRUE;
11752 si->editor_cascade.el_emc = TRUE;
11753 si->editor_cascade.el_rnd = TRUE;
11754 si->editor_cascade.el_sb = TRUE;
11755 si->editor_cascade.el_sp = TRUE;
11756 si->editor_cascade.el_dc = TRUE;
11757 si->editor_cascade.el_dx = TRUE;
11759 si->editor_cascade.el_mm = TRUE;
11760 si->editor_cascade.el_df = TRUE;
11762 si->editor_cascade.el_chars = FALSE;
11763 si->editor_cascade.el_steel_chars = FALSE;
11764 si->editor_cascade.el_ce = FALSE;
11765 si->editor_cascade.el_ge = FALSE;
11766 si->editor_cascade.el_es = FALSE;
11767 si->editor_cascade.el_ref = FALSE;
11768 si->editor_cascade.el_user = FALSE;
11769 si->editor_cascade.el_dynamic = FALSE;
11772 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11774 static char *getHideSetupToken(void *setup_value)
11776 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11778 if (setup_value != NULL)
11779 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11781 return hide_setup_token;
11784 void setHideSetupEntry(void *setup_value)
11786 char *hide_setup_token = getHideSetupToken(setup_value);
11788 if (hide_setup_hash == NULL)
11789 hide_setup_hash = newSetupFileHash();
11791 if (setup_value != NULL)
11792 setHashEntry(hide_setup_hash, hide_setup_token, "");
11795 void removeHideSetupEntry(void *setup_value)
11797 char *hide_setup_token = getHideSetupToken(setup_value);
11799 if (setup_value != NULL)
11800 removeHashEntry(hide_setup_hash, hide_setup_token);
11803 boolean hideSetupEntry(void *setup_value)
11805 char *hide_setup_token = getHideSetupToken(setup_value);
11807 return (setup_value != NULL &&
11808 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11811 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11812 struct TokenInfo *token_info,
11813 int token_nr, char *token_text)
11815 char *token_hide_text = getStringCat2(token_text, ".hide");
11816 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11818 // set the value of this setup option in the setup option structure
11819 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11821 // check if this setup option should be hidden in the setup menu
11822 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11823 setHideSetupEntry(token_info[token_nr].value);
11825 free(token_hide_text);
11828 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11829 struct TokenInfo *token_info,
11832 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11833 token_info[token_nr].text);
11836 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11840 if (!setup_file_hash)
11843 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11844 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11846 setup.touch.grid_initialized = TRUE;
11847 for (i = 0; i < 2; i++)
11849 int grid_xsize = setup.touch.grid_xsize[i];
11850 int grid_ysize = setup.touch.grid_ysize[i];
11853 // if virtual buttons are not loaded from setup file, repeat initializing
11854 // virtual buttons grid with default values later when video is initialized
11855 if (grid_xsize == -1 ||
11858 setup.touch.grid_initialized = FALSE;
11863 for (y = 0; y < grid_ysize; y++)
11865 char token_string[MAX_LINE_LEN];
11867 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11869 char *value_string = getHashEntry(setup_file_hash, token_string);
11871 if (value_string == NULL)
11874 for (x = 0; x < grid_xsize; x++)
11876 char c = value_string[x];
11878 setup.touch.grid_button[i][x][y] =
11879 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11884 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11885 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11887 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11888 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11890 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11894 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11896 setup_input = setup.input[pnr];
11897 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11899 char full_token[100];
11901 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11902 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11905 setup.input[pnr] = setup_input;
11908 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11909 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11911 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11912 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11914 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11915 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11917 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11918 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11920 setHideRelatedSetupEntries();
11923 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11927 if (!setup_file_hash)
11930 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11931 setSetupInfo(auto_setup_tokens, i,
11932 getHashEntry(setup_file_hash,
11933 auto_setup_tokens[i].text));
11936 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11940 if (!setup_file_hash)
11943 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11944 setSetupInfo(server_setup_tokens, i,
11945 getHashEntry(setup_file_hash,
11946 server_setup_tokens[i].text));
11949 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11953 if (!setup_file_hash)
11956 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11957 setSetupInfo(editor_cascade_setup_tokens, i,
11958 getHashEntry(setup_file_hash,
11959 editor_cascade_setup_tokens[i].text));
11962 void LoadUserNames(void)
11964 int last_user_nr = user.nr;
11967 if (global.user_names != NULL)
11969 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11970 checked_free(global.user_names[i]);
11972 checked_free(global.user_names);
11975 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11977 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11981 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11983 if (setup_file_hash)
11985 char *player_name = getHashEntry(setup_file_hash, "player_name");
11987 global.user_names[i] = getFixedUserName(player_name);
11989 freeSetupFileHash(setup_file_hash);
11992 if (global.user_names[i] == NULL)
11993 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11996 user.nr = last_user_nr;
11999 void LoadSetupFromFilename(char *filename)
12001 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12003 if (setup_file_hash)
12005 decodeSetupFileHash_Default(setup_file_hash);
12007 freeSetupFileHash(setup_file_hash);
12011 Debug("setup", "using default setup values");
12015 static void LoadSetup_SpecialPostProcessing(void)
12017 char *player_name_new;
12019 // needed to work around problems with fixed length strings
12020 player_name_new = getFixedUserName(setup.player_name);
12021 free(setup.player_name);
12022 setup.player_name = player_name_new;
12024 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12025 if (setup.scroll_delay == FALSE)
12027 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12028 setup.scroll_delay = TRUE; // now always "on"
12031 // make sure that scroll delay value stays inside valid range
12032 setup.scroll_delay_value =
12033 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12036 void LoadSetup_Default(void)
12040 // always start with reliable default values
12041 setSetupInfoToDefaults(&setup);
12043 // try to load setup values from default setup file
12044 filename = getDefaultSetupFilename();
12046 if (fileExists(filename))
12047 LoadSetupFromFilename(filename);
12049 // try to load setup values from platform setup file
12050 filename = getPlatformSetupFilename();
12052 if (fileExists(filename))
12053 LoadSetupFromFilename(filename);
12055 // try to load setup values from user setup file
12056 filename = getSetupFilename();
12058 LoadSetupFromFilename(filename);
12060 LoadSetup_SpecialPostProcessing();
12063 void LoadSetup_AutoSetup(void)
12065 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12066 SetupFileHash *setup_file_hash = NULL;
12068 // always start with reliable default values
12069 setSetupInfoToDefaults_AutoSetup(&setup);
12071 setup_file_hash = loadSetupFileHash(filename);
12073 if (setup_file_hash)
12075 decodeSetupFileHash_AutoSetup(setup_file_hash);
12077 freeSetupFileHash(setup_file_hash);
12083 void LoadSetup_ServerSetup(void)
12085 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12086 SetupFileHash *setup_file_hash = NULL;
12088 // always start with reliable default values
12089 setSetupInfoToDefaults_ServerSetup(&setup);
12091 setup_file_hash = loadSetupFileHash(filename);
12093 if (setup_file_hash)
12095 decodeSetupFileHash_ServerSetup(setup_file_hash);
12097 freeSetupFileHash(setup_file_hash);
12102 if (setup.player_uuid == NULL)
12104 // player UUID does not yet exist in setup file
12105 setup.player_uuid = getStringCopy(getUUID());
12106 setup.player_version = 2;
12108 SaveSetup_ServerSetup();
12112 void LoadSetup_EditorCascade(void)
12114 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12115 SetupFileHash *setup_file_hash = NULL;
12117 // always start with reliable default values
12118 setSetupInfoToDefaults_EditorCascade(&setup);
12120 setup_file_hash = loadSetupFileHash(filename);
12122 if (setup_file_hash)
12124 decodeSetupFileHash_EditorCascade(setup_file_hash);
12126 freeSetupFileHash(setup_file_hash);
12132 void LoadSetup(void)
12134 LoadSetup_Default();
12135 LoadSetup_AutoSetup();
12136 LoadSetup_ServerSetup();
12137 LoadSetup_EditorCascade();
12140 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12141 char *mapping_line)
12143 char mapping_guid[MAX_LINE_LEN];
12144 char *mapping_start, *mapping_end;
12146 // get GUID from game controller mapping line: copy complete line
12147 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12148 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12150 // get GUID from game controller mapping line: cut after GUID part
12151 mapping_start = strchr(mapping_guid, ',');
12152 if (mapping_start != NULL)
12153 *mapping_start = '\0';
12155 // cut newline from game controller mapping line
12156 mapping_end = strchr(mapping_line, '\n');
12157 if (mapping_end != NULL)
12158 *mapping_end = '\0';
12160 // add mapping entry to game controller mappings hash
12161 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12164 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12169 if (!(file = fopen(filename, MODE_READ)))
12171 Warn("cannot read game controller mappings file '%s'", filename);
12176 while (!feof(file))
12178 char line[MAX_LINE_LEN];
12180 if (!fgets(line, MAX_LINE_LEN, file))
12183 addGameControllerMappingToHash(mappings_hash, line);
12189 void SaveSetup_Default(void)
12191 char *filename = getSetupFilename();
12195 InitUserDataDirectory();
12197 if (!(file = fopen(filename, MODE_WRITE)))
12199 Warn("cannot write setup file '%s'", filename);
12204 fprintFileHeader(file, SETUP_FILENAME);
12206 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12208 // just to make things nicer :)
12209 if (global_setup_tokens[i].value == &setup.multiple_users ||
12210 global_setup_tokens[i].value == &setup.sound ||
12211 global_setup_tokens[i].value == &setup.graphics_set ||
12212 global_setup_tokens[i].value == &setup.volume_simple ||
12213 global_setup_tokens[i].value == &setup.network_mode ||
12214 global_setup_tokens[i].value == &setup.touch.control_type ||
12215 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12216 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12217 fprintf(file, "\n");
12219 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12222 for (i = 0; i < 2; i++)
12224 int grid_xsize = setup.touch.grid_xsize[i];
12225 int grid_ysize = setup.touch.grid_ysize[i];
12228 fprintf(file, "\n");
12230 for (y = 0; y < grid_ysize; y++)
12232 char token_string[MAX_LINE_LEN];
12233 char value_string[MAX_LINE_LEN];
12235 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12237 for (x = 0; x < grid_xsize; x++)
12239 char c = setup.touch.grid_button[i][x][y];
12241 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12244 value_string[grid_xsize] = '\0';
12246 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12250 fprintf(file, "\n");
12251 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12252 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12254 fprintf(file, "\n");
12255 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12256 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12258 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12262 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12263 fprintf(file, "\n");
12265 setup_input = setup.input[pnr];
12266 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12267 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12270 fprintf(file, "\n");
12271 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12272 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12274 // (internal setup values not saved to user setup file)
12276 fprintf(file, "\n");
12277 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12278 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12279 setup.debug.xsn_mode != AUTO)
12280 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12282 fprintf(file, "\n");
12283 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12284 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12288 SetFilePermissions(filename, PERMS_PRIVATE);
12291 void SaveSetup_AutoSetup(void)
12293 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12297 InitUserDataDirectory();
12299 if (!(file = fopen(filename, MODE_WRITE)))
12301 Warn("cannot write auto setup file '%s'", filename);
12308 fprintFileHeader(file, AUTOSETUP_FILENAME);
12310 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12311 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12315 SetFilePermissions(filename, PERMS_PRIVATE);
12320 void SaveSetup_ServerSetup(void)
12322 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12326 InitUserDataDirectory();
12328 if (!(file = fopen(filename, MODE_WRITE)))
12330 Warn("cannot write server setup file '%s'", filename);
12337 fprintFileHeader(file, SERVERSETUP_FILENAME);
12339 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12341 // just to make things nicer :)
12342 if (server_setup_tokens[i].value == &setup.use_api_server)
12343 fprintf(file, "\n");
12345 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12350 SetFilePermissions(filename, PERMS_PRIVATE);
12355 void SaveSetup_EditorCascade(void)
12357 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12361 InitUserDataDirectory();
12363 if (!(file = fopen(filename, MODE_WRITE)))
12365 Warn("cannot write editor cascade state file '%s'", filename);
12372 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12374 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12375 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12379 SetFilePermissions(filename, PERMS_PRIVATE);
12384 void SaveSetup(void)
12386 SaveSetup_Default();
12387 SaveSetup_AutoSetup();
12388 SaveSetup_ServerSetup();
12389 SaveSetup_EditorCascade();
12392 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12397 if (!(file = fopen(filename, MODE_WRITE)))
12399 Warn("cannot write game controller mappings file '%s'", filename);
12404 BEGIN_HASH_ITERATION(mappings_hash, itr)
12406 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12408 END_HASH_ITERATION(mappings_hash, itr)
12413 void SaveSetup_AddGameControllerMapping(char *mapping)
12415 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12416 SetupFileHash *mappings_hash = newSetupFileHash();
12418 InitUserDataDirectory();
12420 // load existing personal game controller mappings
12421 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12423 // add new mapping to personal game controller mappings
12424 addGameControllerMappingToHash(mappings_hash, mapping);
12426 // save updated personal game controller mappings
12427 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12429 freeSetupFileHash(mappings_hash);
12433 void LoadCustomElementDescriptions(void)
12435 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12436 SetupFileHash *setup_file_hash;
12439 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12441 if (element_info[i].custom_description != NULL)
12443 free(element_info[i].custom_description);
12444 element_info[i].custom_description = NULL;
12448 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12451 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12453 char *token = getStringCat2(element_info[i].token_name, ".name");
12454 char *value = getHashEntry(setup_file_hash, token);
12457 element_info[i].custom_description = getStringCopy(value);
12462 freeSetupFileHash(setup_file_hash);
12465 static int getElementFromToken(char *token)
12467 char *value = getHashEntry(element_token_hash, token);
12470 return atoi(value);
12472 Warn("unknown element token '%s'", token);
12474 return EL_UNDEFINED;
12477 void FreeGlobalAnimEventInfo(void)
12479 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12481 if (gaei->event_list == NULL)
12486 for (i = 0; i < gaei->num_event_lists; i++)
12488 checked_free(gaei->event_list[i]->event_value);
12489 checked_free(gaei->event_list[i]);
12492 checked_free(gaei->event_list);
12494 gaei->event_list = NULL;
12495 gaei->num_event_lists = 0;
12498 static int AddGlobalAnimEventList(void)
12500 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12501 int list_pos = gaei->num_event_lists++;
12503 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12504 sizeof(struct GlobalAnimEventListInfo *));
12506 gaei->event_list[list_pos] =
12507 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12509 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12511 gaeli->event_value = NULL;
12512 gaeli->num_event_values = 0;
12517 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12519 // do not add empty global animation events
12520 if (event_value == ANIM_EVENT_NONE)
12523 // if list position is undefined, create new list
12524 if (list_pos == ANIM_EVENT_UNDEFINED)
12525 list_pos = AddGlobalAnimEventList();
12527 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12528 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12529 int value_pos = gaeli->num_event_values++;
12531 gaeli->event_value = checked_realloc(gaeli->event_value,
12532 gaeli->num_event_values * sizeof(int *));
12534 gaeli->event_value[value_pos] = event_value;
12539 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12541 if (list_pos == ANIM_EVENT_UNDEFINED)
12542 return ANIM_EVENT_NONE;
12544 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12545 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12547 return gaeli->event_value[value_pos];
12550 int GetGlobalAnimEventValueCount(int list_pos)
12552 if (list_pos == ANIM_EVENT_UNDEFINED)
12555 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12556 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12558 return gaeli->num_event_values;
12561 // This function checks if a string <s> of the format "string1, string2, ..."
12562 // exactly contains a string <s_contained>.
12564 static boolean string_has_parameter(char *s, char *s_contained)
12568 if (s == NULL || s_contained == NULL)
12571 if (strlen(s_contained) > strlen(s))
12574 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12576 char next_char = s[strlen(s_contained)];
12578 // check if next character is delimiter or whitespace
12579 if (next_char == ',' || next_char == '\0' ||
12580 next_char == ' ' || next_char == '\t')
12584 // check if string contains another parameter string after a comma
12585 substring = strchr(s, ',');
12586 if (substring == NULL) // string does not contain a comma
12589 // advance string pointer to next character after the comma
12592 // skip potential whitespaces after the comma
12593 while (*substring == ' ' || *substring == '\t')
12596 return string_has_parameter(substring, s_contained);
12599 static int get_anim_parameter_value_ce(char *s)
12602 char *pattern_1 = "ce_change:custom_";
12603 char *pattern_2 = ".page_";
12604 int pattern_1_len = strlen(pattern_1);
12605 char *matching_char = strstr(s_ptr, pattern_1);
12606 int result = ANIM_EVENT_NONE;
12608 if (matching_char == NULL)
12609 return ANIM_EVENT_NONE;
12611 result = ANIM_EVENT_CE_CHANGE;
12613 s_ptr = matching_char + pattern_1_len;
12615 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12616 if (*s_ptr >= '0' && *s_ptr <= '9')
12618 int gic_ce_nr = (*s_ptr++ - '0');
12620 if (*s_ptr >= '0' && *s_ptr <= '9')
12622 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12624 if (*s_ptr >= '0' && *s_ptr <= '9')
12625 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12628 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12629 return ANIM_EVENT_NONE;
12631 // custom element stored as 0 to 255
12634 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12638 // invalid custom element number specified
12640 return ANIM_EVENT_NONE;
12643 // check for change page number ("page_X" or "page_XX") (optional)
12644 if (strPrefix(s_ptr, pattern_2))
12646 s_ptr += strlen(pattern_2);
12648 if (*s_ptr >= '0' && *s_ptr <= '9')
12650 int gic_page_nr = (*s_ptr++ - '0');
12652 if (*s_ptr >= '0' && *s_ptr <= '9')
12653 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12655 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12656 return ANIM_EVENT_NONE;
12658 // change page stored as 1 to 32 (0 means "all change pages")
12660 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12664 // invalid animation part number specified
12666 return ANIM_EVENT_NONE;
12670 // discard result if next character is neither delimiter nor whitespace
12671 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12672 *s_ptr == ' ' || *s_ptr == '\t'))
12673 return ANIM_EVENT_NONE;
12678 static int get_anim_parameter_value(char *s)
12680 int event_value[] =
12688 char *pattern_1[] =
12696 char *pattern_2 = ".part_";
12697 char *matching_char = NULL;
12699 int pattern_1_len = 0;
12700 int result = ANIM_EVENT_NONE;
12703 result = get_anim_parameter_value_ce(s);
12705 if (result != ANIM_EVENT_NONE)
12708 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12710 matching_char = strstr(s_ptr, pattern_1[i]);
12711 pattern_1_len = strlen(pattern_1[i]);
12712 result = event_value[i];
12714 if (matching_char != NULL)
12718 if (matching_char == NULL)
12719 return ANIM_EVENT_NONE;
12721 s_ptr = matching_char + pattern_1_len;
12723 // check for main animation number ("anim_X" or "anim_XX")
12724 if (*s_ptr >= '0' && *s_ptr <= '9')
12726 int gic_anim_nr = (*s_ptr++ - '0');
12728 if (*s_ptr >= '0' && *s_ptr <= '9')
12729 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12731 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12732 return ANIM_EVENT_NONE;
12734 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12738 // invalid main animation number specified
12740 return ANIM_EVENT_NONE;
12743 // check for animation part number ("part_X" or "part_XX") (optional)
12744 if (strPrefix(s_ptr, pattern_2))
12746 s_ptr += strlen(pattern_2);
12748 if (*s_ptr >= '0' && *s_ptr <= '9')
12750 int gic_part_nr = (*s_ptr++ - '0');
12752 if (*s_ptr >= '0' && *s_ptr <= '9')
12753 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12755 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12756 return ANIM_EVENT_NONE;
12758 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12762 // invalid animation part number specified
12764 return ANIM_EVENT_NONE;
12768 // discard result if next character is neither delimiter nor whitespace
12769 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12770 *s_ptr == ' ' || *s_ptr == '\t'))
12771 return ANIM_EVENT_NONE;
12776 static int get_anim_parameter_values(char *s)
12778 int list_pos = ANIM_EVENT_UNDEFINED;
12779 int event_value = ANIM_EVENT_DEFAULT;
12781 if (string_has_parameter(s, "any"))
12782 event_value |= ANIM_EVENT_ANY;
12784 if (string_has_parameter(s, "click:self") ||
12785 string_has_parameter(s, "click") ||
12786 string_has_parameter(s, "self"))
12787 event_value |= ANIM_EVENT_SELF;
12789 if (string_has_parameter(s, "unclick:any"))
12790 event_value |= ANIM_EVENT_UNCLICK_ANY;
12792 // if animation event found, add it to global animation event list
12793 if (event_value != ANIM_EVENT_NONE)
12794 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12798 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12799 event_value = get_anim_parameter_value(s);
12801 // if animation event found, add it to global animation event list
12802 if (event_value != ANIM_EVENT_NONE)
12803 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12805 // continue with next part of the string, starting with next comma
12806 s = strchr(s + 1, ',');
12812 static int get_anim_action_parameter_value(char *token)
12814 // check most common default case first to massively speed things up
12815 if (strEqual(token, ARG_UNDEFINED))
12816 return ANIM_EVENT_ACTION_NONE;
12818 int result = getImageIDFromToken(token);
12822 char *gfx_token = getStringCat2("gfx.", token);
12824 result = getImageIDFromToken(gfx_token);
12826 checked_free(gfx_token);
12831 Key key = getKeyFromX11KeyName(token);
12833 if (key != KSYM_UNDEFINED)
12834 result = -(int)key;
12841 result = get_hash_from_string(token); // unsigned int => int
12842 result = ABS(result); // may be negative now
12843 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12845 setHashEntry(anim_url_hash, int2str(result, 0), token);
12850 result = ANIM_EVENT_ACTION_NONE;
12855 int get_parameter_value(char *value_raw, char *suffix, int type)
12857 char *value = getStringToLower(value_raw);
12858 int result = 0; // probably a save default value
12860 if (strEqual(suffix, ".direction"))
12862 result = (strEqual(value, "left") ? MV_LEFT :
12863 strEqual(value, "right") ? MV_RIGHT :
12864 strEqual(value, "up") ? MV_UP :
12865 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12867 else if (strEqual(suffix, ".position"))
12869 result = (strEqual(value, "left") ? POS_LEFT :
12870 strEqual(value, "right") ? POS_RIGHT :
12871 strEqual(value, "top") ? POS_TOP :
12872 strEqual(value, "upper") ? POS_UPPER :
12873 strEqual(value, "middle") ? POS_MIDDLE :
12874 strEqual(value, "lower") ? POS_LOWER :
12875 strEqual(value, "bottom") ? POS_BOTTOM :
12876 strEqual(value, "any") ? POS_ANY :
12877 strEqual(value, "ce") ? POS_CE :
12878 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12879 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12881 else if (strEqual(suffix, ".align"))
12883 result = (strEqual(value, "left") ? ALIGN_LEFT :
12884 strEqual(value, "right") ? ALIGN_RIGHT :
12885 strEqual(value, "center") ? ALIGN_CENTER :
12886 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12888 else if (strEqual(suffix, ".valign"))
12890 result = (strEqual(value, "top") ? VALIGN_TOP :
12891 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12892 strEqual(value, "middle") ? VALIGN_MIDDLE :
12893 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12895 else if (strEqual(suffix, ".anim_mode"))
12897 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12898 string_has_parameter(value, "loop") ? ANIM_LOOP :
12899 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12900 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12901 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12902 string_has_parameter(value, "random") ? ANIM_RANDOM :
12903 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12904 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12905 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12906 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12907 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12908 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12909 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12910 string_has_parameter(value, "all") ? ANIM_ALL :
12911 string_has_parameter(value, "tiled") ? ANIM_TILED :
12912 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12915 if (string_has_parameter(value, "once"))
12916 result |= ANIM_ONCE;
12918 if (string_has_parameter(value, "reverse"))
12919 result |= ANIM_REVERSE;
12921 if (string_has_parameter(value, "opaque_player"))
12922 result |= ANIM_OPAQUE_PLAYER;
12924 if (string_has_parameter(value, "static_panel"))
12925 result |= ANIM_STATIC_PANEL;
12927 else if (strEqual(suffix, ".init_event") ||
12928 strEqual(suffix, ".anim_event"))
12930 result = get_anim_parameter_values(value);
12932 else if (strEqual(suffix, ".init_delay_action") ||
12933 strEqual(suffix, ".anim_delay_action") ||
12934 strEqual(suffix, ".post_delay_action") ||
12935 strEqual(suffix, ".init_event_action") ||
12936 strEqual(suffix, ".anim_event_action"))
12938 result = get_anim_action_parameter_value(value_raw);
12940 else if (strEqual(suffix, ".class"))
12942 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12943 get_hash_from_string(value));
12945 else if (strEqual(suffix, ".style"))
12947 result = STYLE_DEFAULT;
12949 if (string_has_parameter(value, "accurate_borders"))
12950 result |= STYLE_ACCURATE_BORDERS;
12952 if (string_has_parameter(value, "inner_corners"))
12953 result |= STYLE_INNER_CORNERS;
12955 if (string_has_parameter(value, "reverse"))
12956 result |= STYLE_REVERSE;
12958 if (string_has_parameter(value, "leftmost_position"))
12959 result |= STYLE_LEFTMOST_POSITION;
12961 if (string_has_parameter(value, "block_clicks"))
12962 result |= STYLE_BLOCK;
12964 if (string_has_parameter(value, "passthrough_clicks"))
12965 result |= STYLE_PASSTHROUGH;
12967 if (string_has_parameter(value, "multiple_actions"))
12968 result |= STYLE_MULTIPLE_ACTIONS;
12970 if (string_has_parameter(value, "consume_ce_event"))
12971 result |= STYLE_CONSUME_CE_EVENT;
12973 else if (strEqual(suffix, ".fade_mode"))
12975 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12976 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12977 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12978 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12979 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12980 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12981 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12982 FADE_MODE_DEFAULT);
12984 else if (strEqual(suffix, ".auto_delay_unit"))
12986 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12987 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12988 AUTO_DELAY_UNIT_DEFAULT);
12990 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12992 result = gfx.get_font_from_token_function(value);
12994 else // generic parameter of type integer or boolean
12996 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12997 type == TYPE_INTEGER ? get_integer_from_string(value) :
12998 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12999 ARG_UNDEFINED_VALUE);
13007 static int get_token_parameter_value(char *token, char *value_raw)
13011 if (token == NULL || value_raw == NULL)
13012 return ARG_UNDEFINED_VALUE;
13014 suffix = strrchr(token, '.');
13015 if (suffix == NULL)
13018 if (strEqual(suffix, ".element"))
13019 return getElementFromToken(value_raw);
13021 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13022 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13025 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13026 boolean ignore_defaults)
13030 for (i = 0; image_config_vars[i].token != NULL; i++)
13032 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13034 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13035 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13039 *image_config_vars[i].value =
13040 get_token_parameter_value(image_config_vars[i].token, value);
13044 void InitMenuDesignSettings_Static(void)
13046 // always start with reliable default values from static default config
13047 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13050 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13054 // the following initializes hierarchical values from static configuration
13056 // special case: initialize "ARG_DEFAULT" values in static default config
13057 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13058 titlescreen_initial_first_default.fade_mode =
13059 title_initial_first_default.fade_mode;
13060 titlescreen_initial_first_default.fade_delay =
13061 title_initial_first_default.fade_delay;
13062 titlescreen_initial_first_default.post_delay =
13063 title_initial_first_default.post_delay;
13064 titlescreen_initial_first_default.auto_delay =
13065 title_initial_first_default.auto_delay;
13066 titlescreen_initial_first_default.auto_delay_unit =
13067 title_initial_first_default.auto_delay_unit;
13068 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13069 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13070 titlescreen_first_default.post_delay = title_first_default.post_delay;
13071 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13072 titlescreen_first_default.auto_delay_unit =
13073 title_first_default.auto_delay_unit;
13074 titlemessage_initial_first_default.fade_mode =
13075 title_initial_first_default.fade_mode;
13076 titlemessage_initial_first_default.fade_delay =
13077 title_initial_first_default.fade_delay;
13078 titlemessage_initial_first_default.post_delay =
13079 title_initial_first_default.post_delay;
13080 titlemessage_initial_first_default.auto_delay =
13081 title_initial_first_default.auto_delay;
13082 titlemessage_initial_first_default.auto_delay_unit =
13083 title_initial_first_default.auto_delay_unit;
13084 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13085 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13086 titlemessage_first_default.post_delay = title_first_default.post_delay;
13087 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13088 titlemessage_first_default.auto_delay_unit =
13089 title_first_default.auto_delay_unit;
13091 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13092 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13093 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13094 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13095 titlescreen_initial_default.auto_delay_unit =
13096 title_initial_default.auto_delay_unit;
13097 titlescreen_default.fade_mode = title_default.fade_mode;
13098 titlescreen_default.fade_delay = title_default.fade_delay;
13099 titlescreen_default.post_delay = title_default.post_delay;
13100 titlescreen_default.auto_delay = title_default.auto_delay;
13101 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13102 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13103 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13104 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13105 titlemessage_initial_default.auto_delay_unit =
13106 title_initial_default.auto_delay_unit;
13107 titlemessage_default.fade_mode = title_default.fade_mode;
13108 titlemessage_default.fade_delay = title_default.fade_delay;
13109 titlemessage_default.post_delay = title_default.post_delay;
13110 titlemessage_default.auto_delay = title_default.auto_delay;
13111 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13113 // special case: initialize "ARG_DEFAULT" values in static default config
13114 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13115 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13117 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13118 titlescreen_first[i] = titlescreen_first_default;
13119 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13120 titlemessage_first[i] = titlemessage_first_default;
13122 titlescreen_initial[i] = titlescreen_initial_default;
13123 titlescreen[i] = titlescreen_default;
13124 titlemessage_initial[i] = titlemessage_initial_default;
13125 titlemessage[i] = titlemessage_default;
13128 // special case: initialize "ARG_DEFAULT" values in static default config
13129 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13130 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13132 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13135 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13136 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13137 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13140 // special case: initialize "ARG_DEFAULT" values in static default config
13141 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13142 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13144 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13145 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13146 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13148 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13151 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13155 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13159 struct XY *dst, *src;
13161 game_buttons_xy[] =
13163 { &game.button.save, &game.button.stop },
13164 { &game.button.pause2, &game.button.pause },
13165 { &game.button.load, &game.button.play },
13166 { &game.button.undo, &game.button.stop },
13167 { &game.button.redo, &game.button.play },
13173 // special case: initialize later added SETUP list size from LEVELS value
13174 if (menu.list_size[GAME_MODE_SETUP] == -1)
13175 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13177 // set default position for snapshot buttons to stop/pause/play buttons
13178 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13179 if ((*game_buttons_xy[i].dst).x == -1 &&
13180 (*game_buttons_xy[i].dst).y == -1)
13181 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13183 // --------------------------------------------------------------------------
13184 // dynamic viewports (including playfield margins, borders and alignments)
13185 // --------------------------------------------------------------------------
13187 // dynamic viewports currently only supported for landscape mode
13188 int display_width = MAX(video.display_width, video.display_height);
13189 int display_height = MIN(video.display_width, video.display_height);
13191 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13193 struct RectWithBorder *vp_window = &viewport.window[i];
13194 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13195 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13196 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13197 boolean dynamic_window_width = (vp_window->min_width != -1);
13198 boolean dynamic_window_height = (vp_window->min_height != -1);
13199 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13200 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13202 // adjust window size if min/max width/height is specified
13204 if (vp_window->min_width != -1)
13206 int window_width = display_width;
13208 // when using static window height, use aspect ratio of display
13209 if (vp_window->min_height == -1)
13210 window_width = vp_window->height * display_width / display_height;
13212 vp_window->width = MAX(vp_window->min_width, window_width);
13215 if (vp_window->min_height != -1)
13217 int window_height = display_height;
13219 // when using static window width, use aspect ratio of display
13220 if (vp_window->min_width == -1)
13221 window_height = vp_window->width * display_height / display_width;
13223 vp_window->height = MAX(vp_window->min_height, window_height);
13226 if (vp_window->max_width != -1)
13227 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13229 if (vp_window->max_height != -1)
13230 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13232 int playfield_width = vp_window->width;
13233 int playfield_height = vp_window->height;
13235 // adjust playfield size and position according to specified margins
13237 playfield_width -= vp_playfield->margin_left;
13238 playfield_width -= vp_playfield->margin_right;
13240 playfield_height -= vp_playfield->margin_top;
13241 playfield_height -= vp_playfield->margin_bottom;
13243 // adjust playfield size if min/max width/height is specified
13245 if (vp_playfield->min_width != -1)
13246 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13248 if (vp_playfield->min_height != -1)
13249 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13251 if (vp_playfield->max_width != -1)
13252 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13254 if (vp_playfield->max_height != -1)
13255 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13257 // adjust playfield position according to specified alignment
13259 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13260 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13261 else if (vp_playfield->align == ALIGN_CENTER)
13262 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13263 else if (vp_playfield->align == ALIGN_RIGHT)
13264 vp_playfield->x += playfield_width - vp_playfield->width;
13266 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13267 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13268 else if (vp_playfield->valign == VALIGN_MIDDLE)
13269 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13270 else if (vp_playfield->valign == VALIGN_BOTTOM)
13271 vp_playfield->y += playfield_height - vp_playfield->height;
13273 vp_playfield->x += vp_playfield->margin_left;
13274 vp_playfield->y += vp_playfield->margin_top;
13276 // adjust individual playfield borders if only default border is specified
13278 if (vp_playfield->border_left == -1)
13279 vp_playfield->border_left = vp_playfield->border_size;
13280 if (vp_playfield->border_right == -1)
13281 vp_playfield->border_right = vp_playfield->border_size;
13282 if (vp_playfield->border_top == -1)
13283 vp_playfield->border_top = vp_playfield->border_size;
13284 if (vp_playfield->border_bottom == -1)
13285 vp_playfield->border_bottom = vp_playfield->border_size;
13287 // set dynamic playfield borders if borders are specified as undefined
13288 // (but only if window size was dynamic and playfield size was static)
13290 if (dynamic_window_width && !dynamic_playfield_width)
13292 if (vp_playfield->border_left == -1)
13294 vp_playfield->border_left = (vp_playfield->x -
13295 vp_playfield->margin_left);
13296 vp_playfield->x -= vp_playfield->border_left;
13297 vp_playfield->width += vp_playfield->border_left;
13300 if (vp_playfield->border_right == -1)
13302 vp_playfield->border_right = (vp_window->width -
13304 vp_playfield->width -
13305 vp_playfield->margin_right);
13306 vp_playfield->width += vp_playfield->border_right;
13310 if (dynamic_window_height && !dynamic_playfield_height)
13312 if (vp_playfield->border_top == -1)
13314 vp_playfield->border_top = (vp_playfield->y -
13315 vp_playfield->margin_top);
13316 vp_playfield->y -= vp_playfield->border_top;
13317 vp_playfield->height += vp_playfield->border_top;
13320 if (vp_playfield->border_bottom == -1)
13322 vp_playfield->border_bottom = (vp_window->height -
13324 vp_playfield->height -
13325 vp_playfield->margin_bottom);
13326 vp_playfield->height += vp_playfield->border_bottom;
13330 // adjust playfield size to be a multiple of a defined alignment tile size
13332 int align_size = vp_playfield->align_size;
13333 int playfield_xtiles = vp_playfield->width / align_size;
13334 int playfield_ytiles = vp_playfield->height / align_size;
13335 int playfield_width_corrected = playfield_xtiles * align_size;
13336 int playfield_height_corrected = playfield_ytiles * align_size;
13337 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13338 i == GFX_SPECIAL_ARG_EDITOR);
13340 if (is_playfield_mode &&
13341 dynamic_playfield_width &&
13342 vp_playfield->width != playfield_width_corrected)
13344 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13346 vp_playfield->width = playfield_width_corrected;
13348 if (vp_playfield->align == ALIGN_LEFT)
13350 vp_playfield->border_left += playfield_xdiff;
13352 else if (vp_playfield->align == ALIGN_RIGHT)
13354 vp_playfield->border_right += playfield_xdiff;
13356 else if (vp_playfield->align == ALIGN_CENTER)
13358 int border_left_diff = playfield_xdiff / 2;
13359 int border_right_diff = playfield_xdiff - border_left_diff;
13361 vp_playfield->border_left += border_left_diff;
13362 vp_playfield->border_right += border_right_diff;
13366 if (is_playfield_mode &&
13367 dynamic_playfield_height &&
13368 vp_playfield->height != playfield_height_corrected)
13370 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13372 vp_playfield->height = playfield_height_corrected;
13374 if (vp_playfield->valign == VALIGN_TOP)
13376 vp_playfield->border_top += playfield_ydiff;
13378 else if (vp_playfield->align == VALIGN_BOTTOM)
13380 vp_playfield->border_right += playfield_ydiff;
13382 else if (vp_playfield->align == VALIGN_MIDDLE)
13384 int border_top_diff = playfield_ydiff / 2;
13385 int border_bottom_diff = playfield_ydiff - border_top_diff;
13387 vp_playfield->border_top += border_top_diff;
13388 vp_playfield->border_bottom += border_bottom_diff;
13392 // adjust door positions according to specified alignment
13394 for (j = 0; j < 2; j++)
13396 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13398 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13399 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13400 else if (vp_door->align == ALIGN_CENTER)
13401 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13402 else if (vp_door->align == ALIGN_RIGHT)
13403 vp_door->x += vp_window->width - vp_door->width;
13405 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13406 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13407 else if (vp_door->valign == VALIGN_MIDDLE)
13408 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13409 else if (vp_door->valign == VALIGN_BOTTOM)
13410 vp_door->y += vp_window->height - vp_door->height;
13415 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13419 struct XYTileSize *dst, *src;
13422 editor_buttons_xy[] =
13425 &editor.button.element_left, &editor.palette.element_left,
13426 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13429 &editor.button.element_middle, &editor.palette.element_middle,
13430 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13433 &editor.button.element_right, &editor.palette.element_right,
13434 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13441 // set default position for element buttons to element graphics
13442 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13444 if ((*editor_buttons_xy[i].dst).x == -1 &&
13445 (*editor_buttons_xy[i].dst).y == -1)
13447 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13449 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13451 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13455 // adjust editor palette rows and columns if specified to be dynamic
13457 if (editor.palette.cols == -1)
13459 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13460 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13461 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13463 editor.palette.cols = (vp_width - sc_width) / bt_width;
13465 if (editor.palette.x == -1)
13467 int palette_width = editor.palette.cols * bt_width + sc_width;
13469 editor.palette.x = (vp_width - palette_width) / 2;
13473 if (editor.palette.rows == -1)
13475 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13476 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13477 int tx_height = getFontHeight(FONT_TEXT_2);
13479 editor.palette.rows = (vp_height - tx_height) / bt_height;
13481 if (editor.palette.y == -1)
13483 int palette_height = editor.palette.rows * bt_height + tx_height;
13485 editor.palette.y = (vp_height - palette_height) / 2;
13490 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13491 boolean initialize)
13493 // special case: check if network and preview player positions are redefined,
13494 // to compare this later against the main menu level preview being redefined
13495 struct TokenIntPtrInfo menu_config_players[] =
13497 { "main.network_players.x", &menu.main.network_players.redefined },
13498 { "main.network_players.y", &menu.main.network_players.redefined },
13499 { "main.preview_players.x", &menu.main.preview_players.redefined },
13500 { "main.preview_players.y", &menu.main.preview_players.redefined },
13501 { "preview.x", &preview.redefined },
13502 { "preview.y", &preview.redefined }
13508 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13509 *menu_config_players[i].value = FALSE;
13513 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13514 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13515 *menu_config_players[i].value = TRUE;
13519 static void InitMenuDesignSettings_PreviewPlayers(void)
13521 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13524 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13526 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13529 static void LoadMenuDesignSettingsFromFilename(char *filename)
13531 static struct TitleFadingInfo tfi;
13532 static struct TitleMessageInfo tmi;
13533 static struct TokenInfo title_tokens[] =
13535 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13536 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13537 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13538 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13539 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13543 static struct TokenInfo titlemessage_tokens[] =
13545 { TYPE_INTEGER, &tmi.x, ".x" },
13546 { TYPE_INTEGER, &tmi.y, ".y" },
13547 { TYPE_INTEGER, &tmi.width, ".width" },
13548 { TYPE_INTEGER, &tmi.height, ".height" },
13549 { TYPE_INTEGER, &tmi.chars, ".chars" },
13550 { TYPE_INTEGER, &tmi.lines, ".lines" },
13551 { TYPE_INTEGER, &tmi.align, ".align" },
13552 { TYPE_INTEGER, &tmi.valign, ".valign" },
13553 { TYPE_INTEGER, &tmi.font, ".font" },
13554 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13555 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13556 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13557 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13558 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13559 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13560 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13561 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13562 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13568 struct TitleFadingInfo *info;
13573 // initialize first titles from "enter screen" definitions, if defined
13574 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13575 { &title_first_default, "menu.enter_screen.TITLE" },
13577 // initialize title screens from "next screen" definitions, if defined
13578 { &title_initial_default, "menu.next_screen.TITLE" },
13579 { &title_default, "menu.next_screen.TITLE" },
13585 struct TitleMessageInfo *array;
13588 titlemessage_arrays[] =
13590 // initialize first titles from "enter screen" definitions, if defined
13591 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13592 { titlescreen_first, "menu.enter_screen.TITLE" },
13593 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13594 { titlemessage_first, "menu.enter_screen.TITLE" },
13596 // initialize titles from "next screen" definitions, if defined
13597 { titlescreen_initial, "menu.next_screen.TITLE" },
13598 { titlescreen, "menu.next_screen.TITLE" },
13599 { titlemessage_initial, "menu.next_screen.TITLE" },
13600 { titlemessage, "menu.next_screen.TITLE" },
13602 // overwrite titles with title definitions, if defined
13603 { titlescreen_initial_first, "[title_initial]" },
13604 { titlescreen_first, "[title]" },
13605 { titlemessage_initial_first, "[title_initial]" },
13606 { titlemessage_first, "[title]" },
13608 { titlescreen_initial, "[title_initial]" },
13609 { titlescreen, "[title]" },
13610 { titlemessage_initial, "[title_initial]" },
13611 { titlemessage, "[title]" },
13613 // overwrite titles with title screen/message definitions, if defined
13614 { titlescreen_initial_first, "[titlescreen_initial]" },
13615 { titlescreen_first, "[titlescreen]" },
13616 { titlemessage_initial_first, "[titlemessage_initial]" },
13617 { titlemessage_first, "[titlemessage]" },
13619 { titlescreen_initial, "[titlescreen_initial]" },
13620 { titlescreen, "[titlescreen]" },
13621 { titlemessage_initial, "[titlemessage_initial]" },
13622 { titlemessage, "[titlemessage]" },
13626 SetupFileHash *setup_file_hash;
13629 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13632 // the following initializes hierarchical values from dynamic configuration
13634 // special case: initialize with default values that may be overwritten
13635 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13636 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13638 struct TokenIntPtrInfo menu_config[] =
13640 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13641 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13642 { "menu.list_size", &menu.list_size[i] }
13645 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13647 char *token = menu_config[j].token;
13648 char *value = getHashEntry(setup_file_hash, token);
13651 *menu_config[j].value = get_integer_from_string(value);
13655 // special case: initialize with default values that may be overwritten
13656 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13657 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13659 struct TokenIntPtrInfo menu_config[] =
13661 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13662 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13663 { "menu.list_size.INFO", &menu.list_size_info[i] },
13664 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13665 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13668 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13670 char *token = menu_config[j].token;
13671 char *value = getHashEntry(setup_file_hash, token);
13674 *menu_config[j].value = get_integer_from_string(value);
13678 // special case: initialize with default values that may be overwritten
13679 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13680 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13682 struct TokenIntPtrInfo menu_config[] =
13684 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13685 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13688 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13690 char *token = menu_config[j].token;
13691 char *value = getHashEntry(setup_file_hash, token);
13694 *menu_config[j].value = get_integer_from_string(value);
13698 // special case: initialize with default values that may be overwritten
13699 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13700 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13702 struct TokenIntPtrInfo menu_config[] =
13704 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13705 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13706 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13707 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13708 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13709 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13710 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13711 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13712 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13713 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13716 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13718 char *token = menu_config[j].token;
13719 char *value = getHashEntry(setup_file_hash, token);
13722 *menu_config[j].value = get_integer_from_string(value);
13726 // special case: initialize with default values that may be overwritten
13727 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13728 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13730 struct TokenIntPtrInfo menu_config[] =
13732 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13733 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13734 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13735 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13736 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13737 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13738 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13739 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13740 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13743 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13745 char *token = menu_config[j].token;
13746 char *value = getHashEntry(setup_file_hash, token);
13749 *menu_config[j].value = get_token_parameter_value(token, value);
13753 // special case: initialize with default values that may be overwritten
13754 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13755 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13759 char *token_prefix;
13760 struct RectWithBorder *struct_ptr;
13764 { "viewport.window", &viewport.window[i] },
13765 { "viewport.playfield", &viewport.playfield[i] },
13766 { "viewport.door_1", &viewport.door_1[i] },
13767 { "viewport.door_2", &viewport.door_2[i] }
13770 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13772 struct TokenIntPtrInfo vp_config[] =
13774 { ".x", &vp_struct[j].struct_ptr->x },
13775 { ".y", &vp_struct[j].struct_ptr->y },
13776 { ".width", &vp_struct[j].struct_ptr->width },
13777 { ".height", &vp_struct[j].struct_ptr->height },
13778 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13779 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13780 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13781 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13782 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13783 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13784 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13785 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13786 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13787 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13788 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13789 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13790 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13791 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13792 { ".align", &vp_struct[j].struct_ptr->align },
13793 { ".valign", &vp_struct[j].struct_ptr->valign }
13796 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13798 char *token = getStringCat2(vp_struct[j].token_prefix,
13799 vp_config[k].token);
13800 char *value = getHashEntry(setup_file_hash, token);
13803 *vp_config[k].value = get_token_parameter_value(token, value);
13810 // special case: initialize with default values that may be overwritten
13811 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13812 for (i = 0; title_info[i].info != NULL; i++)
13814 struct TitleFadingInfo *info = title_info[i].info;
13815 char *base_token = title_info[i].text;
13817 for (j = 0; title_tokens[j].type != -1; j++)
13819 char *token = getStringCat2(base_token, title_tokens[j].text);
13820 char *value = getHashEntry(setup_file_hash, token);
13824 int parameter_value = get_token_parameter_value(token, value);
13828 *(int *)title_tokens[j].value = (int)parameter_value;
13837 // special case: initialize with default values that may be overwritten
13838 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13839 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13841 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13842 char *base_token = titlemessage_arrays[i].text;
13844 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13846 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13847 char *value = getHashEntry(setup_file_hash, token);
13851 int parameter_value = get_token_parameter_value(token, value);
13853 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13857 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13858 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13860 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13870 // read (and overwrite with) values that may be specified in config file
13871 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13873 // special case: check if network and preview player positions are redefined
13874 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13876 freeSetupFileHash(setup_file_hash);
13879 void LoadMenuDesignSettings(void)
13881 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13883 InitMenuDesignSettings_Static();
13884 InitMenuDesignSettings_SpecialPreProcessing();
13885 InitMenuDesignSettings_PreviewPlayers();
13887 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13889 // first look for special settings configured in level series config
13890 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13892 if (fileExists(filename_base))
13893 LoadMenuDesignSettingsFromFilename(filename_base);
13896 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13898 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13899 LoadMenuDesignSettingsFromFilename(filename_local);
13901 InitMenuDesignSettings_SpecialPostProcessing();
13904 void LoadMenuDesignSettings_AfterGraphics(void)
13906 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13909 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13910 boolean ignore_defaults)
13914 for (i = 0; sound_config_vars[i].token != NULL; i++)
13916 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13918 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13919 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13923 *sound_config_vars[i].value =
13924 get_token_parameter_value(sound_config_vars[i].token, value);
13928 void InitSoundSettings_Static(void)
13930 // always start with reliable default values from static default config
13931 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13934 static void LoadSoundSettingsFromFilename(char *filename)
13936 SetupFileHash *setup_file_hash;
13938 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13941 // read (and overwrite with) values that may be specified in config file
13942 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13944 freeSetupFileHash(setup_file_hash);
13947 void LoadSoundSettings(void)
13949 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13951 InitSoundSettings_Static();
13953 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13955 // first look for special settings configured in level series config
13956 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13958 if (fileExists(filename_base))
13959 LoadSoundSettingsFromFilename(filename_base);
13962 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13964 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13965 LoadSoundSettingsFromFilename(filename_local);
13968 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13970 char *filename = getEditorSetupFilename();
13971 SetupFileList *setup_file_list, *list;
13972 SetupFileHash *element_hash;
13973 int num_unknown_tokens = 0;
13976 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13979 element_hash = newSetupFileHash();
13981 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13982 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13984 // determined size may be larger than needed (due to unknown elements)
13986 for (list = setup_file_list; list != NULL; list = list->next)
13989 // add space for up to 3 more elements for padding that may be needed
13990 *num_elements += 3;
13992 // free memory for old list of elements, if needed
13993 checked_free(*elements);
13995 // allocate memory for new list of elements
13996 *elements = checked_malloc(*num_elements * sizeof(int));
13999 for (list = setup_file_list; list != NULL; list = list->next)
14001 char *value = getHashEntry(element_hash, list->token);
14003 if (value == NULL) // try to find obsolete token mapping
14005 char *mapped_token = get_mapped_token(list->token);
14007 if (mapped_token != NULL)
14009 value = getHashEntry(element_hash, mapped_token);
14011 free(mapped_token);
14017 (*elements)[(*num_elements)++] = atoi(value);
14021 if (num_unknown_tokens == 0)
14024 Warn("unknown token(s) found in config file:");
14025 Warn("- config file: '%s'", filename);
14027 num_unknown_tokens++;
14030 Warn("- token: '%s'", list->token);
14034 if (num_unknown_tokens > 0)
14037 while (*num_elements % 4) // pad with empty elements, if needed
14038 (*elements)[(*num_elements)++] = EL_EMPTY;
14040 freeSetupFileList(setup_file_list);
14041 freeSetupFileHash(element_hash);
14044 for (i = 0; i < *num_elements; i++)
14045 Debug("editor", "element '%s' [%d]\n",
14046 element_info[(*elements)[i]].token_name, (*elements)[i]);
14050 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14053 SetupFileHash *setup_file_hash = NULL;
14054 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14055 char *filename_music, *filename_prefix, *filename_info;
14061 token_to_value_ptr[] =
14063 { "title_header", &tmp_music_file_info.title_header },
14064 { "artist_header", &tmp_music_file_info.artist_header },
14065 { "album_header", &tmp_music_file_info.album_header },
14066 { "year_header", &tmp_music_file_info.year_header },
14067 { "played_header", &tmp_music_file_info.played_header },
14069 { "title", &tmp_music_file_info.title },
14070 { "artist", &tmp_music_file_info.artist },
14071 { "album", &tmp_music_file_info.album },
14072 { "year", &tmp_music_file_info.year },
14073 { "played", &tmp_music_file_info.played },
14079 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14080 getCustomMusicFilename(basename));
14082 if (filename_music == NULL)
14085 // ---------- try to replace file extension ----------
14087 filename_prefix = getStringCopy(filename_music);
14088 if (strrchr(filename_prefix, '.') != NULL)
14089 *strrchr(filename_prefix, '.') = '\0';
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);
14098 if (setup_file_hash == NULL)
14100 // ---------- try to add file extension ----------
14102 filename_prefix = getStringCopy(filename_music);
14103 filename_info = getStringCat2(filename_prefix, ".txt");
14105 if (fileExists(filename_info))
14106 setup_file_hash = loadSetupFileHash(filename_info);
14108 free(filename_prefix);
14109 free(filename_info);
14112 if (setup_file_hash == NULL)
14115 // ---------- music file info found ----------
14117 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14119 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14121 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14123 *token_to_value_ptr[i].value_ptr =
14124 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14127 tmp_music_file_info.basename = getStringCopy(basename);
14128 tmp_music_file_info.music = music;
14129 tmp_music_file_info.is_sound = is_sound;
14131 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14132 *new_music_file_info = tmp_music_file_info;
14134 return new_music_file_info;
14137 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14139 return get_music_file_info_ext(basename, music, FALSE);
14142 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14144 return get_music_file_info_ext(basename, sound, TRUE);
14147 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14148 char *basename, boolean is_sound)
14150 for (; list != NULL; list = list->next)
14151 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14157 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14159 return music_info_listed_ext(list, basename, FALSE);
14162 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14164 return music_info_listed_ext(list, basename, TRUE);
14167 void LoadMusicInfo(void)
14169 int num_music_noconf = getMusicListSize_NoConf();
14170 int num_music = getMusicListSize();
14171 int num_sounds = getSoundListSize();
14172 struct FileInfo *music, *sound;
14173 struct MusicFileInfo *next, **new;
14177 while (music_file_info != NULL)
14179 next = music_file_info->next;
14181 checked_free(music_file_info->basename);
14183 checked_free(music_file_info->title_header);
14184 checked_free(music_file_info->artist_header);
14185 checked_free(music_file_info->album_header);
14186 checked_free(music_file_info->year_header);
14187 checked_free(music_file_info->played_header);
14189 checked_free(music_file_info->title);
14190 checked_free(music_file_info->artist);
14191 checked_free(music_file_info->album);
14192 checked_free(music_file_info->year);
14193 checked_free(music_file_info->played);
14195 free(music_file_info);
14197 music_file_info = next;
14200 new = &music_file_info;
14202 // get (configured or unconfigured) music file info for all levels
14203 for (i = leveldir_current->first_level;
14204 i <= leveldir_current->last_level; i++)
14208 if (levelset.music[i] != MUS_UNDEFINED)
14210 // get music file info for configured level music
14211 music_nr = levelset.music[i];
14213 else if (num_music_noconf > 0)
14215 // get music file info for unconfigured level music
14216 int level_pos = i - leveldir_current->first_level;
14218 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14225 char *basename = getMusicInfoEntryFilename(music_nr);
14227 if (basename == NULL)
14230 if (!music_info_listed(music_file_info, basename))
14232 *new = get_music_file_info(basename, music_nr);
14235 new = &(*new)->next;
14239 // get music file info for all remaining configured music files
14240 for (i = 0; i < num_music; i++)
14242 music = getMusicListEntry(i);
14244 if (music->filename == NULL)
14247 if (strEqual(music->filename, UNDEFINED_FILENAME))
14250 // a configured file may be not recognized as music
14251 if (!FileIsMusic(music->filename))
14254 if (!music_info_listed(music_file_info, music->filename))
14256 *new = get_music_file_info(music->filename, i);
14259 new = &(*new)->next;
14263 // get sound file info for all configured sound files
14264 for (i = 0; i < num_sounds; i++)
14266 sound = getSoundListEntry(i);
14268 if (sound->filename == NULL)
14271 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14274 // a configured file may be not recognized as sound
14275 if (!FileIsSound(sound->filename))
14278 if (!sound_info_listed(music_file_info, sound->filename))
14280 *new = get_sound_file_info(sound->filename, i);
14282 new = &(*new)->next;
14286 // add pointers to previous list nodes
14288 struct MusicFileInfo *node = music_file_info;
14290 while (node != NULL)
14293 node->next->prev = node;
14299 static void add_helpanim_entry(int element, int action, int direction,
14300 int delay, int *num_list_entries)
14302 struct HelpAnimInfo *new_list_entry;
14303 (*num_list_entries)++;
14306 checked_realloc(helpanim_info,
14307 *num_list_entries * sizeof(struct HelpAnimInfo));
14308 new_list_entry = &helpanim_info[*num_list_entries - 1];
14310 new_list_entry->element = element;
14311 new_list_entry->action = action;
14312 new_list_entry->direction = direction;
14313 new_list_entry->delay = delay;
14316 static void print_unknown_token(char *filename, char *token, int token_nr)
14321 Warn("unknown token(s) found in config file:");
14322 Warn("- config file: '%s'", filename);
14325 Warn("- token: '%s'", token);
14328 static void print_unknown_token_end(int token_nr)
14334 void LoadHelpAnimInfo(void)
14336 char *filename = getHelpAnimFilename();
14337 SetupFileList *setup_file_list = NULL, *list;
14338 SetupFileHash *element_hash, *action_hash, *direction_hash;
14339 int num_list_entries = 0;
14340 int num_unknown_tokens = 0;
14343 if (fileExists(filename))
14344 setup_file_list = loadSetupFileList(filename);
14346 if (setup_file_list == NULL)
14348 // use reliable default values from static configuration
14349 SetupFileList *insert_ptr;
14351 insert_ptr = setup_file_list =
14352 newSetupFileList(helpanim_config[0].token,
14353 helpanim_config[0].value);
14355 for (i = 1; helpanim_config[i].token; i++)
14356 insert_ptr = addListEntry(insert_ptr,
14357 helpanim_config[i].token,
14358 helpanim_config[i].value);
14361 element_hash = newSetupFileHash();
14362 action_hash = newSetupFileHash();
14363 direction_hash = newSetupFileHash();
14365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14366 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14368 for (i = 0; i < NUM_ACTIONS; i++)
14369 setHashEntry(action_hash, element_action_info[i].suffix,
14370 i_to_a(element_action_info[i].value));
14372 // do not store direction index (bit) here, but direction value!
14373 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14374 setHashEntry(direction_hash, element_direction_info[i].suffix,
14375 i_to_a(1 << element_direction_info[i].value));
14377 for (list = setup_file_list; list != NULL; list = list->next)
14379 char *element_token, *action_token, *direction_token;
14380 char *element_value, *action_value, *direction_value;
14381 int delay = atoi(list->value);
14383 if (strEqual(list->token, "end"))
14385 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14390 /* first try to break element into element/action/direction parts;
14391 if this does not work, also accept combined "element[.act][.dir]"
14392 elements (like "dynamite.active"), which are unique elements */
14394 if (strchr(list->token, '.') == NULL) // token contains no '.'
14396 element_value = getHashEntry(element_hash, list->token);
14397 if (element_value != NULL) // element found
14398 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14399 &num_list_entries);
14402 // no further suffixes found -- this is not an element
14403 print_unknown_token(filename, list->token, num_unknown_tokens++);
14409 // token has format "<prefix>.<something>"
14411 action_token = strchr(list->token, '.'); // suffix may be action ...
14412 direction_token = action_token; // ... or direction
14414 element_token = getStringCopy(list->token);
14415 *strchr(element_token, '.') = '\0';
14417 element_value = getHashEntry(element_hash, element_token);
14419 if (element_value == NULL) // this is no element
14421 element_value = getHashEntry(element_hash, list->token);
14422 if (element_value != NULL) // combined element found
14423 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14424 &num_list_entries);
14426 print_unknown_token(filename, list->token, num_unknown_tokens++);
14428 free(element_token);
14433 action_value = getHashEntry(action_hash, action_token);
14435 if (action_value != NULL) // action found
14437 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14438 &num_list_entries);
14440 free(element_token);
14445 direction_value = getHashEntry(direction_hash, direction_token);
14447 if (direction_value != NULL) // direction found
14449 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14450 &num_list_entries);
14452 free(element_token);
14457 if (strchr(action_token + 1, '.') == NULL)
14459 // no further suffixes found -- this is not an action nor direction
14461 element_value = getHashEntry(element_hash, list->token);
14462 if (element_value != NULL) // combined element found
14463 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14464 &num_list_entries);
14466 print_unknown_token(filename, list->token, num_unknown_tokens++);
14468 free(element_token);
14473 // token has format "<prefix>.<suffix>.<something>"
14475 direction_token = strchr(action_token + 1, '.');
14477 action_token = getStringCopy(action_token);
14478 *strchr(action_token + 1, '.') = '\0';
14480 action_value = getHashEntry(action_hash, action_token);
14482 if (action_value == NULL) // this is no action
14484 element_value = getHashEntry(element_hash, list->token);
14485 if (element_value != NULL) // combined element found
14486 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14487 &num_list_entries);
14489 print_unknown_token(filename, list->token, num_unknown_tokens++);
14491 free(element_token);
14492 free(action_token);
14497 direction_value = getHashEntry(direction_hash, direction_token);
14499 if (direction_value != NULL) // direction found
14501 add_helpanim_entry(atoi(element_value), atoi(action_value),
14502 atoi(direction_value), delay, &num_list_entries);
14504 free(element_token);
14505 free(action_token);
14510 // this is no direction
14512 element_value = getHashEntry(element_hash, list->token);
14513 if (element_value != NULL) // combined element found
14514 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14515 &num_list_entries);
14517 print_unknown_token(filename, list->token, num_unknown_tokens++);
14519 free(element_token);
14520 free(action_token);
14523 print_unknown_token_end(num_unknown_tokens);
14525 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14526 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14528 freeSetupFileList(setup_file_list);
14529 freeSetupFileHash(element_hash);
14530 freeSetupFileHash(action_hash);
14531 freeSetupFileHash(direction_hash);
14534 for (i = 0; i < num_list_entries; i++)
14535 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14536 EL_NAME(helpanim_info[i].element),
14537 helpanim_info[i].element,
14538 helpanim_info[i].action,
14539 helpanim_info[i].direction,
14540 helpanim_info[i].delay);
14544 void LoadHelpTextInfo(void)
14546 char *filename = getHelpTextFilename();
14549 if (helptext_info != NULL)
14551 freeSetupFileHash(helptext_info);
14552 helptext_info = NULL;
14555 if (fileExists(filename))
14556 helptext_info = loadSetupFileHash(filename);
14558 if (helptext_info == NULL)
14560 // use reliable default values from static configuration
14561 helptext_info = newSetupFileHash();
14563 for (i = 0; helptext_config[i].token; i++)
14564 setHashEntry(helptext_info,
14565 helptext_config[i].token,
14566 helptext_config[i].value);
14570 BEGIN_HASH_ITERATION(helptext_info, itr)
14572 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14573 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14575 END_HASH_ITERATION(hash, itr)
14580 // ----------------------------------------------------------------------------
14582 // ----------------------------------------------------------------------------
14584 #define MAX_NUM_CONVERT_LEVELS 1000
14586 void ConvertLevels(void)
14588 static LevelDirTree *convert_leveldir = NULL;
14589 static int convert_level_nr = -1;
14590 static int num_levels_handled = 0;
14591 static int num_levels_converted = 0;
14592 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14595 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14596 global.convert_leveldir);
14598 if (convert_leveldir == NULL)
14599 Fail("no such level identifier: '%s'", global.convert_leveldir);
14601 leveldir_current = convert_leveldir;
14603 if (global.convert_level_nr != -1)
14605 convert_leveldir->first_level = global.convert_level_nr;
14606 convert_leveldir->last_level = global.convert_level_nr;
14609 convert_level_nr = convert_leveldir->first_level;
14611 PrintLine("=", 79);
14612 Print("Converting levels\n");
14613 PrintLine("-", 79);
14614 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14615 Print("Level series name: '%s'\n", convert_leveldir->name);
14616 Print("Level series author: '%s'\n", convert_leveldir->author);
14617 Print("Number of levels: %d\n", convert_leveldir->levels);
14618 PrintLine("=", 79);
14621 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14622 levels_failed[i] = FALSE;
14624 while (convert_level_nr <= convert_leveldir->last_level)
14626 char *level_filename;
14629 level_nr = convert_level_nr++;
14631 Print("Level %03d: ", level_nr);
14633 LoadLevel(level_nr);
14634 if (level.no_level_file || level.no_valid_file)
14636 Print("(no level)\n");
14640 Print("converting level ... ");
14643 // special case: conversion of some EMC levels as requested by ACME
14644 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14647 level_filename = getDefaultLevelFilename(level_nr);
14648 new_level = !fileExists(level_filename);
14652 SaveLevel(level_nr);
14654 num_levels_converted++;
14656 Print("converted.\n");
14660 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14661 levels_failed[level_nr] = TRUE;
14663 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14666 num_levels_handled++;
14670 PrintLine("=", 79);
14671 Print("Number of levels handled: %d\n", num_levels_handled);
14672 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14673 (num_levels_handled ?
14674 num_levels_converted * 100 / num_levels_handled : 0));
14675 PrintLine("-", 79);
14676 Print("Summary (for automatic parsing by scripts):\n");
14677 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14678 convert_leveldir->identifier, num_levels_converted,
14679 num_levels_handled,
14680 (num_levels_handled ?
14681 num_levels_converted * 100 / num_levels_handled : 0));
14683 if (num_levels_handled != num_levels_converted)
14685 Print(", FAILED:");
14686 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14687 if (levels_failed[i])
14692 PrintLine("=", 79);
14694 CloseAllAndExit(0);
14698 // ----------------------------------------------------------------------------
14699 // create and save images for use in level sketches (raw BMP format)
14700 // ----------------------------------------------------------------------------
14702 void CreateLevelSketchImages(void)
14708 InitElementPropertiesGfxElement();
14710 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14711 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14713 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14715 int element = getMappedElement(i);
14716 char basename1[16];
14717 char basename2[16];
14721 sprintf(basename1, "%04d.bmp", i);
14722 sprintf(basename2, "%04ds.bmp", i);
14724 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14725 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14727 DrawSizedElement(0, 0, element, TILESIZE);
14728 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14730 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14731 Fail("cannot save level sketch image file '%s'", filename1);
14733 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14734 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14736 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14737 Fail("cannot save level sketch image file '%s'", filename2);
14742 // create corresponding SQL statements (for normal and small images)
14745 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14746 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14749 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14750 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14752 // optional: create content for forum level sketch demonstration post
14754 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14757 FreeBitmap(bitmap1);
14758 FreeBitmap(bitmap2);
14761 fprintf(stderr, "\n");
14763 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14765 CloseAllAndExit(0);
14769 // ----------------------------------------------------------------------------
14770 // create and save images for element collecting animations (raw BMP format)
14771 // ----------------------------------------------------------------------------
14773 static boolean createCollectImage(int element)
14775 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14778 void CreateCollectElementImages(void)
14782 int anim_frames = num_steps - 1;
14783 int tile_size = TILESIZE;
14784 int anim_width = tile_size * anim_frames;
14785 int anim_height = tile_size;
14786 int num_collect_images = 0;
14787 int pos_collect_images = 0;
14789 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14790 if (createCollectImage(i))
14791 num_collect_images++;
14793 Info("Creating %d element collecting animation images ...",
14794 num_collect_images);
14796 int dst_width = anim_width * 2;
14797 int dst_height = anim_height * num_collect_images / 2;
14798 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14799 char *basename_bmp = "RocksCollect.bmp";
14800 char *basename_png = "RocksCollect.png";
14801 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14802 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14803 int len_filename_bmp = strlen(filename_bmp);
14804 int len_filename_png = strlen(filename_png);
14805 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14806 char cmd_convert[max_command_len];
14808 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14812 // force using RGBA surface for destination bitmap
14813 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14814 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14816 dst_bitmap->surface =
14817 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14819 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14821 if (!createCollectImage(i))
14824 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14825 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14826 int graphic = el2img(i);
14827 char *token_name = element_info[i].token_name;
14828 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14829 Bitmap *src_bitmap;
14832 Info("- creating collecting image for '%s' ...", token_name);
14834 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14836 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14837 tile_size, tile_size, 0, 0);
14839 // force using RGBA surface for temporary bitmap (using transparent black)
14840 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14841 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14843 tmp_bitmap->surface =
14844 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14846 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14848 for (j = 0; j < anim_frames; j++)
14850 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14851 int frame_size = frame_size_final * num_steps;
14852 int offset = (tile_size - frame_size_final) / 2;
14853 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14855 while (frame_size > frame_size_final)
14859 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14861 FreeBitmap(frame_bitmap);
14863 frame_bitmap = half_bitmap;
14866 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14867 frame_size_final, frame_size_final,
14868 dst_x + j * tile_size + offset, dst_y + offset);
14870 FreeBitmap(frame_bitmap);
14873 tmp_bitmap->surface_masked = NULL;
14875 FreeBitmap(tmp_bitmap);
14877 pos_collect_images++;
14880 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14881 Fail("cannot save element collecting image file '%s'", filename_bmp);
14883 FreeBitmap(dst_bitmap);
14885 Info("Converting image file from BMP to PNG ...");
14887 if (system(cmd_convert) != 0)
14888 Fail("converting image file failed");
14890 unlink(filename_bmp);
14894 CloseAllAndExit(0);
14898 // ----------------------------------------------------------------------------
14899 // create and save images for custom and group elements (raw BMP format)
14900 // ----------------------------------------------------------------------------
14902 void CreateCustomElementImages(char *directory)
14904 char *src_basename = "RocksCE-template.ilbm";
14905 char *dst_basename = "RocksCE.bmp";
14906 char *src_filename = getPath2(directory, src_basename);
14907 char *dst_filename = getPath2(directory, dst_basename);
14908 Bitmap *src_bitmap;
14910 int yoffset_ce = 0;
14911 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14914 InitVideoDefaults();
14916 ReCreateBitmap(&backbuffer, video.width, video.height);
14918 src_bitmap = LoadImage(src_filename);
14920 bitmap = CreateBitmap(TILEX * 16 * 2,
14921 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14924 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14931 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14932 TILEX * x, TILEY * y + yoffset_ce);
14934 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14936 TILEX * x + TILEX * 16,
14937 TILEY * y + yoffset_ce);
14939 for (j = 2; j >= 0; j--)
14943 BlitBitmap(src_bitmap, bitmap,
14944 TILEX + c * 7, 0, 6, 10,
14945 TILEX * x + 6 + j * 7,
14946 TILEY * y + 11 + yoffset_ce);
14948 BlitBitmap(src_bitmap, bitmap,
14949 TILEX + c * 8, TILEY, 6, 10,
14950 TILEX * 16 + TILEX * x + 6 + j * 8,
14951 TILEY * y + 10 + yoffset_ce);
14957 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14964 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14965 TILEX * x, TILEY * y + yoffset_ge);
14967 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14969 TILEX * x + TILEX * 16,
14970 TILEY * y + yoffset_ge);
14972 for (j = 1; j >= 0; j--)
14976 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14977 TILEX * x + 6 + j * 10,
14978 TILEY * y + 11 + yoffset_ge);
14980 BlitBitmap(src_bitmap, bitmap,
14981 TILEX + c * 8, TILEY + 12, 6, 10,
14982 TILEX * 16 + TILEX * x + 10 + j * 8,
14983 TILEY * y + 10 + yoffset_ge);
14989 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14990 Fail("cannot save CE graphics file '%s'", dst_filename);
14992 FreeBitmap(bitmap);
14994 CloseAllAndExit(0);