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();
4613 // ----------------------------------------------------------------------------
4614 // functions for loading EM level
4615 // ----------------------------------------------------------------------------
4617 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4619 static int ball_xy[8][2] =
4630 struct LevelInfo_EM *level_em = level->native_em_level;
4631 struct CAVE *cav = level_em->cav;
4634 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4635 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4637 cav->time_seconds = level->time;
4638 cav->gems_needed = level->gems_needed;
4640 cav->emerald_score = level->score[SC_EMERALD];
4641 cav->diamond_score = level->score[SC_DIAMOND];
4642 cav->alien_score = level->score[SC_ROBOT];
4643 cav->tank_score = level->score[SC_SPACESHIP];
4644 cav->bug_score = level->score[SC_BUG];
4645 cav->eater_score = level->score[SC_YAMYAM];
4646 cav->nut_score = level->score[SC_NUT];
4647 cav->dynamite_score = level->score[SC_DYNAMITE];
4648 cav->key_score = level->score[SC_KEY];
4649 cav->exit_score = level->score[SC_TIME_BONUS];
4651 cav->num_eater_arrays = level->num_yamyam_contents;
4653 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4654 for (y = 0; y < 3; y++)
4655 for (x = 0; x < 3; x++)
4656 cav->eater_array[i][y * 3 + x] =
4657 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4659 cav->amoeba_time = level->amoeba_speed;
4660 cav->wonderwall_time = level->time_magic_wall;
4661 cav->wheel_time = level->time_wheel;
4663 cav->android_move_time = level->android_move_time;
4664 cav->android_clone_time = level->android_clone_time;
4665 cav->ball_random = level->ball_random;
4666 cav->ball_active = level->ball_active_initial;
4667 cav->ball_time = level->ball_time;
4668 cav->num_ball_arrays = level->num_ball_contents;
4670 cav->lenses_score = level->lenses_score;
4671 cav->magnify_score = level->magnify_score;
4672 cav->slurp_score = level->slurp_score;
4674 cav->lenses_time = level->lenses_time;
4675 cav->magnify_time = level->magnify_time;
4677 cav->wind_time = 9999;
4678 cav->wind_direction =
4679 map_direction_RND_to_EM(level->wind_direction_initial);
4681 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4682 for (j = 0; j < 8; j++)
4683 cav->ball_array[i][j] =
4684 map_element_RND_to_EM_cave(level->ball_content[i].
4685 e[ball_xy[j][0]][ball_xy[j][1]]);
4687 map_android_clone_elements_RND_to_EM(level);
4689 // first fill the complete playfield with the empty space element
4690 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4691 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4692 cav->cave[x][y] = Cblank;
4694 // then copy the real level contents from level file into the playfield
4695 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4697 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4699 if (level->field[x][y] == EL_AMOEBA_DEAD)
4700 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4702 cav->cave[x][y] = new_element;
4705 for (i = 0; i < MAX_PLAYERS; i++)
4707 cav->player_x[i] = -1;
4708 cav->player_y[i] = -1;
4711 // initialize player positions and delete players from the playfield
4712 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4714 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4716 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4718 cav->player_x[player_nr] = x;
4719 cav->player_y[player_nr] = y;
4721 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4726 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4728 static int ball_xy[8][2] =
4739 struct LevelInfo_EM *level_em = level->native_em_level;
4740 struct CAVE *cav = level_em->cav;
4743 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4744 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4746 level->time = cav->time_seconds;
4747 level->gems_needed = cav->gems_needed;
4749 sprintf(level->name, "Level %d", level->file_info.nr);
4751 level->score[SC_EMERALD] = cav->emerald_score;
4752 level->score[SC_DIAMOND] = cav->diamond_score;
4753 level->score[SC_ROBOT] = cav->alien_score;
4754 level->score[SC_SPACESHIP] = cav->tank_score;
4755 level->score[SC_BUG] = cav->bug_score;
4756 level->score[SC_YAMYAM] = cav->eater_score;
4757 level->score[SC_NUT] = cav->nut_score;
4758 level->score[SC_DYNAMITE] = cav->dynamite_score;
4759 level->score[SC_KEY] = cav->key_score;
4760 level->score[SC_TIME_BONUS] = cav->exit_score;
4762 level->num_yamyam_contents = cav->num_eater_arrays;
4764 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4765 for (y = 0; y < 3; y++)
4766 for (x = 0; x < 3; x++)
4767 level->yamyam_content[i].e[x][y] =
4768 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4770 level->amoeba_speed = cav->amoeba_time;
4771 level->time_magic_wall = cav->wonderwall_time;
4772 level->time_wheel = cav->wheel_time;
4774 level->android_move_time = cav->android_move_time;
4775 level->android_clone_time = cav->android_clone_time;
4776 level->ball_random = cav->ball_random;
4777 level->ball_active_initial = cav->ball_active;
4778 level->ball_time = cav->ball_time;
4779 level->num_ball_contents = cav->num_ball_arrays;
4781 level->lenses_score = cav->lenses_score;
4782 level->magnify_score = cav->magnify_score;
4783 level->slurp_score = cav->slurp_score;
4785 level->lenses_time = cav->lenses_time;
4786 level->magnify_time = cav->magnify_time;
4788 level->wind_direction_initial =
4789 map_direction_EM_to_RND(cav->wind_direction);
4791 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4792 for (j = 0; j < 8; j++)
4793 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4794 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4796 map_android_clone_elements_EM_to_RND(level);
4798 // convert the playfield (some elements need special treatment)
4799 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4801 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4803 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4804 new_element = EL_AMOEBA_DEAD;
4806 level->field[x][y] = new_element;
4809 for (i = 0; i < MAX_PLAYERS; i++)
4811 // in case of all players set to the same field, use the first player
4812 int nr = MAX_PLAYERS - i - 1;
4813 int jx = cav->player_x[nr];
4814 int jy = cav->player_y[nr];
4816 if (jx != -1 && jy != -1)
4817 level->field[jx][jy] = EL_PLAYER_1 + nr;
4820 // time score is counted for each 10 seconds left in Emerald Mine levels
4821 level->time_score_base = 10;
4825 // ----------------------------------------------------------------------------
4826 // functions for loading SP level
4827 // ----------------------------------------------------------------------------
4829 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4831 struct LevelInfo_SP *level_sp = level->native_sp_level;
4832 LevelInfoType *header = &level_sp->header;
4835 level_sp->width = level->fieldx;
4836 level_sp->height = level->fieldy;
4838 for (x = 0; x < level->fieldx; x++)
4839 for (y = 0; y < level->fieldy; y++)
4840 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4842 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4844 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4845 header->LevelTitle[i] = level->name[i];
4846 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4848 header->InfotronsNeeded = level->gems_needed;
4850 header->SpecialPortCount = 0;
4852 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4854 boolean gravity_port_found = FALSE;
4855 boolean gravity_port_valid = FALSE;
4856 int gravity_port_flag;
4857 int gravity_port_base_element;
4858 int element = level->field[x][y];
4860 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4861 element <= EL_SP_GRAVITY_ON_PORT_UP)
4863 gravity_port_found = TRUE;
4864 gravity_port_valid = TRUE;
4865 gravity_port_flag = 1;
4866 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4868 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4869 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4871 gravity_port_found = TRUE;
4872 gravity_port_valid = TRUE;
4873 gravity_port_flag = 0;
4874 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4876 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4877 element <= EL_SP_GRAVITY_PORT_UP)
4879 // change R'n'D style gravity inverting special port to normal port
4880 // (there are no gravity inverting ports in native Supaplex engine)
4882 gravity_port_found = TRUE;
4883 gravity_port_valid = FALSE;
4884 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4887 if (gravity_port_found)
4889 if (gravity_port_valid &&
4890 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4892 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4894 port->PortLocation = (y * level->fieldx + x) * 2;
4895 port->Gravity = gravity_port_flag;
4897 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4899 header->SpecialPortCount++;
4903 // change special gravity port to normal port
4905 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4908 level_sp->playfield[x][y] = element - EL_SP_START;
4913 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4915 struct LevelInfo_SP *level_sp = level->native_sp_level;
4916 LevelInfoType *header = &level_sp->header;
4917 boolean num_invalid_elements = 0;
4920 level->fieldx = level_sp->width;
4921 level->fieldy = level_sp->height;
4923 for (x = 0; x < level->fieldx; x++)
4925 for (y = 0; y < level->fieldy; y++)
4927 int element_old = level_sp->playfield[x][y];
4928 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4930 if (element_new == EL_UNKNOWN)
4932 num_invalid_elements++;
4934 Debug("level:native:SP", "invalid element %d at position %d, %d",
4938 level->field[x][y] = element_new;
4942 if (num_invalid_elements > 0)
4943 Warn("found %d invalid elements%s", num_invalid_elements,
4944 (!options.debug ? " (use '--debug' for more details)" : ""));
4946 for (i = 0; i < MAX_PLAYERS; i++)
4947 level->initial_player_gravity[i] =
4948 (header->InitialGravity == 1 ? TRUE : FALSE);
4950 // skip leading spaces
4951 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4952 if (header->LevelTitle[i] != ' ')
4956 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4957 level->name[j] = header->LevelTitle[i];
4958 level->name[j] = '\0';
4960 // cut trailing spaces
4962 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4963 level->name[j - 1] = '\0';
4965 level->gems_needed = header->InfotronsNeeded;
4967 for (i = 0; i < header->SpecialPortCount; i++)
4969 SpecialPortType *port = &header->SpecialPort[i];
4970 int port_location = port->PortLocation;
4971 int gravity = port->Gravity;
4972 int port_x, port_y, port_element;
4974 port_x = (port_location / 2) % level->fieldx;
4975 port_y = (port_location / 2) / level->fieldx;
4977 if (port_x < 0 || port_x >= level->fieldx ||
4978 port_y < 0 || port_y >= level->fieldy)
4980 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4985 port_element = level->field[port_x][port_y];
4987 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4988 port_element > EL_SP_GRAVITY_PORT_UP)
4990 Warn("no special port at position (%d, %d)", port_x, port_y);
4995 // change previous (wrong) gravity inverting special port to either
4996 // gravity enabling special port or gravity disabling special port
4997 level->field[port_x][port_y] +=
4998 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4999 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5002 // change special gravity ports without database entries to normal ports
5003 for (x = 0; x < level->fieldx; x++)
5004 for (y = 0; y < level->fieldy; y++)
5005 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5006 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5007 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5009 level->time = 0; // no time limit
5010 level->amoeba_speed = 0;
5011 level->time_magic_wall = 0;
5012 level->time_wheel = 0;
5013 level->amoeba_content = EL_EMPTY;
5015 // original Supaplex does not use score values -- rate by playing time
5016 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5017 level->score[i] = 0;
5019 level->rate_time_over_score = TRUE;
5021 // there are no yamyams in supaplex levels
5022 for (i = 0; i < level->num_yamyam_contents; i++)
5023 for (x = 0; x < 3; x++)
5024 for (y = 0; y < 3; y++)
5025 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5028 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5030 struct LevelInfo_SP *level_sp = level->native_sp_level;
5031 struct DemoInfo_SP *demo = &level_sp->demo;
5034 // always start with reliable default values
5035 demo->is_available = FALSE;
5038 if (TAPE_IS_EMPTY(tape))
5041 demo->level_nr = tape.level_nr; // (currently not used)
5043 level_sp->header.DemoRandomSeed = tape.random_seed;
5047 for (i = 0; i < tape.length; i++)
5049 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5050 int demo_repeat = tape.pos[i].delay;
5051 int demo_entries = (demo_repeat + 15) / 16;
5053 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5055 Warn("tape truncated: size exceeds maximum SP demo size %d",
5061 for (j = 0; j < demo_repeat / 16; j++)
5062 demo->data[demo->length++] = 0xf0 | demo_action;
5064 if (demo_repeat % 16)
5065 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5068 demo->is_available = TRUE;
5071 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5073 struct LevelInfo_SP *level_sp = level->native_sp_level;
5074 struct DemoInfo_SP *demo = &level_sp->demo;
5075 char *filename = level->file_info.filename;
5078 // always start with reliable default values
5079 setTapeInfoToDefaults();
5081 if (!demo->is_available)
5084 tape.level_nr = demo->level_nr; // (currently not used)
5085 tape.random_seed = level_sp->header.DemoRandomSeed;
5087 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5090 tape.pos[tape.counter].delay = 0;
5092 for (i = 0; i < demo->length; i++)
5094 int demo_action = demo->data[i] & 0x0f;
5095 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5096 int tape_action = map_key_SP_to_RND(demo_action);
5097 int tape_repeat = demo_repeat + 1;
5098 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5099 boolean success = 0;
5102 for (j = 0; j < tape_repeat; j++)
5103 success = TapeAddAction(action);
5107 Warn("SP demo truncated: size exceeds maximum tape size %d",
5114 TapeHaltRecording();
5118 // ----------------------------------------------------------------------------
5119 // functions for loading MM level
5120 // ----------------------------------------------------------------------------
5122 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5124 struct LevelInfo_MM *level_mm = level->native_mm_level;
5127 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5128 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5130 level_mm->time = level->time;
5131 level_mm->kettles_needed = level->gems_needed;
5132 level_mm->auto_count_kettles = level->auto_count_gems;
5134 level_mm->mm_laser_red = level->mm_laser_red;
5135 level_mm->mm_laser_green = level->mm_laser_green;
5136 level_mm->mm_laser_blue = level->mm_laser_blue;
5138 level_mm->df_laser_red = level->df_laser_red;
5139 level_mm->df_laser_green = level->df_laser_green;
5140 level_mm->df_laser_blue = level->df_laser_blue;
5142 strcpy(level_mm->name, level->name);
5143 strcpy(level_mm->author, level->author);
5145 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5146 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5147 level_mm->score[SC_KEY] = level->score[SC_KEY];
5148 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5149 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5151 level_mm->amoeba_speed = level->amoeba_speed;
5152 level_mm->time_fuse = level->mm_time_fuse;
5153 level_mm->time_bomb = level->mm_time_bomb;
5154 level_mm->time_ball = level->mm_time_ball;
5155 level_mm->time_block = level->mm_time_block;
5157 level_mm->num_ball_contents = level->num_mm_ball_contents;
5158 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5159 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5160 level_mm->explode_ball = level->explode_mm_ball;
5162 for (i = 0; i < level->num_mm_ball_contents; i++)
5163 level_mm->ball_content[i] =
5164 map_element_RND_to_MM(level->mm_ball_content[i]);
5166 for (x = 0; x < level->fieldx; x++)
5167 for (y = 0; y < level->fieldy; y++)
5169 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5172 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5174 struct LevelInfo_MM *level_mm = level->native_mm_level;
5177 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5178 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5180 level->time = level_mm->time;
5181 level->gems_needed = level_mm->kettles_needed;
5182 level->auto_count_gems = level_mm->auto_count_kettles;
5184 level->mm_laser_red = level_mm->mm_laser_red;
5185 level->mm_laser_green = level_mm->mm_laser_green;
5186 level->mm_laser_blue = level_mm->mm_laser_blue;
5188 level->df_laser_red = level_mm->df_laser_red;
5189 level->df_laser_green = level_mm->df_laser_green;
5190 level->df_laser_blue = level_mm->df_laser_blue;
5192 strcpy(level->name, level_mm->name);
5194 // only overwrite author from 'levelinfo.conf' if author defined in level
5195 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5196 strcpy(level->author, level_mm->author);
5198 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5199 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5200 level->score[SC_KEY] = level_mm->score[SC_KEY];
5201 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5202 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5204 level->amoeba_speed = level_mm->amoeba_speed;
5205 level->mm_time_fuse = level_mm->time_fuse;
5206 level->mm_time_bomb = level_mm->time_bomb;
5207 level->mm_time_ball = level_mm->time_ball;
5208 level->mm_time_block = level_mm->time_block;
5210 level->num_mm_ball_contents = level_mm->num_ball_contents;
5211 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5212 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5213 level->explode_mm_ball = level_mm->explode_ball;
5215 for (i = 0; i < level->num_mm_ball_contents; i++)
5216 level->mm_ball_content[i] =
5217 map_element_MM_to_RND(level_mm->ball_content[i]);
5219 for (x = 0; x < level->fieldx; x++)
5220 for (y = 0; y < level->fieldy; y++)
5221 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5225 // ----------------------------------------------------------------------------
5226 // functions for loading DC level
5227 // ----------------------------------------------------------------------------
5229 #define DC_LEVEL_HEADER_SIZE 344
5231 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5234 static int last_data_encoded;
5238 int diff_hi, diff_lo;
5239 int data_hi, data_lo;
5240 unsigned short data_decoded;
5244 last_data_encoded = 0;
5251 diff = data_encoded - last_data_encoded;
5252 diff_hi = diff & ~0xff;
5253 diff_lo = diff & 0xff;
5257 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5258 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5259 data_hi = data_hi & 0xff00;
5261 data_decoded = data_hi | data_lo;
5263 last_data_encoded = data_encoded;
5265 offset1 = (offset1 + 1) % 31;
5266 offset2 = offset2 & 0xff;
5268 return data_decoded;
5271 static int getMappedElement_DC(int element)
5279 // 0x0117 - 0x036e: (?)
5282 // 0x042d - 0x0684: (?)
5298 element = EL_CRYSTAL;
5301 case 0x0e77: // quicksand (boulder)
5302 element = EL_QUICKSAND_FAST_FULL;
5305 case 0x0e99: // slow quicksand (boulder)
5306 element = EL_QUICKSAND_FULL;
5310 element = EL_EM_EXIT_OPEN;
5314 element = EL_EM_EXIT_CLOSED;
5318 element = EL_EM_STEEL_EXIT_OPEN;
5322 element = EL_EM_STEEL_EXIT_CLOSED;
5325 case 0x0f4f: // dynamite (lit 1)
5326 element = EL_EM_DYNAMITE_ACTIVE;
5329 case 0x0f57: // dynamite (lit 2)
5330 element = EL_EM_DYNAMITE_ACTIVE;
5333 case 0x0f5f: // dynamite (lit 3)
5334 element = EL_EM_DYNAMITE_ACTIVE;
5337 case 0x0f67: // dynamite (lit 4)
5338 element = EL_EM_DYNAMITE_ACTIVE;
5345 element = EL_AMOEBA_WET;
5349 element = EL_AMOEBA_DROP;
5353 element = EL_DC_MAGIC_WALL;
5357 element = EL_SPACESHIP_UP;
5361 element = EL_SPACESHIP_DOWN;
5365 element = EL_SPACESHIP_LEFT;
5369 element = EL_SPACESHIP_RIGHT;
5373 element = EL_BUG_UP;
5377 element = EL_BUG_DOWN;
5381 element = EL_BUG_LEFT;
5385 element = EL_BUG_RIGHT;
5389 element = EL_MOLE_UP;
5393 element = EL_MOLE_DOWN;
5397 element = EL_MOLE_LEFT;
5401 element = EL_MOLE_RIGHT;
5409 element = EL_YAMYAM_UP;
5413 element = EL_SWITCHGATE_OPEN;
5417 element = EL_SWITCHGATE_CLOSED;
5421 element = EL_DC_SWITCHGATE_SWITCH_UP;
5425 element = EL_TIMEGATE_CLOSED;
5428 case 0x144c: // conveyor belt switch (green)
5429 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5432 case 0x144f: // conveyor belt switch (red)
5433 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5436 case 0x1452: // conveyor belt switch (blue)
5437 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5441 element = EL_CONVEYOR_BELT_3_MIDDLE;
5445 element = EL_CONVEYOR_BELT_3_LEFT;
5449 element = EL_CONVEYOR_BELT_3_RIGHT;
5453 element = EL_CONVEYOR_BELT_1_MIDDLE;
5457 element = EL_CONVEYOR_BELT_1_LEFT;
5461 element = EL_CONVEYOR_BELT_1_RIGHT;
5465 element = EL_CONVEYOR_BELT_4_MIDDLE;
5469 element = EL_CONVEYOR_BELT_4_LEFT;
5473 element = EL_CONVEYOR_BELT_4_RIGHT;
5477 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5481 element = EL_EXPANDABLE_WALL_VERTICAL;
5485 element = EL_EXPANDABLE_WALL_ANY;
5488 case 0x14ce: // growing steel wall (left/right)
5489 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5492 case 0x14df: // growing steel wall (up/down)
5493 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5496 case 0x14e8: // growing steel wall (up/down/left/right)
5497 element = EL_EXPANDABLE_STEELWALL_ANY;
5501 element = EL_SHIELD_DEADLY;
5505 element = EL_EXTRA_TIME;
5513 element = EL_EMPTY_SPACE;
5516 case 0x1578: // quicksand (empty)
5517 element = EL_QUICKSAND_FAST_EMPTY;
5520 case 0x1579: // slow quicksand (empty)
5521 element = EL_QUICKSAND_EMPTY;
5531 element = EL_EM_DYNAMITE;
5534 case 0x15a1: // key (red)
5535 element = EL_EM_KEY_1;
5538 case 0x15a2: // key (yellow)
5539 element = EL_EM_KEY_2;
5542 case 0x15a3: // key (blue)
5543 element = EL_EM_KEY_4;
5546 case 0x15a4: // key (green)
5547 element = EL_EM_KEY_3;
5550 case 0x15a5: // key (white)
5551 element = EL_DC_KEY_WHITE;
5555 element = EL_WALL_SLIPPERY;
5562 case 0x15a8: // wall (not round)
5566 case 0x15a9: // (blue)
5567 element = EL_CHAR_A;
5570 case 0x15aa: // (blue)
5571 element = EL_CHAR_B;
5574 case 0x15ab: // (blue)
5575 element = EL_CHAR_C;
5578 case 0x15ac: // (blue)
5579 element = EL_CHAR_D;
5582 case 0x15ad: // (blue)
5583 element = EL_CHAR_E;
5586 case 0x15ae: // (blue)
5587 element = EL_CHAR_F;
5590 case 0x15af: // (blue)
5591 element = EL_CHAR_G;
5594 case 0x15b0: // (blue)
5595 element = EL_CHAR_H;
5598 case 0x15b1: // (blue)
5599 element = EL_CHAR_I;
5602 case 0x15b2: // (blue)
5603 element = EL_CHAR_J;
5606 case 0x15b3: // (blue)
5607 element = EL_CHAR_K;
5610 case 0x15b4: // (blue)
5611 element = EL_CHAR_L;
5614 case 0x15b5: // (blue)
5615 element = EL_CHAR_M;
5618 case 0x15b6: // (blue)
5619 element = EL_CHAR_N;
5622 case 0x15b7: // (blue)
5623 element = EL_CHAR_O;
5626 case 0x15b8: // (blue)
5627 element = EL_CHAR_P;
5630 case 0x15b9: // (blue)
5631 element = EL_CHAR_Q;
5634 case 0x15ba: // (blue)
5635 element = EL_CHAR_R;
5638 case 0x15bb: // (blue)
5639 element = EL_CHAR_S;
5642 case 0x15bc: // (blue)
5643 element = EL_CHAR_T;
5646 case 0x15bd: // (blue)
5647 element = EL_CHAR_U;
5650 case 0x15be: // (blue)
5651 element = EL_CHAR_V;
5654 case 0x15bf: // (blue)
5655 element = EL_CHAR_W;
5658 case 0x15c0: // (blue)
5659 element = EL_CHAR_X;
5662 case 0x15c1: // (blue)
5663 element = EL_CHAR_Y;
5666 case 0x15c2: // (blue)
5667 element = EL_CHAR_Z;
5670 case 0x15c3: // (blue)
5671 element = EL_CHAR_AUMLAUT;
5674 case 0x15c4: // (blue)
5675 element = EL_CHAR_OUMLAUT;
5678 case 0x15c5: // (blue)
5679 element = EL_CHAR_UUMLAUT;
5682 case 0x15c6: // (blue)
5683 element = EL_CHAR_0;
5686 case 0x15c7: // (blue)
5687 element = EL_CHAR_1;
5690 case 0x15c8: // (blue)
5691 element = EL_CHAR_2;
5694 case 0x15c9: // (blue)
5695 element = EL_CHAR_3;
5698 case 0x15ca: // (blue)
5699 element = EL_CHAR_4;
5702 case 0x15cb: // (blue)
5703 element = EL_CHAR_5;
5706 case 0x15cc: // (blue)
5707 element = EL_CHAR_6;
5710 case 0x15cd: // (blue)
5711 element = EL_CHAR_7;
5714 case 0x15ce: // (blue)
5715 element = EL_CHAR_8;
5718 case 0x15cf: // (blue)
5719 element = EL_CHAR_9;
5722 case 0x15d0: // (blue)
5723 element = EL_CHAR_PERIOD;
5726 case 0x15d1: // (blue)
5727 element = EL_CHAR_EXCLAM;
5730 case 0x15d2: // (blue)
5731 element = EL_CHAR_COLON;
5734 case 0x15d3: // (blue)
5735 element = EL_CHAR_LESS;
5738 case 0x15d4: // (blue)
5739 element = EL_CHAR_GREATER;
5742 case 0x15d5: // (blue)
5743 element = EL_CHAR_QUESTION;
5746 case 0x15d6: // (blue)
5747 element = EL_CHAR_COPYRIGHT;
5750 case 0x15d7: // (blue)
5751 element = EL_CHAR_UP;
5754 case 0x15d8: // (blue)
5755 element = EL_CHAR_DOWN;
5758 case 0x15d9: // (blue)
5759 element = EL_CHAR_BUTTON;
5762 case 0x15da: // (blue)
5763 element = EL_CHAR_PLUS;
5766 case 0x15db: // (blue)
5767 element = EL_CHAR_MINUS;
5770 case 0x15dc: // (blue)
5771 element = EL_CHAR_APOSTROPHE;
5774 case 0x15dd: // (blue)
5775 element = EL_CHAR_PARENLEFT;
5778 case 0x15de: // (blue)
5779 element = EL_CHAR_PARENRIGHT;
5782 case 0x15df: // (green)
5783 element = EL_CHAR_A;
5786 case 0x15e0: // (green)
5787 element = EL_CHAR_B;
5790 case 0x15e1: // (green)
5791 element = EL_CHAR_C;
5794 case 0x15e2: // (green)
5795 element = EL_CHAR_D;
5798 case 0x15e3: // (green)
5799 element = EL_CHAR_E;
5802 case 0x15e4: // (green)
5803 element = EL_CHAR_F;
5806 case 0x15e5: // (green)
5807 element = EL_CHAR_G;
5810 case 0x15e6: // (green)
5811 element = EL_CHAR_H;
5814 case 0x15e7: // (green)
5815 element = EL_CHAR_I;
5818 case 0x15e8: // (green)
5819 element = EL_CHAR_J;
5822 case 0x15e9: // (green)
5823 element = EL_CHAR_K;
5826 case 0x15ea: // (green)
5827 element = EL_CHAR_L;
5830 case 0x15eb: // (green)
5831 element = EL_CHAR_M;
5834 case 0x15ec: // (green)
5835 element = EL_CHAR_N;
5838 case 0x15ed: // (green)
5839 element = EL_CHAR_O;
5842 case 0x15ee: // (green)
5843 element = EL_CHAR_P;
5846 case 0x15ef: // (green)
5847 element = EL_CHAR_Q;
5850 case 0x15f0: // (green)
5851 element = EL_CHAR_R;
5854 case 0x15f1: // (green)
5855 element = EL_CHAR_S;
5858 case 0x15f2: // (green)
5859 element = EL_CHAR_T;
5862 case 0x15f3: // (green)
5863 element = EL_CHAR_U;
5866 case 0x15f4: // (green)
5867 element = EL_CHAR_V;
5870 case 0x15f5: // (green)
5871 element = EL_CHAR_W;
5874 case 0x15f6: // (green)
5875 element = EL_CHAR_X;
5878 case 0x15f7: // (green)
5879 element = EL_CHAR_Y;
5882 case 0x15f8: // (green)
5883 element = EL_CHAR_Z;
5886 case 0x15f9: // (green)
5887 element = EL_CHAR_AUMLAUT;
5890 case 0x15fa: // (green)
5891 element = EL_CHAR_OUMLAUT;
5894 case 0x15fb: // (green)
5895 element = EL_CHAR_UUMLAUT;
5898 case 0x15fc: // (green)
5899 element = EL_CHAR_0;
5902 case 0x15fd: // (green)
5903 element = EL_CHAR_1;
5906 case 0x15fe: // (green)
5907 element = EL_CHAR_2;
5910 case 0x15ff: // (green)
5911 element = EL_CHAR_3;
5914 case 0x1600: // (green)
5915 element = EL_CHAR_4;
5918 case 0x1601: // (green)
5919 element = EL_CHAR_5;
5922 case 0x1602: // (green)
5923 element = EL_CHAR_6;
5926 case 0x1603: // (green)
5927 element = EL_CHAR_7;
5930 case 0x1604: // (green)
5931 element = EL_CHAR_8;
5934 case 0x1605: // (green)
5935 element = EL_CHAR_9;
5938 case 0x1606: // (green)
5939 element = EL_CHAR_PERIOD;
5942 case 0x1607: // (green)
5943 element = EL_CHAR_EXCLAM;
5946 case 0x1608: // (green)
5947 element = EL_CHAR_COLON;
5950 case 0x1609: // (green)
5951 element = EL_CHAR_LESS;
5954 case 0x160a: // (green)
5955 element = EL_CHAR_GREATER;
5958 case 0x160b: // (green)
5959 element = EL_CHAR_QUESTION;
5962 case 0x160c: // (green)
5963 element = EL_CHAR_COPYRIGHT;
5966 case 0x160d: // (green)
5967 element = EL_CHAR_UP;
5970 case 0x160e: // (green)
5971 element = EL_CHAR_DOWN;
5974 case 0x160f: // (green)
5975 element = EL_CHAR_BUTTON;
5978 case 0x1610: // (green)
5979 element = EL_CHAR_PLUS;
5982 case 0x1611: // (green)
5983 element = EL_CHAR_MINUS;
5986 case 0x1612: // (green)
5987 element = EL_CHAR_APOSTROPHE;
5990 case 0x1613: // (green)
5991 element = EL_CHAR_PARENLEFT;
5994 case 0x1614: // (green)
5995 element = EL_CHAR_PARENRIGHT;
5998 case 0x1615: // (blue steel)
5999 element = EL_STEEL_CHAR_A;
6002 case 0x1616: // (blue steel)
6003 element = EL_STEEL_CHAR_B;
6006 case 0x1617: // (blue steel)
6007 element = EL_STEEL_CHAR_C;
6010 case 0x1618: // (blue steel)
6011 element = EL_STEEL_CHAR_D;
6014 case 0x1619: // (blue steel)
6015 element = EL_STEEL_CHAR_E;
6018 case 0x161a: // (blue steel)
6019 element = EL_STEEL_CHAR_F;
6022 case 0x161b: // (blue steel)
6023 element = EL_STEEL_CHAR_G;
6026 case 0x161c: // (blue steel)
6027 element = EL_STEEL_CHAR_H;
6030 case 0x161d: // (blue steel)
6031 element = EL_STEEL_CHAR_I;
6034 case 0x161e: // (blue steel)
6035 element = EL_STEEL_CHAR_J;
6038 case 0x161f: // (blue steel)
6039 element = EL_STEEL_CHAR_K;
6042 case 0x1620: // (blue steel)
6043 element = EL_STEEL_CHAR_L;
6046 case 0x1621: // (blue steel)
6047 element = EL_STEEL_CHAR_M;
6050 case 0x1622: // (blue steel)
6051 element = EL_STEEL_CHAR_N;
6054 case 0x1623: // (blue steel)
6055 element = EL_STEEL_CHAR_O;
6058 case 0x1624: // (blue steel)
6059 element = EL_STEEL_CHAR_P;
6062 case 0x1625: // (blue steel)
6063 element = EL_STEEL_CHAR_Q;
6066 case 0x1626: // (blue steel)
6067 element = EL_STEEL_CHAR_R;
6070 case 0x1627: // (blue steel)
6071 element = EL_STEEL_CHAR_S;
6074 case 0x1628: // (blue steel)
6075 element = EL_STEEL_CHAR_T;
6078 case 0x1629: // (blue steel)
6079 element = EL_STEEL_CHAR_U;
6082 case 0x162a: // (blue steel)
6083 element = EL_STEEL_CHAR_V;
6086 case 0x162b: // (blue steel)
6087 element = EL_STEEL_CHAR_W;
6090 case 0x162c: // (blue steel)
6091 element = EL_STEEL_CHAR_X;
6094 case 0x162d: // (blue steel)
6095 element = EL_STEEL_CHAR_Y;
6098 case 0x162e: // (blue steel)
6099 element = EL_STEEL_CHAR_Z;
6102 case 0x162f: // (blue steel)
6103 element = EL_STEEL_CHAR_AUMLAUT;
6106 case 0x1630: // (blue steel)
6107 element = EL_STEEL_CHAR_OUMLAUT;
6110 case 0x1631: // (blue steel)
6111 element = EL_STEEL_CHAR_UUMLAUT;
6114 case 0x1632: // (blue steel)
6115 element = EL_STEEL_CHAR_0;
6118 case 0x1633: // (blue steel)
6119 element = EL_STEEL_CHAR_1;
6122 case 0x1634: // (blue steel)
6123 element = EL_STEEL_CHAR_2;
6126 case 0x1635: // (blue steel)
6127 element = EL_STEEL_CHAR_3;
6130 case 0x1636: // (blue steel)
6131 element = EL_STEEL_CHAR_4;
6134 case 0x1637: // (blue steel)
6135 element = EL_STEEL_CHAR_5;
6138 case 0x1638: // (blue steel)
6139 element = EL_STEEL_CHAR_6;
6142 case 0x1639: // (blue steel)
6143 element = EL_STEEL_CHAR_7;
6146 case 0x163a: // (blue steel)
6147 element = EL_STEEL_CHAR_8;
6150 case 0x163b: // (blue steel)
6151 element = EL_STEEL_CHAR_9;
6154 case 0x163c: // (blue steel)
6155 element = EL_STEEL_CHAR_PERIOD;
6158 case 0x163d: // (blue steel)
6159 element = EL_STEEL_CHAR_EXCLAM;
6162 case 0x163e: // (blue steel)
6163 element = EL_STEEL_CHAR_COLON;
6166 case 0x163f: // (blue steel)
6167 element = EL_STEEL_CHAR_LESS;
6170 case 0x1640: // (blue steel)
6171 element = EL_STEEL_CHAR_GREATER;
6174 case 0x1641: // (blue steel)
6175 element = EL_STEEL_CHAR_QUESTION;
6178 case 0x1642: // (blue steel)
6179 element = EL_STEEL_CHAR_COPYRIGHT;
6182 case 0x1643: // (blue steel)
6183 element = EL_STEEL_CHAR_UP;
6186 case 0x1644: // (blue steel)
6187 element = EL_STEEL_CHAR_DOWN;
6190 case 0x1645: // (blue steel)
6191 element = EL_STEEL_CHAR_BUTTON;
6194 case 0x1646: // (blue steel)
6195 element = EL_STEEL_CHAR_PLUS;
6198 case 0x1647: // (blue steel)
6199 element = EL_STEEL_CHAR_MINUS;
6202 case 0x1648: // (blue steel)
6203 element = EL_STEEL_CHAR_APOSTROPHE;
6206 case 0x1649: // (blue steel)
6207 element = EL_STEEL_CHAR_PARENLEFT;
6210 case 0x164a: // (blue steel)
6211 element = EL_STEEL_CHAR_PARENRIGHT;
6214 case 0x164b: // (green steel)
6215 element = EL_STEEL_CHAR_A;
6218 case 0x164c: // (green steel)
6219 element = EL_STEEL_CHAR_B;
6222 case 0x164d: // (green steel)
6223 element = EL_STEEL_CHAR_C;
6226 case 0x164e: // (green steel)
6227 element = EL_STEEL_CHAR_D;
6230 case 0x164f: // (green steel)
6231 element = EL_STEEL_CHAR_E;
6234 case 0x1650: // (green steel)
6235 element = EL_STEEL_CHAR_F;
6238 case 0x1651: // (green steel)
6239 element = EL_STEEL_CHAR_G;
6242 case 0x1652: // (green steel)
6243 element = EL_STEEL_CHAR_H;
6246 case 0x1653: // (green steel)
6247 element = EL_STEEL_CHAR_I;
6250 case 0x1654: // (green steel)
6251 element = EL_STEEL_CHAR_J;
6254 case 0x1655: // (green steel)
6255 element = EL_STEEL_CHAR_K;
6258 case 0x1656: // (green steel)
6259 element = EL_STEEL_CHAR_L;
6262 case 0x1657: // (green steel)
6263 element = EL_STEEL_CHAR_M;
6266 case 0x1658: // (green steel)
6267 element = EL_STEEL_CHAR_N;
6270 case 0x1659: // (green steel)
6271 element = EL_STEEL_CHAR_O;
6274 case 0x165a: // (green steel)
6275 element = EL_STEEL_CHAR_P;
6278 case 0x165b: // (green steel)
6279 element = EL_STEEL_CHAR_Q;
6282 case 0x165c: // (green steel)
6283 element = EL_STEEL_CHAR_R;
6286 case 0x165d: // (green steel)
6287 element = EL_STEEL_CHAR_S;
6290 case 0x165e: // (green steel)
6291 element = EL_STEEL_CHAR_T;
6294 case 0x165f: // (green steel)
6295 element = EL_STEEL_CHAR_U;
6298 case 0x1660: // (green steel)
6299 element = EL_STEEL_CHAR_V;
6302 case 0x1661: // (green steel)
6303 element = EL_STEEL_CHAR_W;
6306 case 0x1662: // (green steel)
6307 element = EL_STEEL_CHAR_X;
6310 case 0x1663: // (green steel)
6311 element = EL_STEEL_CHAR_Y;
6314 case 0x1664: // (green steel)
6315 element = EL_STEEL_CHAR_Z;
6318 case 0x1665: // (green steel)
6319 element = EL_STEEL_CHAR_AUMLAUT;
6322 case 0x1666: // (green steel)
6323 element = EL_STEEL_CHAR_OUMLAUT;
6326 case 0x1667: // (green steel)
6327 element = EL_STEEL_CHAR_UUMLAUT;
6330 case 0x1668: // (green steel)
6331 element = EL_STEEL_CHAR_0;
6334 case 0x1669: // (green steel)
6335 element = EL_STEEL_CHAR_1;
6338 case 0x166a: // (green steel)
6339 element = EL_STEEL_CHAR_2;
6342 case 0x166b: // (green steel)
6343 element = EL_STEEL_CHAR_3;
6346 case 0x166c: // (green steel)
6347 element = EL_STEEL_CHAR_4;
6350 case 0x166d: // (green steel)
6351 element = EL_STEEL_CHAR_5;
6354 case 0x166e: // (green steel)
6355 element = EL_STEEL_CHAR_6;
6358 case 0x166f: // (green steel)
6359 element = EL_STEEL_CHAR_7;
6362 case 0x1670: // (green steel)
6363 element = EL_STEEL_CHAR_8;
6366 case 0x1671: // (green steel)
6367 element = EL_STEEL_CHAR_9;
6370 case 0x1672: // (green steel)
6371 element = EL_STEEL_CHAR_PERIOD;
6374 case 0x1673: // (green steel)
6375 element = EL_STEEL_CHAR_EXCLAM;
6378 case 0x1674: // (green steel)
6379 element = EL_STEEL_CHAR_COLON;
6382 case 0x1675: // (green steel)
6383 element = EL_STEEL_CHAR_LESS;
6386 case 0x1676: // (green steel)
6387 element = EL_STEEL_CHAR_GREATER;
6390 case 0x1677: // (green steel)
6391 element = EL_STEEL_CHAR_QUESTION;
6394 case 0x1678: // (green steel)
6395 element = EL_STEEL_CHAR_COPYRIGHT;
6398 case 0x1679: // (green steel)
6399 element = EL_STEEL_CHAR_UP;
6402 case 0x167a: // (green steel)
6403 element = EL_STEEL_CHAR_DOWN;
6406 case 0x167b: // (green steel)
6407 element = EL_STEEL_CHAR_BUTTON;
6410 case 0x167c: // (green steel)
6411 element = EL_STEEL_CHAR_PLUS;
6414 case 0x167d: // (green steel)
6415 element = EL_STEEL_CHAR_MINUS;
6418 case 0x167e: // (green steel)
6419 element = EL_STEEL_CHAR_APOSTROPHE;
6422 case 0x167f: // (green steel)
6423 element = EL_STEEL_CHAR_PARENLEFT;
6426 case 0x1680: // (green steel)
6427 element = EL_STEEL_CHAR_PARENRIGHT;
6430 case 0x1681: // gate (red)
6431 element = EL_EM_GATE_1;
6434 case 0x1682: // secret gate (red)
6435 element = EL_EM_GATE_1_GRAY;
6438 case 0x1683: // gate (yellow)
6439 element = EL_EM_GATE_2;
6442 case 0x1684: // secret gate (yellow)
6443 element = EL_EM_GATE_2_GRAY;
6446 case 0x1685: // gate (blue)
6447 element = EL_EM_GATE_4;
6450 case 0x1686: // secret gate (blue)
6451 element = EL_EM_GATE_4_GRAY;
6454 case 0x1687: // gate (green)
6455 element = EL_EM_GATE_3;
6458 case 0x1688: // secret gate (green)
6459 element = EL_EM_GATE_3_GRAY;
6462 case 0x1689: // gate (white)
6463 element = EL_DC_GATE_WHITE;
6466 case 0x168a: // secret gate (white)
6467 element = EL_DC_GATE_WHITE_GRAY;
6470 case 0x168b: // secret gate (no key)
6471 element = EL_DC_GATE_FAKE_GRAY;
6475 element = EL_ROBOT_WHEEL;
6479 element = EL_DC_TIMEGATE_SWITCH;
6483 element = EL_ACID_POOL_BOTTOM;
6487 element = EL_ACID_POOL_TOPLEFT;
6491 element = EL_ACID_POOL_TOPRIGHT;
6495 element = EL_ACID_POOL_BOTTOMLEFT;
6499 element = EL_ACID_POOL_BOTTOMRIGHT;
6503 element = EL_STEELWALL;
6507 element = EL_STEELWALL_SLIPPERY;
6510 case 0x1695: // steel wall (not round)
6511 element = EL_STEELWALL;
6514 case 0x1696: // steel wall (left)
6515 element = EL_DC_STEELWALL_1_LEFT;
6518 case 0x1697: // steel wall (bottom)
6519 element = EL_DC_STEELWALL_1_BOTTOM;
6522 case 0x1698: // steel wall (right)
6523 element = EL_DC_STEELWALL_1_RIGHT;
6526 case 0x1699: // steel wall (top)
6527 element = EL_DC_STEELWALL_1_TOP;
6530 case 0x169a: // steel wall (left/bottom)
6531 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6534 case 0x169b: // steel wall (right/bottom)
6535 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6538 case 0x169c: // steel wall (right/top)
6539 element = EL_DC_STEELWALL_1_TOPRIGHT;
6542 case 0x169d: // steel wall (left/top)
6543 element = EL_DC_STEELWALL_1_TOPLEFT;
6546 case 0x169e: // steel wall (right/bottom small)
6547 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6550 case 0x169f: // steel wall (left/bottom small)
6551 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6554 case 0x16a0: // steel wall (right/top small)
6555 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6558 case 0x16a1: // steel wall (left/top small)
6559 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6562 case 0x16a2: // steel wall (left/right)
6563 element = EL_DC_STEELWALL_1_VERTICAL;
6566 case 0x16a3: // steel wall (top/bottom)
6567 element = EL_DC_STEELWALL_1_HORIZONTAL;
6570 case 0x16a4: // steel wall 2 (left end)
6571 element = EL_DC_STEELWALL_2_LEFT;
6574 case 0x16a5: // steel wall 2 (right end)
6575 element = EL_DC_STEELWALL_2_RIGHT;
6578 case 0x16a6: // steel wall 2 (top end)
6579 element = EL_DC_STEELWALL_2_TOP;
6582 case 0x16a7: // steel wall 2 (bottom end)
6583 element = EL_DC_STEELWALL_2_BOTTOM;
6586 case 0x16a8: // steel wall 2 (left/right)
6587 element = EL_DC_STEELWALL_2_HORIZONTAL;
6590 case 0x16a9: // steel wall 2 (up/down)
6591 element = EL_DC_STEELWALL_2_VERTICAL;
6594 case 0x16aa: // steel wall 2 (mid)
6595 element = EL_DC_STEELWALL_2_MIDDLE;
6599 element = EL_SIGN_EXCLAMATION;
6603 element = EL_SIGN_RADIOACTIVITY;
6607 element = EL_SIGN_STOP;
6611 element = EL_SIGN_WHEELCHAIR;
6615 element = EL_SIGN_PARKING;
6619 element = EL_SIGN_NO_ENTRY;
6623 element = EL_SIGN_HEART;
6627 element = EL_SIGN_GIVE_WAY;
6631 element = EL_SIGN_ENTRY_FORBIDDEN;
6635 element = EL_SIGN_EMERGENCY_EXIT;
6639 element = EL_SIGN_YIN_YANG;
6643 element = EL_WALL_EMERALD;
6647 element = EL_WALL_DIAMOND;
6651 element = EL_WALL_PEARL;
6655 element = EL_WALL_CRYSTAL;
6659 element = EL_INVISIBLE_WALL;
6663 element = EL_INVISIBLE_STEELWALL;
6667 // EL_INVISIBLE_SAND
6670 element = EL_LIGHT_SWITCH;
6674 element = EL_ENVELOPE_1;
6678 if (element >= 0x0117 && element <= 0x036e) // (?)
6679 element = EL_DIAMOND;
6680 else if (element >= 0x042d && element <= 0x0684) // (?)
6681 element = EL_EMERALD;
6682 else if (element >= 0x157c && element <= 0x158b)
6684 else if (element >= 0x1590 && element <= 0x159f)
6685 element = EL_DC_LANDMINE;
6686 else if (element >= 0x16bc && element <= 0x16cb)
6687 element = EL_INVISIBLE_SAND;
6690 Warn("unknown Diamond Caves element 0x%04x", element);
6692 element = EL_UNKNOWN;
6697 return getMappedElement(element);
6700 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6702 byte header[DC_LEVEL_HEADER_SIZE];
6704 int envelope_header_pos = 62;
6705 int envelope_content_pos = 94;
6706 int level_name_pos = 251;
6707 int level_author_pos = 292;
6708 int envelope_header_len;
6709 int envelope_content_len;
6711 int level_author_len;
6713 int num_yamyam_contents;
6716 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6718 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6720 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6722 header[i * 2 + 0] = header_word >> 8;
6723 header[i * 2 + 1] = header_word & 0xff;
6726 // read some values from level header to check level decoding integrity
6727 fieldx = header[6] | (header[7] << 8);
6728 fieldy = header[8] | (header[9] << 8);
6729 num_yamyam_contents = header[60] | (header[61] << 8);
6731 // do some simple sanity checks to ensure that level was correctly decoded
6732 if (fieldx < 1 || fieldx > 256 ||
6733 fieldy < 1 || fieldy > 256 ||
6734 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6736 level->no_valid_file = TRUE;
6738 Warn("cannot decode level from stream -- using empty level");
6743 // maximum envelope header size is 31 bytes
6744 envelope_header_len = header[envelope_header_pos];
6745 // maximum envelope content size is 110 (156?) bytes
6746 envelope_content_len = header[envelope_content_pos];
6748 // maximum level title size is 40 bytes
6749 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6750 // maximum level author size is 30 (51?) bytes
6751 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6755 for (i = 0; i < envelope_header_len; i++)
6756 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6757 level->envelope[0].text[envelope_size++] =
6758 header[envelope_header_pos + 1 + i];
6760 if (envelope_header_len > 0 && envelope_content_len > 0)
6762 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6763 level->envelope[0].text[envelope_size++] = '\n';
6764 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6765 level->envelope[0].text[envelope_size++] = '\n';
6768 for (i = 0; i < envelope_content_len; i++)
6769 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6770 level->envelope[0].text[envelope_size++] =
6771 header[envelope_content_pos + 1 + i];
6773 level->envelope[0].text[envelope_size] = '\0';
6775 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6776 level->envelope[0].ysize = 10;
6777 level->envelope[0].autowrap = TRUE;
6778 level->envelope[0].centered = TRUE;
6780 for (i = 0; i < level_name_len; i++)
6781 level->name[i] = header[level_name_pos + 1 + i];
6782 level->name[level_name_len] = '\0';
6784 for (i = 0; i < level_author_len; i++)
6785 level->author[i] = header[level_author_pos + 1 + i];
6786 level->author[level_author_len] = '\0';
6788 num_yamyam_contents = header[60] | (header[61] << 8);
6789 level->num_yamyam_contents =
6790 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6792 for (i = 0; i < num_yamyam_contents; i++)
6794 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6796 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6797 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6799 if (i < MAX_ELEMENT_CONTENTS)
6800 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6804 fieldx = header[6] | (header[7] << 8);
6805 fieldy = header[8] | (header[9] << 8);
6806 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6807 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6809 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6811 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6812 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6814 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6815 level->field[x][y] = getMappedElement_DC(element_dc);
6818 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6819 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6820 level->field[x][y] = EL_PLAYER_1;
6822 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6823 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6824 level->field[x][y] = EL_PLAYER_2;
6826 level->gems_needed = header[18] | (header[19] << 8);
6828 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6829 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6830 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6831 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6832 level->score[SC_NUT] = header[28] | (header[29] << 8);
6833 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6834 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6835 level->score[SC_BUG] = header[34] | (header[35] << 8);
6836 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6837 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6838 level->score[SC_KEY] = header[40] | (header[41] << 8);
6839 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6841 level->time = header[44] | (header[45] << 8);
6843 level->amoeba_speed = header[46] | (header[47] << 8);
6844 level->time_light = header[48] | (header[49] << 8);
6845 level->time_timegate = header[50] | (header[51] << 8);
6846 level->time_wheel = header[52] | (header[53] << 8);
6847 level->time_magic_wall = header[54] | (header[55] << 8);
6848 level->extra_time = header[56] | (header[57] << 8);
6849 level->shield_normal_time = header[58] | (header[59] << 8);
6851 // shield and extra time elements do not have a score
6852 level->score[SC_SHIELD] = 0;
6853 level->extra_time_score = 0;
6855 // set time for normal and deadly shields to the same value
6856 level->shield_deadly_time = level->shield_normal_time;
6858 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6859 // can slip down from flat walls, like normal walls and steel walls
6860 level->em_slippery_gems = TRUE;
6862 // time score is counted for each 10 seconds left in Diamond Caves levels
6863 level->time_score_base = 10;
6866 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6867 struct LevelFileInfo *level_file_info,
6868 boolean level_info_only)
6870 char *filename = level_file_info->filename;
6872 int num_magic_bytes = 8;
6873 char magic_bytes[num_magic_bytes + 1];
6874 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6876 if (!(file = openFile(filename, MODE_READ)))
6878 level->no_valid_file = TRUE;
6880 if (!level_info_only)
6881 Warn("cannot read level '%s' -- using empty level", filename);
6886 // fseek(file, 0x0000, SEEK_SET);
6888 if (level_file_info->packed)
6890 // read "magic bytes" from start of file
6891 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6892 magic_bytes[0] = '\0';
6894 // check "magic bytes" for correct file format
6895 if (!strPrefix(magic_bytes, "DC2"))
6897 level->no_valid_file = TRUE;
6899 Warn("unknown DC level file '%s' -- using empty level", filename);
6904 if (strPrefix(magic_bytes, "DC2Win95") ||
6905 strPrefix(magic_bytes, "DC2Win98"))
6907 int position_first_level = 0x00fa;
6908 int extra_bytes = 4;
6911 // advance file stream to first level inside the level package
6912 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6914 // each block of level data is followed by block of non-level data
6915 num_levels_to_skip *= 2;
6917 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6918 while (num_levels_to_skip >= 0)
6920 // advance file stream to next level inside the level package
6921 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6923 level->no_valid_file = TRUE;
6925 Warn("cannot fseek in file '%s' -- using empty level", filename);
6930 // skip apparently unused extra bytes following each level
6931 ReadUnusedBytesFromFile(file, extra_bytes);
6933 // read size of next level in level package
6934 skip_bytes = getFile32BitLE(file);
6936 num_levels_to_skip--;
6941 level->no_valid_file = TRUE;
6943 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6949 LoadLevelFromFileStream_DC(file, level);
6955 // ----------------------------------------------------------------------------
6956 // functions for loading SB level
6957 // ----------------------------------------------------------------------------
6959 int getMappedElement_SB(int element_ascii, boolean use_ces)
6967 sb_element_mapping[] =
6969 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6970 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6971 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6972 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6973 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6974 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6975 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6976 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6983 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6984 if (element_ascii == sb_element_mapping[i].ascii)
6985 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6987 return EL_UNDEFINED;
6990 static void SetLevelSettings_SB(struct LevelInfo *level)
6994 level->use_step_counter = TRUE;
6997 level->score[SC_TIME_BONUS] = 0;
6998 level->time_score_base = 1;
6999 level->rate_time_over_score = TRUE;
7002 level->auto_exit_sokoban = TRUE;
7005 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7006 struct LevelFileInfo *level_file_info,
7007 boolean level_info_only)
7009 char *filename = level_file_info->filename;
7010 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7011 char last_comment[MAX_LINE_LEN];
7012 char level_name[MAX_LINE_LEN];
7015 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7016 boolean read_continued_line = FALSE;
7017 boolean reading_playfield = FALSE;
7018 boolean got_valid_playfield_line = FALSE;
7019 boolean invalid_playfield_char = FALSE;
7020 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7021 int file_level_nr = 0;
7022 int x = 0, y = 0; // initialized to make compilers happy
7024 last_comment[0] = '\0';
7025 level_name[0] = '\0';
7027 if (!(file = openFile(filename, MODE_READ)))
7029 level->no_valid_file = TRUE;
7031 if (!level_info_only)
7032 Warn("cannot read level '%s' -- using empty level", filename);
7037 while (!checkEndOfFile(file))
7039 // level successfully read, but next level may follow here
7040 if (!got_valid_playfield_line && reading_playfield)
7042 // read playfield from single level file -- skip remaining file
7043 if (!level_file_info->packed)
7046 if (file_level_nr >= num_levels_to_skip)
7051 last_comment[0] = '\0';
7052 level_name[0] = '\0';
7054 reading_playfield = FALSE;
7057 got_valid_playfield_line = FALSE;
7059 // read next line of input file
7060 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7063 // cut trailing line break (this can be newline and/or carriage return)
7064 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7065 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7068 // copy raw input line for later use (mainly debugging output)
7069 strcpy(line_raw, line);
7071 if (read_continued_line)
7073 // append new line to existing line, if there is enough space
7074 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7075 strcat(previous_line, line_ptr);
7077 strcpy(line, previous_line); // copy storage buffer to line
7079 read_continued_line = FALSE;
7082 // if the last character is '\', continue at next line
7083 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7085 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7086 strcpy(previous_line, line); // copy line to storage buffer
7088 read_continued_line = TRUE;
7094 if (line[0] == '\0')
7097 // extract comment text from comment line
7100 for (line_ptr = line; *line_ptr; line_ptr++)
7101 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7104 strcpy(last_comment, line_ptr);
7109 // extract level title text from line containing level title
7110 if (line[0] == '\'')
7112 strcpy(level_name, &line[1]);
7114 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7115 level_name[strlen(level_name) - 1] = '\0';
7120 // skip lines containing only spaces (or empty lines)
7121 for (line_ptr = line; *line_ptr; line_ptr++)
7122 if (*line_ptr != ' ')
7124 if (*line_ptr == '\0')
7127 // at this point, we have found a line containing part of a playfield
7129 got_valid_playfield_line = TRUE;
7131 if (!reading_playfield)
7133 reading_playfield = TRUE;
7134 invalid_playfield_char = FALSE;
7136 for (x = 0; x < MAX_LEV_FIELDX; x++)
7137 for (y = 0; y < MAX_LEV_FIELDY; y++)
7138 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7143 // start with topmost tile row
7147 // skip playfield line if larger row than allowed
7148 if (y >= MAX_LEV_FIELDY)
7151 // start with leftmost tile column
7154 // read playfield elements from line
7155 for (line_ptr = line; *line_ptr; line_ptr++)
7157 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7159 // stop parsing playfield line if larger column than allowed
7160 if (x >= MAX_LEV_FIELDX)
7163 if (mapped_sb_element == EL_UNDEFINED)
7165 invalid_playfield_char = TRUE;
7170 level->field[x][y] = mapped_sb_element;
7172 // continue with next tile column
7175 level->fieldx = MAX(x, level->fieldx);
7178 if (invalid_playfield_char)
7180 // if first playfield line, treat invalid lines as comment lines
7182 reading_playfield = FALSE;
7187 // continue with next tile row
7195 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7196 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7198 if (!reading_playfield)
7200 level->no_valid_file = TRUE;
7202 Warn("cannot read level '%s' -- using empty level", filename);
7207 if (*level_name != '\0')
7209 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7210 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7212 else if (*last_comment != '\0')
7214 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7215 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7219 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7222 // set all empty fields beyond the border walls to invisible steel wall
7223 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7225 if ((x == 0 || x == level->fieldx - 1 ||
7226 y == 0 || y == level->fieldy - 1) &&
7227 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7228 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7229 level->field, level->fieldx, level->fieldy);
7232 // set special level settings for Sokoban levels
7233 SetLevelSettings_SB(level);
7235 if (load_xsb_to_ces)
7237 // special global settings can now be set in level template
7238 level->use_custom_template = TRUE;
7243 // -------------------------------------------------------------------------
7244 // functions for handling native levels
7245 // -------------------------------------------------------------------------
7247 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7248 struct LevelFileInfo *level_file_info,
7249 boolean level_info_only)
7253 // determine position of requested level inside level package
7254 if (level_file_info->packed)
7255 pos = level_file_info->nr - leveldir_current->first_level;
7257 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7258 level->no_valid_file = TRUE;
7261 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7262 struct LevelFileInfo *level_file_info,
7263 boolean level_info_only)
7265 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7266 level->no_valid_file = TRUE;
7269 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7270 struct LevelFileInfo *level_file_info,
7271 boolean level_info_only)
7275 // determine position of requested level inside level package
7276 if (level_file_info->packed)
7277 pos = level_file_info->nr - leveldir_current->first_level;
7279 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7280 level->no_valid_file = TRUE;
7283 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7284 struct LevelFileInfo *level_file_info,
7285 boolean level_info_only)
7287 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7288 level->no_valid_file = TRUE;
7291 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7293 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7294 CopyNativeLevel_RND_to_BD(level);
7295 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7296 CopyNativeLevel_RND_to_EM(level);
7297 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7298 CopyNativeLevel_RND_to_SP(level);
7299 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7300 CopyNativeLevel_RND_to_MM(level);
7303 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7305 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7306 CopyNativeLevel_BD_to_RND(level);
7307 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7308 CopyNativeLevel_EM_to_RND(level);
7309 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7310 CopyNativeLevel_SP_to_RND(level);
7311 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7312 CopyNativeLevel_MM_to_RND(level);
7315 void SaveNativeLevel(struct LevelInfo *level)
7317 // saving native level files only supported for some game engines
7318 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7319 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7322 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7323 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7324 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7325 char *filename = getLevelFilenameFromBasename(basename);
7327 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7330 boolean success = FALSE;
7332 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7334 CopyNativeLevel_RND_to_BD(level);
7335 // CopyNativeTape_RND_to_BD(level);
7337 success = SaveNativeLevel_BD(filename);
7339 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7341 CopyNativeLevel_RND_to_SP(level);
7342 CopyNativeTape_RND_to_SP(level);
7344 success = SaveNativeLevel_SP(filename);
7348 Request("Native level file saved!", REQ_CONFIRM);
7350 Request("Failed to save native level file!", REQ_CONFIRM);
7354 // ----------------------------------------------------------------------------
7355 // functions for loading generic level
7356 // ----------------------------------------------------------------------------
7358 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7359 struct LevelFileInfo *level_file_info,
7360 boolean level_info_only)
7362 // always start with reliable default values
7363 setLevelInfoToDefaults(level, level_info_only, TRUE);
7365 switch (level_file_info->type)
7367 case LEVEL_FILE_TYPE_RND:
7368 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7371 case LEVEL_FILE_TYPE_BD:
7372 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7373 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7376 case LEVEL_FILE_TYPE_EM:
7377 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7378 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7381 case LEVEL_FILE_TYPE_SP:
7382 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7383 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7386 case LEVEL_FILE_TYPE_MM:
7387 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7388 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7391 case LEVEL_FILE_TYPE_DC:
7392 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7395 case LEVEL_FILE_TYPE_SB:
7396 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7400 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7404 // if level file is invalid, restore level structure to default values
7405 if (level->no_valid_file)
7406 setLevelInfoToDefaults(level, level_info_only, FALSE);
7408 if (check_special_flags("use_native_bd_game_engine"))
7409 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7411 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7412 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7414 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7415 CopyNativeLevel_Native_to_RND(level);
7418 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7420 static struct LevelFileInfo level_file_info;
7422 // always start with reliable default values
7423 setFileInfoToDefaults(&level_file_info);
7425 level_file_info.nr = 0; // unknown level number
7426 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7428 setString(&level_file_info.filename, filename);
7430 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7433 static void LoadLevel_InitVersion(struct LevelInfo *level)
7437 if (leveldir_current == NULL) // only when dumping level
7440 // all engine modifications also valid for levels which use latest engine
7441 if (level->game_version < VERSION_IDENT(3,2,0,5))
7443 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7444 level->time_score_base = 10;
7447 if (leveldir_current->latest_engine)
7449 // ---------- use latest game engine --------------------------------------
7451 /* For all levels which are forced to use the latest game engine version
7452 (normally all but user contributed, private and undefined levels), set
7453 the game engine version to the actual version; this allows for actual
7454 corrections in the game engine to take effect for existing, converted
7455 levels (from "classic" or other existing games) to make the emulation
7456 of the corresponding game more accurate, while (hopefully) not breaking
7457 existing levels created from other players. */
7459 level->game_version = GAME_VERSION_ACTUAL;
7461 /* Set special EM style gems behaviour: EM style gems slip down from
7462 normal, steel and growing wall. As this is a more fundamental change,
7463 it seems better to set the default behaviour to "off" (as it is more
7464 natural) and make it configurable in the level editor (as a property
7465 of gem style elements). Already existing converted levels (neither
7466 private nor contributed levels) are changed to the new behaviour. */
7468 if (level->file_version < FILE_VERSION_2_0)
7469 level->em_slippery_gems = TRUE;
7474 // ---------- use game engine the level was created with --------------------
7476 /* For all levels which are not forced to use the latest game engine
7477 version (normally user contributed, private and undefined levels),
7478 use the version of the game engine the levels were created for.
7480 Since 2.0.1, the game engine version is now directly stored
7481 in the level file (chunk "VERS"), so there is no need anymore
7482 to set the game version from the file version (except for old,
7483 pre-2.0 levels, where the game version is still taken from the
7484 file format version used to store the level -- see above). */
7486 // player was faster than enemies in 1.0.0 and before
7487 if (level->file_version == FILE_VERSION_1_0)
7488 for (i = 0; i < MAX_PLAYERS; i++)
7489 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7491 // default behaviour for EM style gems was "slippery" only in 2.0.1
7492 if (level->game_version == VERSION_IDENT(2,0,1,0))
7493 level->em_slippery_gems = TRUE;
7495 // springs could be pushed over pits before (pre-release version) 2.2.0
7496 if (level->game_version < VERSION_IDENT(2,2,0,0))
7497 level->use_spring_bug = TRUE;
7499 if (level->game_version < VERSION_IDENT(3,2,0,5))
7501 // time orb caused limited time in endless time levels before 3.2.0-5
7502 level->use_time_orb_bug = TRUE;
7504 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7505 level->block_snap_field = FALSE;
7507 // extra time score was same value as time left score before 3.2.0-5
7508 level->extra_time_score = level->score[SC_TIME_BONUS];
7511 if (level->game_version < VERSION_IDENT(3,2,0,7))
7513 // default behaviour for snapping was "not continuous" before 3.2.0-7
7514 level->continuous_snapping = FALSE;
7517 // only few elements were able to actively move into acid before 3.1.0
7518 // trigger settings did not exist before 3.1.0; set to default "any"
7519 if (level->game_version < VERSION_IDENT(3,1,0,0))
7521 // correct "can move into acid" settings (all zero in old levels)
7523 level->can_move_into_acid_bits = 0; // nothing can move into acid
7524 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7526 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7527 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7528 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7529 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7531 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7532 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7534 // correct trigger settings (stored as zero == "none" in old levels)
7536 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7538 int element = EL_CUSTOM_START + i;
7539 struct ElementInfo *ei = &element_info[element];
7541 for (j = 0; j < ei->num_change_pages; j++)
7543 struct ElementChangeInfo *change = &ei->change_page[j];
7545 change->trigger_player = CH_PLAYER_ANY;
7546 change->trigger_page = CH_PAGE_ANY;
7551 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7553 int element = EL_CUSTOM_256;
7554 struct ElementInfo *ei = &element_info[element];
7555 struct ElementChangeInfo *change = &ei->change_page[0];
7557 /* This is needed to fix a problem that was caused by a bugfix in function
7558 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7559 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7560 not replace walkable elements, but instead just placed the player on it,
7561 without placing the Sokoban field under the player). Unfortunately, this
7562 breaks "Snake Bite" style levels when the snake is halfway through a door
7563 that just closes (the snake head is still alive and can be moved in this
7564 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7565 player (without Sokoban element) which then gets killed as designed). */
7567 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7568 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7569 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7570 change->target_element = EL_PLAYER_1;
7573 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7574 if (level->game_version < VERSION_IDENT(3,2,5,0))
7576 /* This is needed to fix a problem that was caused by a bugfix in function
7577 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7578 corrects the behaviour when a custom element changes to another custom
7579 element with a higher element number that has change actions defined.
7580 Normally, only one change per frame is allowed for custom elements.
7581 Therefore, it is checked if a custom element already changed in the
7582 current frame; if it did, subsequent changes are suppressed.
7583 Unfortunately, this is only checked for element changes, but not for
7584 change actions, which are still executed. As the function above loops
7585 through all custom elements from lower to higher, an element change
7586 resulting in a lower CE number won't be checked again, while a target
7587 element with a higher number will also be checked, and potential change
7588 actions will get executed for this CE, too (which is wrong), while
7589 further changes are ignored (which is correct). As this bugfix breaks
7590 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7591 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7592 behaviour for existing levels and tapes that make use of this bug */
7594 level->use_action_after_change_bug = TRUE;
7597 // not centering level after relocating player was default only in 3.2.3
7598 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7599 level->shifted_relocation = TRUE;
7601 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7602 if (level->game_version < VERSION_IDENT(3,2,6,0))
7603 level->em_explodes_by_fire = TRUE;
7605 // levels were solved by the first player entering an exit up to 4.1.0.0
7606 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7607 level->solved_by_one_player = TRUE;
7609 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7610 if (level->game_version < VERSION_IDENT(4,1,1,1))
7611 level->use_life_bugs = TRUE;
7613 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7614 if (level->game_version < VERSION_IDENT(4,1,1,1))
7615 level->sb_objects_needed = FALSE;
7617 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7618 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7619 level->finish_dig_collect = FALSE;
7621 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7622 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7623 level->keep_walkable_ce = TRUE;
7626 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7628 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7631 // check if this level is (not) a Sokoban level
7632 for (y = 0; y < level->fieldy; y++)
7633 for (x = 0; x < level->fieldx; x++)
7634 if (!IS_SB_ELEMENT(Tile[x][y]))
7635 is_sokoban_level = FALSE;
7637 if (is_sokoban_level)
7639 // set special level settings for Sokoban levels
7640 SetLevelSettings_SB(level);
7644 static void LoadLevel_InitSettings(struct LevelInfo *level)
7646 // adjust level settings for (non-native) Sokoban-style levels
7647 LoadLevel_InitSettings_SB(level);
7649 // rename levels with title "nameless level" or if renaming is forced
7650 if (leveldir_current->empty_level_name != NULL &&
7651 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7652 leveldir_current->force_level_name))
7653 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7654 leveldir_current->empty_level_name, level_nr);
7657 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7661 // map elements that have changed in newer versions
7662 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7663 level->game_version);
7664 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7665 for (x = 0; x < 3; x++)
7666 for (y = 0; y < 3; y++)
7667 level->yamyam_content[i].e[x][y] =
7668 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7669 level->game_version);
7673 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7677 // map custom element change events that have changed in newer versions
7678 // (these following values were accidentally changed in version 3.0.1)
7679 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7680 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7682 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7684 int element = EL_CUSTOM_START + i;
7686 // order of checking and copying events to be mapped is important
7687 // (do not change the start and end value -- they are constant)
7688 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7690 if (HAS_CHANGE_EVENT(element, j - 2))
7692 SET_CHANGE_EVENT(element, j - 2, FALSE);
7693 SET_CHANGE_EVENT(element, j, TRUE);
7697 // order of checking and copying events to be mapped is important
7698 // (do not change the start and end value -- they are constant)
7699 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7701 if (HAS_CHANGE_EVENT(element, j - 1))
7703 SET_CHANGE_EVENT(element, j - 1, FALSE);
7704 SET_CHANGE_EVENT(element, j, TRUE);
7710 // initialize "can_change" field for old levels with only one change page
7711 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7713 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7715 int element = EL_CUSTOM_START + i;
7717 if (CAN_CHANGE(element))
7718 element_info[element].change->can_change = TRUE;
7722 // correct custom element values (for old levels without these options)
7723 if (level->game_version < VERSION_IDENT(3,1,1,0))
7725 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7727 int element = EL_CUSTOM_START + i;
7728 struct ElementInfo *ei = &element_info[element];
7730 if (ei->access_direction == MV_NO_DIRECTION)
7731 ei->access_direction = MV_ALL_DIRECTIONS;
7735 // correct custom element values (fix invalid values for all versions)
7738 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7740 int element = EL_CUSTOM_START + i;
7741 struct ElementInfo *ei = &element_info[element];
7743 for (j = 0; j < ei->num_change_pages; j++)
7745 struct ElementChangeInfo *change = &ei->change_page[j];
7747 if (change->trigger_player == CH_PLAYER_NONE)
7748 change->trigger_player = CH_PLAYER_ANY;
7750 if (change->trigger_side == CH_SIDE_NONE)
7751 change->trigger_side = CH_SIDE_ANY;
7756 // initialize "can_explode" field for old levels which did not store this
7757 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7758 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7760 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7762 int element = EL_CUSTOM_START + i;
7764 if (EXPLODES_1X1_OLD(element))
7765 element_info[element].explosion_type = EXPLODES_1X1;
7767 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7768 EXPLODES_SMASHED(element) ||
7769 EXPLODES_IMPACT(element)));
7773 // correct previously hard-coded move delay values for maze runner style
7774 if (level->game_version < VERSION_IDENT(3,1,1,0))
7776 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7778 int element = EL_CUSTOM_START + i;
7780 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7782 // previously hard-coded and therefore ignored
7783 element_info[element].move_delay_fixed = 9;
7784 element_info[element].move_delay_random = 0;
7789 // set some other uninitialized values of custom elements in older levels
7790 if (level->game_version < VERSION_IDENT(3,1,0,0))
7792 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7794 int element = EL_CUSTOM_START + i;
7796 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7798 element_info[element].explosion_delay = 17;
7799 element_info[element].ignition_delay = 8;
7803 // set mouse click change events to work for left/middle/right mouse button
7804 if (level->game_version < VERSION_IDENT(4,2,3,0))
7806 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7808 int element = EL_CUSTOM_START + i;
7809 struct ElementInfo *ei = &element_info[element];
7811 for (j = 0; j < ei->num_change_pages; j++)
7813 struct ElementChangeInfo *change = &ei->change_page[j];
7815 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7816 change->has_event[CE_PRESSED_BY_MOUSE] ||
7817 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7818 change->has_event[CE_MOUSE_PRESSED_ON_X])
7819 change->trigger_side = CH_SIDE_ANY;
7825 static void LoadLevel_InitElements(struct LevelInfo *level)
7827 LoadLevel_InitStandardElements(level);
7829 if (level->file_has_custom_elements)
7830 LoadLevel_InitCustomElements(level);
7832 // initialize element properties for level editor etc.
7833 InitElementPropertiesEngine(level->game_version);
7834 InitElementPropertiesGfxElement();
7837 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7841 // map elements that have changed in newer versions
7842 for (y = 0; y < level->fieldy; y++)
7843 for (x = 0; x < level->fieldx; x++)
7844 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7845 level->game_version);
7847 // clear unused playfield data (nicer if level gets resized in editor)
7848 for (x = 0; x < MAX_LEV_FIELDX; x++)
7849 for (y = 0; y < MAX_LEV_FIELDY; y++)
7850 if (x >= level->fieldx || y >= level->fieldy)
7851 level->field[x][y] = EL_EMPTY;
7853 // copy elements to runtime playfield array
7854 for (x = 0; x < MAX_LEV_FIELDX; x++)
7855 for (y = 0; y < MAX_LEV_FIELDY; y++)
7856 Tile[x][y] = level->field[x][y];
7858 // initialize level size variables for faster access
7859 lev_fieldx = level->fieldx;
7860 lev_fieldy = level->fieldy;
7862 // determine border element for this level
7863 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7864 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7869 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7871 struct LevelFileInfo *level_file_info = &level->file_info;
7873 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7874 CopyNativeLevel_RND_to_Native(level);
7877 static void LoadLevelTemplate_LoadAndInit(void)
7879 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7881 LoadLevel_InitVersion(&level_template);
7882 LoadLevel_InitElements(&level_template);
7883 LoadLevel_InitSettings(&level_template);
7885 ActivateLevelTemplate();
7888 void LoadLevelTemplate(int nr)
7890 if (!fileExists(getGlobalLevelTemplateFilename()))
7892 Warn("no level template found for this level");
7897 setLevelFileInfo(&level_template.file_info, nr);
7899 LoadLevelTemplate_LoadAndInit();
7902 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7904 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7906 LoadLevelTemplate_LoadAndInit();
7909 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7911 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7913 if (level.use_custom_template)
7915 if (network_level != NULL)
7916 LoadNetworkLevelTemplate(network_level);
7918 LoadLevelTemplate(-1);
7921 LoadLevel_InitVersion(&level);
7922 LoadLevel_InitElements(&level);
7923 LoadLevel_InitPlayfield(&level);
7924 LoadLevel_InitSettings(&level);
7926 LoadLevel_InitNativeEngines(&level);
7929 void LoadLevel(int nr)
7931 SetLevelSetInfo(leveldir_current->identifier, nr);
7933 setLevelFileInfo(&level.file_info, nr);
7935 LoadLevel_LoadAndInit(NULL);
7938 void LoadLevelInfoOnly(int nr)
7940 setLevelFileInfo(&level.file_info, nr);
7942 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7945 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7947 SetLevelSetInfo(network_level->leveldir_identifier,
7948 network_level->file_info.nr);
7950 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7952 LoadLevel_LoadAndInit(network_level);
7955 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7959 chunk_size += putFileVersion(file, level->file_version);
7960 chunk_size += putFileVersion(file, level->game_version);
7965 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7969 chunk_size += putFile16BitBE(file, level->creation_date.year);
7970 chunk_size += putFile8Bit(file, level->creation_date.month);
7971 chunk_size += putFile8Bit(file, level->creation_date.day);
7976 #if ENABLE_HISTORIC_CHUNKS
7977 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7981 putFile8Bit(file, level->fieldx);
7982 putFile8Bit(file, level->fieldy);
7984 putFile16BitBE(file, level->time);
7985 putFile16BitBE(file, level->gems_needed);
7987 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7988 putFile8Bit(file, level->name[i]);
7990 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7991 putFile8Bit(file, level->score[i]);
7993 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7994 for (y = 0; y < 3; y++)
7995 for (x = 0; x < 3; x++)
7996 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7997 level->yamyam_content[i].e[x][y]));
7998 putFile8Bit(file, level->amoeba_speed);
7999 putFile8Bit(file, level->time_magic_wall);
8000 putFile8Bit(file, level->time_wheel);
8001 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8002 level->amoeba_content));
8003 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8004 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8005 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8006 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8008 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8010 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8011 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8012 putFile32BitBE(file, level->can_move_into_acid_bits);
8013 putFile8Bit(file, level->dont_collide_with_bits);
8015 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8016 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8018 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8019 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8020 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8022 putFile8Bit(file, level->game_engine_type);
8024 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8028 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8033 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8034 chunk_size += putFile8Bit(file, level->name[i]);
8039 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8044 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8045 chunk_size += putFile8Bit(file, level->author[i]);
8050 #if ENABLE_HISTORIC_CHUNKS
8051 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8056 for (y = 0; y < level->fieldy; y++)
8057 for (x = 0; x < level->fieldx; x++)
8058 if (level->encoding_16bit_field)
8059 chunk_size += putFile16BitBE(file, level->field[x][y]);
8061 chunk_size += putFile8Bit(file, level->field[x][y]);
8067 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8072 for (y = 0; y < level->fieldy; y++)
8073 for (x = 0; x < level->fieldx; x++)
8074 chunk_size += putFile16BitBE(file, level->field[x][y]);
8079 #if ENABLE_HISTORIC_CHUNKS
8080 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8084 putFile8Bit(file, EL_YAMYAM);
8085 putFile8Bit(file, level->num_yamyam_contents);
8086 putFile8Bit(file, 0);
8087 putFile8Bit(file, 0);
8089 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8090 for (y = 0; y < 3; y++)
8091 for (x = 0; x < 3; x++)
8092 if (level->encoding_16bit_field)
8093 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8095 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8099 #if ENABLE_HISTORIC_CHUNKS
8100 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8103 int num_contents, content_xsize, content_ysize;
8104 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8106 if (element == EL_YAMYAM)
8108 num_contents = level->num_yamyam_contents;
8112 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8113 for (y = 0; y < 3; y++)
8114 for (x = 0; x < 3; x++)
8115 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8117 else if (element == EL_BD_AMOEBA)
8123 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8124 for (y = 0; y < 3; y++)
8125 for (x = 0; x < 3; x++)
8126 content_array[i][x][y] = EL_EMPTY;
8127 content_array[0][0][0] = level->amoeba_content;
8131 // chunk header already written -- write empty chunk data
8132 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8134 Warn("cannot save content for element '%d'", element);
8139 putFile16BitBE(file, element);
8140 putFile8Bit(file, num_contents);
8141 putFile8Bit(file, content_xsize);
8142 putFile8Bit(file, content_ysize);
8144 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8146 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8147 for (y = 0; y < 3; y++)
8148 for (x = 0; x < 3; x++)
8149 putFile16BitBE(file, content_array[i][x][y]);
8153 #if ENABLE_HISTORIC_CHUNKS
8154 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8156 int envelope_nr = element - EL_ENVELOPE_1;
8157 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8161 chunk_size += putFile16BitBE(file, element);
8162 chunk_size += putFile16BitBE(file, envelope_len);
8163 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8164 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8166 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8167 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8169 for (i = 0; i < envelope_len; i++)
8170 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8176 #if ENABLE_HISTORIC_CHUNKS
8177 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8178 int num_changed_custom_elements)
8182 putFile16BitBE(file, num_changed_custom_elements);
8184 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8186 int element = EL_CUSTOM_START + i;
8188 struct ElementInfo *ei = &element_info[element];
8190 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8192 if (check < num_changed_custom_elements)
8194 putFile16BitBE(file, element);
8195 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8202 if (check != num_changed_custom_elements) // should not happen
8203 Warn("inconsistent number of custom element properties");
8207 #if ENABLE_HISTORIC_CHUNKS
8208 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8209 int num_changed_custom_elements)
8213 putFile16BitBE(file, num_changed_custom_elements);
8215 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8217 int element = EL_CUSTOM_START + i;
8219 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8221 if (check < num_changed_custom_elements)
8223 putFile16BitBE(file, element);
8224 putFile16BitBE(file, element_info[element].change->target_element);
8231 if (check != num_changed_custom_elements) // should not happen
8232 Warn("inconsistent number of custom target elements");
8236 #if ENABLE_HISTORIC_CHUNKS
8237 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8238 int num_changed_custom_elements)
8240 int i, j, x, y, check = 0;
8242 putFile16BitBE(file, num_changed_custom_elements);
8244 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8246 int element = EL_CUSTOM_START + i;
8247 struct ElementInfo *ei = &element_info[element];
8249 if (ei->modified_settings)
8251 if (check < num_changed_custom_elements)
8253 putFile16BitBE(file, element);
8255 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8256 putFile8Bit(file, ei->description[j]);
8258 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8260 // some free bytes for future properties and padding
8261 WriteUnusedBytesToFile(file, 7);
8263 putFile8Bit(file, ei->use_gfx_element);
8264 putFile16BitBE(file, ei->gfx_element_initial);
8266 putFile8Bit(file, ei->collect_score_initial);
8267 putFile8Bit(file, ei->collect_count_initial);
8269 putFile16BitBE(file, ei->push_delay_fixed);
8270 putFile16BitBE(file, ei->push_delay_random);
8271 putFile16BitBE(file, ei->move_delay_fixed);
8272 putFile16BitBE(file, ei->move_delay_random);
8274 putFile16BitBE(file, ei->move_pattern);
8275 putFile8Bit(file, ei->move_direction_initial);
8276 putFile8Bit(file, ei->move_stepsize);
8278 for (y = 0; y < 3; y++)
8279 for (x = 0; x < 3; x++)
8280 putFile16BitBE(file, ei->content.e[x][y]);
8282 putFile32BitBE(file, ei->change->events);
8284 putFile16BitBE(file, ei->change->target_element);
8286 putFile16BitBE(file, ei->change->delay_fixed);
8287 putFile16BitBE(file, ei->change->delay_random);
8288 putFile16BitBE(file, ei->change->delay_frames);
8290 putFile16BitBE(file, ei->change->initial_trigger_element);
8292 putFile8Bit(file, ei->change->explode);
8293 putFile8Bit(file, ei->change->use_target_content);
8294 putFile8Bit(file, ei->change->only_if_complete);
8295 putFile8Bit(file, ei->change->use_random_replace);
8297 putFile8Bit(file, ei->change->random_percentage);
8298 putFile8Bit(file, ei->change->replace_when);
8300 for (y = 0; y < 3; y++)
8301 for (x = 0; x < 3; x++)
8302 putFile16BitBE(file, ei->change->content.e[x][y]);
8304 putFile8Bit(file, ei->slippery_type);
8306 // some free bytes for future properties and padding
8307 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8314 if (check != num_changed_custom_elements) // should not happen
8315 Warn("inconsistent number of custom element properties");
8319 #if ENABLE_HISTORIC_CHUNKS
8320 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8322 struct ElementInfo *ei = &element_info[element];
8325 // ---------- custom element base property values (96 bytes) ----------------
8327 putFile16BitBE(file, element);
8329 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8330 putFile8Bit(file, ei->description[i]);
8332 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8334 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8336 putFile8Bit(file, ei->num_change_pages);
8338 putFile16BitBE(file, ei->ce_value_fixed_initial);
8339 putFile16BitBE(file, ei->ce_value_random_initial);
8340 putFile8Bit(file, ei->use_last_ce_value);
8342 putFile8Bit(file, ei->use_gfx_element);
8343 putFile16BitBE(file, ei->gfx_element_initial);
8345 putFile8Bit(file, ei->collect_score_initial);
8346 putFile8Bit(file, ei->collect_count_initial);
8348 putFile8Bit(file, ei->drop_delay_fixed);
8349 putFile8Bit(file, ei->push_delay_fixed);
8350 putFile8Bit(file, ei->drop_delay_random);
8351 putFile8Bit(file, ei->push_delay_random);
8352 putFile16BitBE(file, ei->move_delay_fixed);
8353 putFile16BitBE(file, ei->move_delay_random);
8355 // bits 0 - 15 of "move_pattern" ...
8356 putFile16BitBE(file, ei->move_pattern & 0xffff);
8357 putFile8Bit(file, ei->move_direction_initial);
8358 putFile8Bit(file, ei->move_stepsize);
8360 putFile8Bit(file, ei->slippery_type);
8362 for (y = 0; y < 3; y++)
8363 for (x = 0; x < 3; x++)
8364 putFile16BitBE(file, ei->content.e[x][y]);
8366 putFile16BitBE(file, ei->move_enter_element);
8367 putFile16BitBE(file, ei->move_leave_element);
8368 putFile8Bit(file, ei->move_leave_type);
8370 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8371 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8373 putFile8Bit(file, ei->access_direction);
8375 putFile8Bit(file, ei->explosion_delay);
8376 putFile8Bit(file, ei->ignition_delay);
8377 putFile8Bit(file, ei->explosion_type);
8379 // some free bytes for future custom property values and padding
8380 WriteUnusedBytesToFile(file, 1);
8382 // ---------- change page property values (48 bytes) ------------------------
8384 for (i = 0; i < ei->num_change_pages; i++)
8386 struct ElementChangeInfo *change = &ei->change_page[i];
8387 unsigned int event_bits;
8389 // bits 0 - 31 of "has_event[]" ...
8391 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8392 if (change->has_event[j])
8393 event_bits |= (1u << j);
8394 putFile32BitBE(file, event_bits);
8396 putFile16BitBE(file, change->target_element);
8398 putFile16BitBE(file, change->delay_fixed);
8399 putFile16BitBE(file, change->delay_random);
8400 putFile16BitBE(file, change->delay_frames);
8402 putFile16BitBE(file, change->initial_trigger_element);
8404 putFile8Bit(file, change->explode);
8405 putFile8Bit(file, change->use_target_content);
8406 putFile8Bit(file, change->only_if_complete);
8407 putFile8Bit(file, change->use_random_replace);
8409 putFile8Bit(file, change->random_percentage);
8410 putFile8Bit(file, change->replace_when);
8412 for (y = 0; y < 3; y++)
8413 for (x = 0; x < 3; x++)
8414 putFile16BitBE(file, change->target_content.e[x][y]);
8416 putFile8Bit(file, change->can_change);
8418 putFile8Bit(file, change->trigger_side);
8420 putFile8Bit(file, change->trigger_player);
8421 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8422 log_2(change->trigger_page)));
8424 putFile8Bit(file, change->has_action);
8425 putFile8Bit(file, change->action_type);
8426 putFile8Bit(file, change->action_mode);
8427 putFile16BitBE(file, change->action_arg);
8429 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8431 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8432 if (change->has_event[j])
8433 event_bits |= (1u << (j - 32));
8434 putFile8Bit(file, event_bits);
8439 #if ENABLE_HISTORIC_CHUNKS
8440 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8442 struct ElementInfo *ei = &element_info[element];
8443 struct ElementGroupInfo *group = ei->group;
8446 putFile16BitBE(file, element);
8448 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8449 putFile8Bit(file, ei->description[i]);
8451 putFile8Bit(file, group->num_elements);
8453 putFile8Bit(file, ei->use_gfx_element);
8454 putFile16BitBE(file, ei->gfx_element_initial);
8456 putFile8Bit(file, group->choice_mode);
8458 // some free bytes for future values and padding
8459 WriteUnusedBytesToFile(file, 3);
8461 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8462 putFile16BitBE(file, group->element[i]);
8466 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8467 boolean write_element)
8469 int save_type = entry->save_type;
8470 int data_type = entry->data_type;
8471 int conf_type = entry->conf_type;
8472 int byte_mask = conf_type & CONF_MASK_BYTES;
8473 int element = entry->element;
8474 int default_value = entry->default_value;
8476 boolean modified = FALSE;
8478 if (byte_mask != CONF_MASK_MULTI_BYTES)
8480 void *value_ptr = entry->value;
8481 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8484 // check if any settings have been modified before saving them
8485 if (value != default_value)
8488 // do not save if explicitly told or if unmodified default settings
8489 if ((save_type == SAVE_CONF_NEVER) ||
8490 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8494 num_bytes += putFile16BitBE(file, element);
8496 num_bytes += putFile8Bit(file, conf_type);
8497 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8498 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8499 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8502 else if (data_type == TYPE_STRING)
8504 char *default_string = entry->default_string;
8505 char *string = (char *)(entry->value);
8506 int string_length = strlen(string);
8509 // check if any settings have been modified before saving them
8510 if (!strEqual(string, default_string))
8513 // do not save if explicitly told or if unmodified default settings
8514 if ((save_type == SAVE_CONF_NEVER) ||
8515 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8519 num_bytes += putFile16BitBE(file, element);
8521 num_bytes += putFile8Bit(file, conf_type);
8522 num_bytes += putFile16BitBE(file, string_length);
8524 for (i = 0; i < string_length; i++)
8525 num_bytes += putFile8Bit(file, string[i]);
8527 else if (data_type == TYPE_ELEMENT_LIST)
8529 int *element_array = (int *)(entry->value);
8530 int num_elements = *(int *)(entry->num_entities);
8533 // check if any settings have been modified before saving them
8534 for (i = 0; i < num_elements; i++)
8535 if (element_array[i] != default_value)
8538 // do not save if explicitly told or if unmodified default settings
8539 if ((save_type == SAVE_CONF_NEVER) ||
8540 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8544 num_bytes += putFile16BitBE(file, element);
8546 num_bytes += putFile8Bit(file, conf_type);
8547 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8549 for (i = 0; i < num_elements; i++)
8550 num_bytes += putFile16BitBE(file, element_array[i]);
8552 else if (data_type == TYPE_CONTENT_LIST)
8554 struct Content *content = (struct Content *)(entry->value);
8555 int num_contents = *(int *)(entry->num_entities);
8558 // check if any settings have been modified before saving them
8559 for (i = 0; i < num_contents; i++)
8560 for (y = 0; y < 3; y++)
8561 for (x = 0; x < 3; x++)
8562 if (content[i].e[x][y] != default_value)
8565 // do not save if explicitly told or if unmodified default settings
8566 if ((save_type == SAVE_CONF_NEVER) ||
8567 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8571 num_bytes += putFile16BitBE(file, element);
8573 num_bytes += putFile8Bit(file, conf_type);
8574 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8576 for (i = 0; i < num_contents; i++)
8577 for (y = 0; y < 3; y++)
8578 for (x = 0; x < 3; x++)
8579 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8585 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8590 li = *level; // copy level data into temporary buffer
8592 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8593 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8598 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8603 li = *level; // copy level data into temporary buffer
8605 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8606 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8611 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8613 int envelope_nr = element - EL_ENVELOPE_1;
8617 chunk_size += putFile16BitBE(file, element);
8619 // copy envelope data into temporary buffer
8620 xx_envelope = level->envelope[envelope_nr];
8622 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8623 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8628 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8630 struct ElementInfo *ei = &element_info[element];
8634 chunk_size += putFile16BitBE(file, element);
8636 xx_ei = *ei; // copy element data into temporary buffer
8638 // set default description string for this specific element
8639 strcpy(xx_default_description, getDefaultElementDescription(ei));
8641 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8642 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8644 for (i = 0; i < ei->num_change_pages; i++)
8646 struct ElementChangeInfo *change = &ei->change_page[i];
8648 xx_current_change_page = i;
8650 xx_change = *change; // copy change data into temporary buffer
8653 setEventBitsFromEventFlags(change);
8655 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8656 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8663 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8665 struct ElementInfo *ei = &element_info[element];
8666 struct ElementGroupInfo *group = ei->group;
8670 chunk_size += putFile16BitBE(file, element);
8672 xx_ei = *ei; // copy element data into temporary buffer
8673 xx_group = *group; // copy group data into temporary buffer
8675 // set default description string for this specific element
8676 strcpy(xx_default_description, getDefaultElementDescription(ei));
8678 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8679 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8684 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8686 struct ElementInfo *ei = &element_info[element];
8690 chunk_size += putFile16BitBE(file, element);
8692 xx_ei = *ei; // copy element data into temporary buffer
8694 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8695 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8700 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8701 boolean save_as_template)
8707 if (!(file = fopen(filename, MODE_WRITE)))
8709 Warn("cannot save level file '%s'", filename);
8714 level->file_version = FILE_VERSION_ACTUAL;
8715 level->game_version = GAME_VERSION_ACTUAL;
8717 level->creation_date = getCurrentDate();
8719 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8720 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8722 chunk_size = SaveLevel_VERS(NULL, level);
8723 putFileChunkBE(file, "VERS", chunk_size);
8724 SaveLevel_VERS(file, level);
8726 chunk_size = SaveLevel_DATE(NULL, level);
8727 putFileChunkBE(file, "DATE", chunk_size);
8728 SaveLevel_DATE(file, level);
8730 chunk_size = SaveLevel_NAME(NULL, level);
8731 putFileChunkBE(file, "NAME", chunk_size);
8732 SaveLevel_NAME(file, level);
8734 chunk_size = SaveLevel_AUTH(NULL, level);
8735 putFileChunkBE(file, "AUTH", chunk_size);
8736 SaveLevel_AUTH(file, level);
8738 chunk_size = SaveLevel_INFO(NULL, level);
8739 putFileChunkBE(file, "INFO", chunk_size);
8740 SaveLevel_INFO(file, level);
8742 chunk_size = SaveLevel_BODY(NULL, level);
8743 putFileChunkBE(file, "BODY", chunk_size);
8744 SaveLevel_BODY(file, level);
8746 chunk_size = SaveLevel_ELEM(NULL, level);
8747 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8749 putFileChunkBE(file, "ELEM", chunk_size);
8750 SaveLevel_ELEM(file, level);
8753 for (i = 0; i < NUM_ENVELOPES; i++)
8755 int element = EL_ENVELOPE_1 + i;
8757 chunk_size = SaveLevel_NOTE(NULL, level, element);
8758 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8760 putFileChunkBE(file, "NOTE", chunk_size);
8761 SaveLevel_NOTE(file, level, element);
8765 // if not using template level, check for non-default custom/group elements
8766 if (!level->use_custom_template || save_as_template)
8768 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8770 int element = EL_CUSTOM_START + i;
8772 chunk_size = SaveLevel_CUSX(NULL, level, element);
8773 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8775 putFileChunkBE(file, "CUSX", chunk_size);
8776 SaveLevel_CUSX(file, level, element);
8780 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8782 int element = EL_GROUP_START + i;
8784 chunk_size = SaveLevel_GRPX(NULL, level, element);
8785 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8787 putFileChunkBE(file, "GRPX", chunk_size);
8788 SaveLevel_GRPX(file, level, element);
8792 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8794 int element = GET_EMPTY_ELEMENT(i);
8796 chunk_size = SaveLevel_EMPX(NULL, level, element);
8797 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8799 putFileChunkBE(file, "EMPX", chunk_size);
8800 SaveLevel_EMPX(file, level, element);
8807 SetFilePermissions(filename, PERMS_PRIVATE);
8810 void SaveLevel(int nr)
8812 char *filename = getDefaultLevelFilename(nr);
8814 SaveLevelFromFilename(&level, filename, FALSE);
8817 void SaveLevelTemplate(void)
8819 char *filename = getLocalLevelTemplateFilename();
8821 SaveLevelFromFilename(&level, filename, TRUE);
8824 boolean SaveLevelChecked(int nr)
8826 char *filename = getDefaultLevelFilename(nr);
8827 boolean new_level = !fileExists(filename);
8828 boolean level_saved = FALSE;
8830 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8835 Request("Level saved!", REQ_CONFIRM);
8843 void DumpLevel(struct LevelInfo *level)
8845 if (level->no_level_file || level->no_valid_file)
8847 Warn("cannot dump -- no valid level file found");
8853 Print("Level xxx (file version %08d, game version %08d)\n",
8854 level->file_version, level->game_version);
8857 Print("Level author: '%s'\n", level->author);
8858 Print("Level title: '%s'\n", level->name);
8860 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8862 Print("Level time: %d seconds\n", level->time);
8863 Print("Gems needed: %d\n", level->gems_needed);
8865 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8866 Print("Time for wheel: %d seconds\n", level->time_wheel);
8867 Print("Time for light: %d seconds\n", level->time_light);
8868 Print("Time for timegate: %d seconds\n", level->time_timegate);
8870 Print("Amoeba speed: %d\n", level->amoeba_speed);
8873 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8874 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8875 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8876 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8877 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8878 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8884 for (i = 0; i < NUM_ENVELOPES; i++)
8886 char *text = level->envelope[i].text;
8887 int text_len = strlen(text);
8888 boolean has_text = FALSE;
8890 for (j = 0; j < text_len; j++)
8891 if (text[j] != ' ' && text[j] != '\n')
8897 Print("Envelope %d:\n'%s'\n", i + 1, text);
8905 void DumpLevels(void)
8907 static LevelDirTree *dumplevel_leveldir = NULL;
8909 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8910 global.dumplevel_leveldir);
8912 if (dumplevel_leveldir == NULL)
8913 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8915 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8916 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8917 Fail("no such level number: %d", global.dumplevel_level_nr);
8919 leveldir_current = dumplevel_leveldir;
8921 LoadLevel(global.dumplevel_level_nr);
8928 // ============================================================================
8929 // tape file functions
8930 // ============================================================================
8932 static void setTapeInfoToDefaults(void)
8936 // always start with reliable default values (empty tape)
8939 // default values (also for pre-1.2 tapes) with only the first player
8940 tape.player_participates[0] = TRUE;
8941 for (i = 1; i < MAX_PLAYERS; i++)
8942 tape.player_participates[i] = FALSE;
8944 // at least one (default: the first) player participates in every tape
8945 tape.num_participating_players = 1;
8947 tape.property_bits = TAPE_PROPERTY_NONE;
8949 tape.level_nr = level_nr;
8951 tape.changed = FALSE;
8952 tape.solved = FALSE;
8954 tape.recording = FALSE;
8955 tape.playing = FALSE;
8956 tape.pausing = FALSE;
8958 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8959 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8961 tape.no_info_chunk = TRUE;
8962 tape.no_valid_file = FALSE;
8965 static int getTapePosSize(struct TapeInfo *tape)
8967 int tape_pos_size = 0;
8969 if (tape->use_key_actions)
8970 tape_pos_size += tape->num_participating_players;
8972 if (tape->use_mouse_actions)
8973 tape_pos_size += 3; // x and y position and mouse button mask
8975 tape_pos_size += 1; // tape action delay value
8977 return tape_pos_size;
8980 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8982 tape->use_key_actions = FALSE;
8983 tape->use_mouse_actions = FALSE;
8985 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8986 tape->use_key_actions = TRUE;
8988 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8989 tape->use_mouse_actions = TRUE;
8992 static int getTapeActionValue(struct TapeInfo *tape)
8994 return (tape->use_key_actions &&
8995 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8996 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8997 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8998 TAPE_ACTIONS_DEFAULT);
9001 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9003 tape->file_version = getFileVersion(file);
9004 tape->game_version = getFileVersion(file);
9009 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9013 tape->random_seed = getFile32BitBE(file);
9014 tape->date = getFile32BitBE(file);
9015 tape->length = getFile32BitBE(file);
9017 // read header fields that are new since version 1.2
9018 if (tape->file_version >= FILE_VERSION_1_2)
9020 byte store_participating_players = getFile8Bit(file);
9023 // since version 1.2, tapes store which players participate in the tape
9024 tape->num_participating_players = 0;
9025 for (i = 0; i < MAX_PLAYERS; i++)
9027 tape->player_participates[i] = FALSE;
9029 if (store_participating_players & (1 << i))
9031 tape->player_participates[i] = TRUE;
9032 tape->num_participating_players++;
9036 setTapeActionFlags(tape, getFile8Bit(file));
9038 tape->property_bits = getFile8Bit(file);
9039 tape->solved = getFile8Bit(file);
9041 engine_version = getFileVersion(file);
9042 if (engine_version > 0)
9043 tape->engine_version = engine_version;
9045 tape->engine_version = tape->game_version;
9051 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9053 tape->scr_fieldx = getFile8Bit(file);
9054 tape->scr_fieldy = getFile8Bit(file);
9059 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9061 char *level_identifier = NULL;
9062 int level_identifier_size;
9065 tape->no_info_chunk = FALSE;
9067 level_identifier_size = getFile16BitBE(file);
9069 level_identifier = checked_malloc(level_identifier_size);
9071 for (i = 0; i < level_identifier_size; i++)
9072 level_identifier[i] = getFile8Bit(file);
9074 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9075 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9077 checked_free(level_identifier);
9079 tape->level_nr = getFile16BitBE(file);
9081 chunk_size = 2 + level_identifier_size + 2;
9086 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9089 int tape_pos_size = getTapePosSize(tape);
9090 int chunk_size_expected = tape_pos_size * tape->length;
9092 if (chunk_size_expected != chunk_size)
9094 ReadUnusedBytesFromFile(file, chunk_size);
9095 return chunk_size_expected;
9098 for (i = 0; i < tape->length; i++)
9100 if (i >= MAX_TAPE_LEN)
9102 Warn("tape truncated -- size exceeds maximum tape size %d",
9105 // tape too large; read and ignore remaining tape data from this chunk
9106 for (;i < tape->length; i++)
9107 ReadUnusedBytesFromFile(file, tape_pos_size);
9112 if (tape->use_key_actions)
9114 for (j = 0; j < MAX_PLAYERS; j++)
9116 tape->pos[i].action[j] = MV_NONE;
9118 if (tape->player_participates[j])
9119 tape->pos[i].action[j] = getFile8Bit(file);
9123 if (tape->use_mouse_actions)
9125 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9126 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9127 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9130 tape->pos[i].delay = getFile8Bit(file);
9132 if (tape->file_version == FILE_VERSION_1_0)
9134 // eliminate possible diagonal moves in old tapes
9135 // this is only for backward compatibility
9137 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9138 byte action = tape->pos[i].action[0];
9139 int k, num_moves = 0;
9141 for (k = 0; k < 4; k++)
9143 if (action & joy_dir[k])
9145 tape->pos[i + num_moves].action[0] = joy_dir[k];
9147 tape->pos[i + num_moves].delay = 0;
9156 tape->length += num_moves;
9159 else if (tape->file_version < FILE_VERSION_2_0)
9161 // convert pre-2.0 tapes to new tape format
9163 if (tape->pos[i].delay > 1)
9166 tape->pos[i + 1] = tape->pos[i];
9167 tape->pos[i + 1].delay = 1;
9170 for (j = 0; j < MAX_PLAYERS; j++)
9171 tape->pos[i].action[j] = MV_NONE;
9172 tape->pos[i].delay--;
9179 if (checkEndOfFile(file))
9183 if (i != tape->length)
9184 chunk_size = tape_pos_size * i;
9189 static void LoadTape_SokobanSolution(char *filename)
9192 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9194 if (!(file = openFile(filename, MODE_READ)))
9196 tape.no_valid_file = TRUE;
9201 while (!checkEndOfFile(file))
9203 unsigned char c = getByteFromFile(file);
9205 if (checkEndOfFile(file))
9212 tape.pos[tape.length].action[0] = MV_UP;
9213 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9219 tape.pos[tape.length].action[0] = MV_DOWN;
9220 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9226 tape.pos[tape.length].action[0] = MV_LEFT;
9227 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9233 tape.pos[tape.length].action[0] = MV_RIGHT;
9234 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9242 // ignore white-space characters
9246 tape.no_valid_file = TRUE;
9248 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9256 if (tape.no_valid_file)
9259 tape.length_frames = GetTapeLengthFrames();
9260 tape.length_seconds = GetTapeLengthSeconds();
9263 void LoadTapeFromFilename(char *filename)
9265 char cookie[MAX_LINE_LEN];
9266 char chunk_name[CHUNK_ID_LEN + 1];
9270 // always start with reliable default values
9271 setTapeInfoToDefaults();
9273 if (strSuffix(filename, ".sln"))
9275 LoadTape_SokobanSolution(filename);
9280 if (!(file = openFile(filename, MODE_READ)))
9282 tape.no_valid_file = TRUE;
9287 getFileChunkBE(file, chunk_name, NULL);
9288 if (strEqual(chunk_name, "RND1"))
9290 getFile32BitBE(file); // not used
9292 getFileChunkBE(file, chunk_name, NULL);
9293 if (!strEqual(chunk_name, "TAPE"))
9295 tape.no_valid_file = TRUE;
9297 Warn("unknown format of tape file '%s'", filename);
9304 else // check for pre-2.0 file format with cookie string
9306 strcpy(cookie, chunk_name);
9307 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9309 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9310 cookie[strlen(cookie) - 1] = '\0';
9312 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9314 tape.no_valid_file = TRUE;
9316 Warn("unknown format of tape file '%s'", filename);
9323 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9325 tape.no_valid_file = TRUE;
9327 Warn("unsupported version of tape file '%s'", filename);
9334 // pre-2.0 tape files have no game version, so use file version here
9335 tape.game_version = tape.file_version;
9338 if (tape.file_version < FILE_VERSION_1_2)
9340 // tape files from versions before 1.2.0 without chunk structure
9341 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9342 LoadTape_BODY(file, 2 * tape.length, &tape);
9350 int (*loader)(File *, int, struct TapeInfo *);
9354 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9355 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9356 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9357 { "INFO", -1, LoadTape_INFO },
9358 { "BODY", -1, LoadTape_BODY },
9362 while (getFileChunkBE(file, chunk_name, &chunk_size))
9366 while (chunk_info[i].name != NULL &&
9367 !strEqual(chunk_name, chunk_info[i].name))
9370 if (chunk_info[i].name == NULL)
9372 Warn("unknown chunk '%s' in tape file '%s'",
9373 chunk_name, filename);
9375 ReadUnusedBytesFromFile(file, chunk_size);
9377 else if (chunk_info[i].size != -1 &&
9378 chunk_info[i].size != chunk_size)
9380 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9381 chunk_size, chunk_name, filename);
9383 ReadUnusedBytesFromFile(file, chunk_size);
9387 // call function to load this tape chunk
9388 int chunk_size_expected =
9389 (chunk_info[i].loader)(file, chunk_size, &tape);
9391 // the size of some chunks cannot be checked before reading other
9392 // chunks first (like "HEAD" and "BODY") that contain some header
9393 // information, so check them here
9394 if (chunk_size_expected != chunk_size)
9396 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9397 chunk_size, chunk_name, filename);
9405 tape.length_frames = GetTapeLengthFrames();
9406 tape.length_seconds = GetTapeLengthSeconds();
9409 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9411 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9413 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9414 tape.engine_version);
9418 void LoadTape(int nr)
9420 char *filename = getTapeFilename(nr);
9422 LoadTapeFromFilename(filename);
9425 void LoadSolutionTape(int nr)
9427 char *filename = getSolutionTapeFilename(nr);
9429 LoadTapeFromFilename(filename);
9431 if (TAPE_IS_EMPTY(tape))
9433 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9434 level.native_bd_level->replay != NULL)
9435 CopyNativeTape_BD_to_RND(&level);
9436 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9437 level.native_sp_level->demo.is_available)
9438 CopyNativeTape_SP_to_RND(&level);
9442 void LoadScoreTape(char *score_tape_basename, int nr)
9444 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9446 LoadTapeFromFilename(filename);
9449 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9451 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9453 LoadTapeFromFilename(filename);
9456 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9458 // chunk required for team mode tapes with non-default screen size
9459 return (tape->num_participating_players > 1 &&
9460 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9461 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9464 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9466 putFileVersion(file, tape->file_version);
9467 putFileVersion(file, tape->game_version);
9470 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9473 byte store_participating_players = 0;
9475 // set bits for participating players for compact storage
9476 for (i = 0; i < MAX_PLAYERS; i++)
9477 if (tape->player_participates[i])
9478 store_participating_players |= (1 << i);
9480 putFile32BitBE(file, tape->random_seed);
9481 putFile32BitBE(file, tape->date);
9482 putFile32BitBE(file, tape->length);
9484 putFile8Bit(file, store_participating_players);
9486 putFile8Bit(file, getTapeActionValue(tape));
9488 putFile8Bit(file, tape->property_bits);
9489 putFile8Bit(file, tape->solved);
9491 putFileVersion(file, tape->engine_version);
9494 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9496 putFile8Bit(file, tape->scr_fieldx);
9497 putFile8Bit(file, tape->scr_fieldy);
9500 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9502 int level_identifier_size = strlen(tape->level_identifier) + 1;
9505 putFile16BitBE(file, level_identifier_size);
9507 for (i = 0; i < level_identifier_size; i++)
9508 putFile8Bit(file, tape->level_identifier[i]);
9510 putFile16BitBE(file, tape->level_nr);
9513 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9517 for (i = 0; i < tape->length; i++)
9519 if (tape->use_key_actions)
9521 for (j = 0; j < MAX_PLAYERS; j++)
9522 if (tape->player_participates[j])
9523 putFile8Bit(file, tape->pos[i].action[j]);
9526 if (tape->use_mouse_actions)
9528 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9529 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9530 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9533 putFile8Bit(file, tape->pos[i].delay);
9537 void SaveTapeToFilename(char *filename)
9541 int info_chunk_size;
9542 int body_chunk_size;
9544 if (!(file = fopen(filename, MODE_WRITE)))
9546 Warn("cannot save level recording file '%s'", filename);
9551 tape_pos_size = getTapePosSize(&tape);
9553 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9554 body_chunk_size = tape_pos_size * tape.length;
9556 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9557 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9559 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9560 SaveTape_VERS(file, &tape);
9562 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9563 SaveTape_HEAD(file, &tape);
9565 if (checkSaveTape_SCRN(&tape))
9567 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9568 SaveTape_SCRN(file, &tape);
9571 putFileChunkBE(file, "INFO", info_chunk_size);
9572 SaveTape_INFO(file, &tape);
9574 putFileChunkBE(file, "BODY", body_chunk_size);
9575 SaveTape_BODY(file, &tape);
9579 SetFilePermissions(filename, PERMS_PRIVATE);
9582 static void SaveTapeExt(char *filename)
9586 tape.file_version = FILE_VERSION_ACTUAL;
9587 tape.game_version = GAME_VERSION_ACTUAL;
9589 tape.num_participating_players = 0;
9591 // count number of participating players
9592 for (i = 0; i < MAX_PLAYERS; i++)
9593 if (tape.player_participates[i])
9594 tape.num_participating_players++;
9596 SaveTapeToFilename(filename);
9598 tape.changed = FALSE;
9601 void SaveTape(int nr)
9603 char *filename = getTapeFilename(nr);
9605 InitTapeDirectory(leveldir_current->subdir);
9607 SaveTapeExt(filename);
9610 void SaveScoreTape(int nr)
9612 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9614 // used instead of "leveldir_current->subdir" (for network games)
9615 InitScoreTapeDirectory(levelset.identifier, nr);
9617 SaveTapeExt(filename);
9620 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9621 unsigned int req_state_added)
9623 char *filename = getTapeFilename(nr);
9624 boolean new_tape = !fileExists(filename);
9625 boolean tape_saved = FALSE;
9627 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9632 Request(msg_saved, REQ_CONFIRM | req_state_added);
9640 boolean SaveTapeChecked(int nr)
9642 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9645 boolean SaveTapeChecked_LevelSolved(int nr)
9647 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9648 "Level solved! Tape saved!", REQ_STAY_OPEN);
9651 void DumpTape(struct TapeInfo *tape)
9653 int tape_frame_counter;
9656 if (tape->no_valid_file)
9658 Warn("cannot dump -- no valid tape file found");
9665 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9666 tape->level_nr, tape->file_version, tape->game_version);
9667 Print(" (effective engine version %08d)\n",
9668 tape->engine_version);
9669 Print("Level series identifier: '%s'\n", tape->level_identifier);
9671 Print("Solution tape: %s\n",
9672 tape->solved ? "yes" :
9673 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9675 Print("Special tape properties: ");
9676 if (tape->property_bits == TAPE_PROPERTY_NONE)
9678 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9679 Print("[em_random_bug]");
9680 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9681 Print("[game_speed]");
9682 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9684 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9685 Print("[single_step]");
9686 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9687 Print("[snapshot]");
9688 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9689 Print("[replayed]");
9690 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9691 Print("[tas_keys]");
9692 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9693 Print("[small_graphics]");
9696 int year2 = tape->date / 10000;
9697 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9698 int month_index_raw = (tape->date / 100) % 100;
9699 int month_index = month_index_raw % 12; // prevent invalid index
9700 int month = month_index + 1;
9701 int day = tape->date % 100;
9703 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9707 tape_frame_counter = 0;
9709 for (i = 0; i < tape->length; i++)
9711 if (i >= MAX_TAPE_LEN)
9716 for (j = 0; j < MAX_PLAYERS; j++)
9718 if (tape->player_participates[j])
9720 int action = tape->pos[i].action[j];
9722 Print("%d:%02x ", j, action);
9723 Print("[%c%c%c%c|%c%c] - ",
9724 (action & JOY_LEFT ? '<' : ' '),
9725 (action & JOY_RIGHT ? '>' : ' '),
9726 (action & JOY_UP ? '^' : ' '),
9727 (action & JOY_DOWN ? 'v' : ' '),
9728 (action & JOY_BUTTON_1 ? '1' : ' '),
9729 (action & JOY_BUTTON_2 ? '2' : ' '));
9733 Print("(%03d) ", tape->pos[i].delay);
9734 Print("[%05d]\n", tape_frame_counter);
9736 tape_frame_counter += tape->pos[i].delay;
9742 void DumpTapes(void)
9744 static LevelDirTree *dumptape_leveldir = NULL;
9746 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9747 global.dumptape_leveldir);
9749 if (dumptape_leveldir == NULL)
9750 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9752 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9753 global.dumptape_level_nr > dumptape_leveldir->last_level)
9754 Fail("no such level number: %d", global.dumptape_level_nr);
9756 leveldir_current = dumptape_leveldir;
9758 if (options.mytapes)
9759 LoadTape(global.dumptape_level_nr);
9761 LoadSolutionTape(global.dumptape_level_nr);
9769 // ============================================================================
9770 // score file functions
9771 // ============================================================================
9773 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9777 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9779 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9780 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9781 scores->entry[i].score = 0;
9782 scores->entry[i].time = 0;
9784 scores->entry[i].id = -1;
9785 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9786 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9787 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9788 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9789 strcpy(scores->entry[i].country_code, "??");
9792 scores->num_entries = 0;
9793 scores->last_added = -1;
9794 scores->last_added_local = -1;
9796 scores->updated = FALSE;
9797 scores->uploaded = FALSE;
9798 scores->tape_downloaded = FALSE;
9799 scores->force_last_added = FALSE;
9801 // The following values are intentionally not reset here:
9805 // - continue_playing
9806 // - continue_on_return
9809 static void setScoreInfoToDefaults(void)
9811 setScoreInfoToDefaultsExt(&scores);
9814 static void setServerScoreInfoToDefaults(void)
9816 setScoreInfoToDefaultsExt(&server_scores);
9819 static void LoadScore_OLD(int nr)
9822 char *filename = getScoreFilename(nr);
9823 char cookie[MAX_LINE_LEN];
9824 char line[MAX_LINE_LEN];
9828 if (!(file = fopen(filename, MODE_READ)))
9831 // check file identifier
9832 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9834 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9835 cookie[strlen(cookie) - 1] = '\0';
9837 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9839 Warn("unknown format of score file '%s'", filename);
9846 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9848 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9849 Warn("fscanf() failed; %s", strerror(errno));
9851 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9854 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9855 line[strlen(line) - 1] = '\0';
9857 for (line_ptr = line; *line_ptr; line_ptr++)
9859 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9861 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9862 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9871 static void ConvertScore_OLD(void)
9873 // only convert score to time for levels that rate playing time over score
9874 if (!level.rate_time_over_score)
9877 // convert old score to playing time for score-less levels (like Supaplex)
9878 int time_final_max = 999;
9881 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9883 int score = scores.entry[i].score;
9885 if (score > 0 && score < time_final_max)
9886 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9890 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9892 scores->file_version = getFileVersion(file);
9893 scores->game_version = getFileVersion(file);
9898 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9900 char *level_identifier = NULL;
9901 int level_identifier_size;
9904 level_identifier_size = getFile16BitBE(file);
9906 level_identifier = checked_malloc(level_identifier_size);
9908 for (i = 0; i < level_identifier_size; i++)
9909 level_identifier[i] = getFile8Bit(file);
9911 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9912 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9914 checked_free(level_identifier);
9916 scores->level_nr = getFile16BitBE(file);
9917 scores->num_entries = getFile16BitBE(file);
9919 chunk_size = 2 + level_identifier_size + 2 + 2;
9924 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9928 for (i = 0; i < scores->num_entries; i++)
9930 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9931 scores->entry[i].name[j] = getFile8Bit(file);
9933 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9936 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9941 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9945 for (i = 0; i < scores->num_entries; i++)
9946 scores->entry[i].score = getFile16BitBE(file);
9948 chunk_size = scores->num_entries * 2;
9953 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9957 for (i = 0; i < scores->num_entries; i++)
9958 scores->entry[i].score = getFile32BitBE(file);
9960 chunk_size = scores->num_entries * 4;
9965 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9969 for (i = 0; i < scores->num_entries; i++)
9970 scores->entry[i].time = getFile32BitBE(file);
9972 chunk_size = scores->num_entries * 4;
9977 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9981 for (i = 0; i < scores->num_entries; i++)
9983 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9984 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9986 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9989 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9994 void LoadScore(int nr)
9996 char *filename = getScoreFilename(nr);
9997 char cookie[MAX_LINE_LEN];
9998 char chunk_name[CHUNK_ID_LEN + 1];
10000 boolean old_score_file_format = FALSE;
10003 // always start with reliable default values
10004 setScoreInfoToDefaults();
10006 if (!(file = openFile(filename, MODE_READ)))
10009 getFileChunkBE(file, chunk_name, NULL);
10010 if (strEqual(chunk_name, "RND1"))
10012 getFile32BitBE(file); // not used
10014 getFileChunkBE(file, chunk_name, NULL);
10015 if (!strEqual(chunk_name, "SCOR"))
10017 Warn("unknown format of score file '%s'", filename);
10024 else // check for old file format with cookie string
10026 strcpy(cookie, chunk_name);
10027 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10029 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10030 cookie[strlen(cookie) - 1] = '\0';
10032 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10034 Warn("unknown format of score file '%s'", filename);
10041 old_score_file_format = TRUE;
10044 if (old_score_file_format)
10046 // score files from versions before 4.2.4.0 without chunk structure
10049 // convert score to time, if possible (mainly for Supaplex levels)
10050 ConvertScore_OLD();
10058 int (*loader)(File *, int, struct ScoreInfo *);
10062 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10063 { "INFO", -1, LoadScore_INFO },
10064 { "NAME", -1, LoadScore_NAME },
10065 { "SCOR", -1, LoadScore_SCOR },
10066 { "SC4R", -1, LoadScore_SC4R },
10067 { "TIME", -1, LoadScore_TIME },
10068 { "TAPE", -1, LoadScore_TAPE },
10073 while (getFileChunkBE(file, chunk_name, &chunk_size))
10077 while (chunk_info[i].name != NULL &&
10078 !strEqual(chunk_name, chunk_info[i].name))
10081 if (chunk_info[i].name == NULL)
10083 Warn("unknown chunk '%s' in score file '%s'",
10084 chunk_name, filename);
10086 ReadUnusedBytesFromFile(file, chunk_size);
10088 else if (chunk_info[i].size != -1 &&
10089 chunk_info[i].size != chunk_size)
10091 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10092 chunk_size, chunk_name, filename);
10094 ReadUnusedBytesFromFile(file, chunk_size);
10098 // call function to load this score chunk
10099 int chunk_size_expected =
10100 (chunk_info[i].loader)(file, chunk_size, &scores);
10102 // the size of some chunks cannot be checked before reading other
10103 // chunks first (like "HEAD" and "BODY") that contain some header
10104 // information, so check them here
10105 if (chunk_size_expected != chunk_size)
10107 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10108 chunk_size, chunk_name, filename);
10117 #if ENABLE_HISTORIC_CHUNKS
10118 void SaveScore_OLD(int nr)
10121 char *filename = getScoreFilename(nr);
10124 // used instead of "leveldir_current->subdir" (for network games)
10125 InitScoreDirectory(levelset.identifier);
10127 if (!(file = fopen(filename, MODE_WRITE)))
10129 Warn("cannot save score for level %d", nr);
10134 fprintf(file, "%s\n\n", SCORE_COOKIE);
10136 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10137 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10141 SetFilePermissions(filename, PERMS_PRIVATE);
10145 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10147 putFileVersion(file, scores->file_version);
10148 putFileVersion(file, scores->game_version);
10151 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10153 int level_identifier_size = strlen(scores->level_identifier) + 1;
10156 putFile16BitBE(file, level_identifier_size);
10158 for (i = 0; i < level_identifier_size; i++)
10159 putFile8Bit(file, scores->level_identifier[i]);
10161 putFile16BitBE(file, scores->level_nr);
10162 putFile16BitBE(file, scores->num_entries);
10165 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10169 for (i = 0; i < scores->num_entries; i++)
10171 int name_size = strlen(scores->entry[i].name);
10173 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10174 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10178 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10182 for (i = 0; i < scores->num_entries; i++)
10183 putFile16BitBE(file, scores->entry[i].score);
10186 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10190 for (i = 0; i < scores->num_entries; i++)
10191 putFile32BitBE(file, scores->entry[i].score);
10194 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10198 for (i = 0; i < scores->num_entries; i++)
10199 putFile32BitBE(file, scores->entry[i].time);
10202 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10206 for (i = 0; i < scores->num_entries; i++)
10208 int size = strlen(scores->entry[i].tape_basename);
10210 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10211 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10215 static void SaveScoreToFilename(char *filename)
10218 int info_chunk_size;
10219 int name_chunk_size;
10220 int scor_chunk_size;
10221 int sc4r_chunk_size;
10222 int time_chunk_size;
10223 int tape_chunk_size;
10224 boolean has_large_score_values;
10227 if (!(file = fopen(filename, MODE_WRITE)))
10229 Warn("cannot save score file '%s'", filename);
10234 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10235 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10236 scor_chunk_size = scores.num_entries * 2;
10237 sc4r_chunk_size = scores.num_entries * 4;
10238 time_chunk_size = scores.num_entries * 4;
10239 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10241 has_large_score_values = FALSE;
10242 for (i = 0; i < scores.num_entries; i++)
10243 if (scores.entry[i].score > 0xffff)
10244 has_large_score_values = TRUE;
10246 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10247 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10249 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10250 SaveScore_VERS(file, &scores);
10252 putFileChunkBE(file, "INFO", info_chunk_size);
10253 SaveScore_INFO(file, &scores);
10255 putFileChunkBE(file, "NAME", name_chunk_size);
10256 SaveScore_NAME(file, &scores);
10258 if (has_large_score_values)
10260 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10261 SaveScore_SC4R(file, &scores);
10265 putFileChunkBE(file, "SCOR", scor_chunk_size);
10266 SaveScore_SCOR(file, &scores);
10269 putFileChunkBE(file, "TIME", time_chunk_size);
10270 SaveScore_TIME(file, &scores);
10272 putFileChunkBE(file, "TAPE", tape_chunk_size);
10273 SaveScore_TAPE(file, &scores);
10277 SetFilePermissions(filename, PERMS_PRIVATE);
10280 void SaveScore(int nr)
10282 char *filename = getScoreFilename(nr);
10285 // used instead of "leveldir_current->subdir" (for network games)
10286 InitScoreDirectory(levelset.identifier);
10288 scores.file_version = FILE_VERSION_ACTUAL;
10289 scores.game_version = GAME_VERSION_ACTUAL;
10291 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10292 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10293 scores.level_nr = level_nr;
10295 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10296 if (scores.entry[i].score == 0 &&
10297 scores.entry[i].time == 0 &&
10298 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10301 scores.num_entries = i;
10303 if (scores.num_entries == 0)
10306 SaveScoreToFilename(filename);
10309 static void LoadServerScoreFromCache(int nr)
10311 struct ScoreEntry score_entry;
10320 { &score_entry.score, FALSE, 0 },
10321 { &score_entry.time, FALSE, 0 },
10322 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10323 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10324 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10325 { &score_entry.id, FALSE, 0 },
10326 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10327 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10328 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10329 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10333 char *filename = getScoreCacheFilename(nr);
10334 SetupFileHash *score_hash = loadSetupFileHash(filename);
10337 server_scores.num_entries = 0;
10339 if (score_hash == NULL)
10342 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10344 score_entry = server_scores.entry[i];
10346 for (j = 0; score_mapping[j].value != NULL; j++)
10350 sprintf(token, "%02d.%d", i, j);
10352 char *value = getHashEntry(score_hash, token);
10357 if (score_mapping[j].is_string)
10359 char *score_value = (char *)score_mapping[j].value;
10360 int value_size = score_mapping[j].string_size;
10362 strncpy(score_value, value, value_size);
10363 score_value[value_size] = '\0';
10367 int *score_value = (int *)score_mapping[j].value;
10369 *score_value = atoi(value);
10372 server_scores.num_entries = i + 1;
10375 server_scores.entry[i] = score_entry;
10378 freeSetupFileHash(score_hash);
10381 void LoadServerScore(int nr, boolean download_score)
10383 if (!setup.use_api_server)
10386 // always start with reliable default values
10387 setServerScoreInfoToDefaults();
10389 // 1st step: load server scores from cache file (which may not exist)
10390 // (this should prevent reading it while the thread is writing to it)
10391 LoadServerScoreFromCache(nr);
10393 if (download_score && runtime.use_api_server)
10395 // 2nd step: download server scores from score server to cache file
10396 // (as thread, as it might time out if the server is not reachable)
10397 ApiGetScoreAsThread(nr);
10401 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10403 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10405 // if score tape not uploaded, ask for uploading missing tapes later
10406 if (!setup.has_remaining_tapes)
10407 setup.ask_for_remaining_tapes = TRUE;
10409 setup.provide_uploading_tapes = TRUE;
10410 setup.has_remaining_tapes = TRUE;
10412 SaveSetup_ServerSetup();
10415 void SaveServerScore(int nr, boolean tape_saved)
10417 if (!runtime.use_api_server)
10419 PrepareScoreTapesForUpload(leveldir_current->subdir);
10424 ApiAddScoreAsThread(nr, tape_saved, NULL);
10427 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10428 char *score_tape_filename)
10430 if (!runtime.use_api_server)
10433 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10436 void LoadLocalAndServerScore(int nr, boolean download_score)
10438 int last_added_local = scores.last_added_local;
10439 boolean force_last_added = scores.force_last_added;
10441 // needed if only showing server scores
10442 setScoreInfoToDefaults();
10444 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10447 // restore last added local score entry (before merging server scores)
10448 scores.last_added = scores.last_added_local = last_added_local;
10450 if (setup.use_api_server &&
10451 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10453 // load server scores from cache file and trigger update from server
10454 LoadServerScore(nr, download_score);
10456 // merge local scores with scores from server
10457 MergeServerScore();
10460 if (force_last_added)
10461 scores.force_last_added = force_last_added;
10465 // ============================================================================
10466 // setup file functions
10467 // ============================================================================
10469 #define TOKEN_STR_PLAYER_PREFIX "player_"
10472 static struct TokenInfo global_setup_tokens[] =
10476 &setup.player_name, "player_name"
10480 &setup.multiple_users, "multiple_users"
10484 &setup.sound, "sound"
10488 &setup.sound_loops, "repeating_sound_loops"
10492 &setup.sound_music, "background_music"
10496 &setup.sound_simple, "simple_sound_effects"
10500 &setup.toons, "toons"
10504 &setup.global_animations, "global_animations"
10508 &setup.scroll_delay, "scroll_delay"
10512 &setup.forced_scroll_delay, "forced_scroll_delay"
10516 &setup.scroll_delay_value, "scroll_delay_value"
10520 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10524 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10528 &setup.fade_screens, "fade_screens"
10532 &setup.autorecord, "automatic_tape_recording"
10536 &setup.autorecord_after_replay, "autorecord_after_replay"
10540 &setup.auto_pause_on_start, "auto_pause_on_start"
10544 &setup.show_titlescreen, "show_titlescreen"
10548 &setup.quick_doors, "quick_doors"
10552 &setup.team_mode, "team_mode"
10556 &setup.handicap, "handicap"
10560 &setup.skip_levels, "skip_levels"
10564 &setup.increment_levels, "increment_levels"
10568 &setup.auto_play_next_level, "auto_play_next_level"
10572 &setup.count_score_after_game, "count_score_after_game"
10576 &setup.show_scores_after_game, "show_scores_after_game"
10580 &setup.time_limit, "time_limit"
10584 &setup.fullscreen, "fullscreen"
10588 &setup.window_scaling_percent, "window_scaling_percent"
10592 &setup.window_scaling_quality, "window_scaling_quality"
10596 &setup.screen_rendering_mode, "screen_rendering_mode"
10600 &setup.vsync_mode, "vsync_mode"
10604 &setup.ask_on_escape, "ask_on_escape"
10608 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10612 &setup.ask_on_game_over, "ask_on_game_over"
10616 &setup.ask_on_quit_game, "ask_on_quit_game"
10620 &setup.ask_on_quit_program, "ask_on_quit_program"
10624 &setup.quick_switch, "quick_player_switch"
10628 &setup.input_on_focus, "input_on_focus"
10632 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10636 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10640 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10644 &setup.game_speed_extended, "game_speed_extended"
10648 &setup.game_frame_delay, "game_frame_delay"
10652 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10656 &setup.bd_skip_hatching, "bd_skip_hatching"
10660 &setup.bd_scroll_delay, "bd_scroll_delay"
10664 &setup.bd_smooth_movements, "bd_smooth_movements"
10668 &setup.sp_show_border_elements, "sp_show_border_elements"
10672 &setup.small_game_graphics, "small_game_graphics"
10676 &setup.show_load_save_buttons, "show_load_save_buttons"
10680 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10684 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10688 &setup.graphics_set, "graphics_set"
10692 &setup.sounds_set, "sounds_set"
10696 &setup.music_set, "music_set"
10700 &setup.override_level_graphics, "override_level_graphics"
10704 &setup.override_level_sounds, "override_level_sounds"
10708 &setup.override_level_music, "override_level_music"
10712 &setup.volume_simple, "volume_simple"
10716 &setup.volume_loops, "volume_loops"
10720 &setup.volume_music, "volume_music"
10724 &setup.network_mode, "network_mode"
10728 &setup.network_player_nr, "network_player"
10732 &setup.network_server_hostname, "network_server_hostname"
10736 &setup.touch.control_type, "touch.control_type"
10740 &setup.touch.move_distance, "touch.move_distance"
10744 &setup.touch.drop_distance, "touch.drop_distance"
10748 &setup.touch.transparency, "touch.transparency"
10752 &setup.touch.draw_outlined, "touch.draw_outlined"
10756 &setup.touch.draw_pressed, "touch.draw_pressed"
10760 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10764 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10768 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10772 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10776 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10780 static struct TokenInfo auto_setup_tokens[] =
10784 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10788 static struct TokenInfo server_setup_tokens[] =
10792 &setup.player_uuid, "player_uuid"
10796 &setup.player_version, "player_version"
10800 &setup.use_api_server, TEST_PREFIX "use_api_server"
10804 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10808 &setup.api_server_password, TEST_PREFIX "api_server_password"
10812 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10816 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10820 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10824 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10828 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10832 static struct TokenInfo editor_setup_tokens[] =
10836 &setup.editor.el_classic, "editor.el_classic"
10840 &setup.editor.el_custom, "editor.el_custom"
10844 &setup.editor.el_user_defined, "editor.el_user_defined"
10848 &setup.editor.el_dynamic, "editor.el_dynamic"
10852 &setup.editor.el_headlines, "editor.el_headlines"
10856 &setup.editor.show_element_token, "editor.show_element_token"
10860 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10864 static struct TokenInfo editor_cascade_setup_tokens[] =
10868 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10872 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10876 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10880 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10884 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10888 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10892 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10896 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10900 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10904 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10908 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10912 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10916 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10920 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10924 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10928 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10932 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10936 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10940 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10944 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10948 static struct TokenInfo shortcut_setup_tokens[] =
10952 &setup.shortcut.save_game, "shortcut.save_game"
10956 &setup.shortcut.load_game, "shortcut.load_game"
10960 &setup.shortcut.restart_game, "shortcut.restart_game"
10964 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10968 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10972 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10976 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10980 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10984 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10988 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10992 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10996 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11000 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11004 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11008 &setup.shortcut.tape_record, "shortcut.tape_record"
11012 &setup.shortcut.tape_play, "shortcut.tape_play"
11016 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11020 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11024 &setup.shortcut.sound_music, "shortcut.sound_music"
11028 &setup.shortcut.snap_left, "shortcut.snap_left"
11032 &setup.shortcut.snap_right, "shortcut.snap_right"
11036 &setup.shortcut.snap_up, "shortcut.snap_up"
11040 &setup.shortcut.snap_down, "shortcut.snap_down"
11044 static struct SetupInputInfo setup_input;
11045 static struct TokenInfo player_setup_tokens[] =
11049 &setup_input.use_joystick, ".use_joystick"
11053 &setup_input.joy.device_name, ".joy.device_name"
11057 &setup_input.joy.xleft, ".joy.xleft"
11061 &setup_input.joy.xmiddle, ".joy.xmiddle"
11065 &setup_input.joy.xright, ".joy.xright"
11069 &setup_input.joy.yupper, ".joy.yupper"
11073 &setup_input.joy.ymiddle, ".joy.ymiddle"
11077 &setup_input.joy.ylower, ".joy.ylower"
11081 &setup_input.joy.snap, ".joy.snap_field"
11085 &setup_input.joy.drop, ".joy.place_bomb"
11089 &setup_input.key.left, ".key.move_left"
11093 &setup_input.key.right, ".key.move_right"
11097 &setup_input.key.up, ".key.move_up"
11101 &setup_input.key.down, ".key.move_down"
11105 &setup_input.key.snap, ".key.snap_field"
11109 &setup_input.key.drop, ".key.place_bomb"
11113 static struct TokenInfo system_setup_tokens[] =
11117 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11121 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11125 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11129 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11133 static struct TokenInfo internal_setup_tokens[] =
11137 &setup.internal.program_title, "program_title"
11141 &setup.internal.program_version, "program_version"
11145 &setup.internal.program_author, "program_author"
11149 &setup.internal.program_email, "program_email"
11153 &setup.internal.program_website, "program_website"
11157 &setup.internal.program_copyright, "program_copyright"
11161 &setup.internal.program_company, "program_company"
11165 &setup.internal.program_icon_file, "program_icon_file"
11169 &setup.internal.default_graphics_set, "default_graphics_set"
11173 &setup.internal.default_sounds_set, "default_sounds_set"
11177 &setup.internal.default_music_set, "default_music_set"
11181 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11185 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11189 &setup.internal.fallback_music_file, "fallback_music_file"
11193 &setup.internal.default_level_series, "default_level_series"
11197 &setup.internal.default_window_width, "default_window_width"
11201 &setup.internal.default_window_height, "default_window_height"
11205 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11209 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11213 &setup.internal.create_user_levelset, "create_user_levelset"
11217 &setup.internal.info_screens_from_main, "info_screens_from_main"
11221 &setup.internal.menu_game, "menu_game"
11225 &setup.internal.menu_engines, "menu_engines"
11229 &setup.internal.menu_editor, "menu_editor"
11233 &setup.internal.menu_graphics, "menu_graphics"
11237 &setup.internal.menu_sound, "menu_sound"
11241 &setup.internal.menu_artwork, "menu_artwork"
11245 &setup.internal.menu_input, "menu_input"
11249 &setup.internal.menu_touch, "menu_touch"
11253 &setup.internal.menu_shortcuts, "menu_shortcuts"
11257 &setup.internal.menu_exit, "menu_exit"
11261 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11265 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11269 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11273 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11277 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11281 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11285 &setup.internal.info_title, "info_title"
11289 &setup.internal.info_elements, "info_elements"
11293 &setup.internal.info_music, "info_music"
11297 &setup.internal.info_credits, "info_credits"
11301 &setup.internal.info_program, "info_program"
11305 &setup.internal.info_version, "info_version"
11309 &setup.internal.info_levelset, "info_levelset"
11313 &setup.internal.info_exit, "info_exit"
11317 static struct TokenInfo debug_setup_tokens[] =
11321 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11325 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11329 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11333 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11337 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11341 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11345 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11349 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11353 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11357 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11361 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11365 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11369 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11373 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11377 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11381 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11385 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11389 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11393 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11397 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11401 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11404 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11408 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11412 &setup.debug.xsn_mode, "debug.xsn_mode"
11416 &setup.debug.xsn_percent, "debug.xsn_percent"
11420 static struct TokenInfo options_setup_tokens[] =
11424 &setup.options.verbose, "options.verbose"
11428 &setup.options.debug, "options.debug"
11432 &setup.options.debug_mode, "options.debug_mode"
11436 static void setSetupInfoToDefaults(struct SetupInfo *si)
11440 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11442 si->multiple_users = TRUE;
11445 si->sound_loops = TRUE;
11446 si->sound_music = TRUE;
11447 si->sound_simple = TRUE;
11449 si->global_animations = TRUE;
11450 si->scroll_delay = TRUE;
11451 si->forced_scroll_delay = FALSE;
11452 si->scroll_delay_value = STD_SCROLL_DELAY;
11453 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11454 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11455 si->fade_screens = TRUE;
11456 si->autorecord = TRUE;
11457 si->autorecord_after_replay = TRUE;
11458 si->auto_pause_on_start = FALSE;
11459 si->show_titlescreen = TRUE;
11460 si->quick_doors = FALSE;
11461 si->team_mode = FALSE;
11462 si->handicap = TRUE;
11463 si->skip_levels = TRUE;
11464 si->increment_levels = TRUE;
11465 si->auto_play_next_level = TRUE;
11466 si->count_score_after_game = TRUE;
11467 si->show_scores_after_game = TRUE;
11468 si->time_limit = TRUE;
11469 si->fullscreen = FALSE;
11470 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11471 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11472 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11473 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11474 si->ask_on_escape = TRUE;
11475 si->ask_on_escape_editor = TRUE;
11476 si->ask_on_game_over = TRUE;
11477 si->ask_on_quit_game = TRUE;
11478 si->ask_on_quit_program = TRUE;
11479 si->quick_switch = FALSE;
11480 si->input_on_focus = FALSE;
11481 si->prefer_aga_graphics = TRUE;
11482 si->prefer_lowpass_sounds = FALSE;
11483 si->prefer_extra_panel_items = TRUE;
11484 si->game_speed_extended = FALSE;
11485 si->game_frame_delay = GAME_FRAME_DELAY;
11486 si->bd_skip_uncovering = FALSE;
11487 si->bd_skip_hatching = FALSE;
11488 si->bd_scroll_delay = TRUE;
11489 si->bd_smooth_movements = AUTO;
11490 si->sp_show_border_elements = FALSE;
11491 si->small_game_graphics = FALSE;
11492 si->show_load_save_buttons = FALSE;
11493 si->show_undo_redo_buttons = FALSE;
11494 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11496 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11497 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11498 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11500 si->override_level_graphics = FALSE;
11501 si->override_level_sounds = FALSE;
11502 si->override_level_music = FALSE;
11504 si->volume_simple = 100; // percent
11505 si->volume_loops = 100; // percent
11506 si->volume_music = 100; // percent
11508 si->network_mode = FALSE;
11509 si->network_player_nr = 0; // first player
11510 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11512 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11513 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11514 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11515 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11516 si->touch.draw_outlined = TRUE;
11517 si->touch.draw_pressed = TRUE;
11519 for (i = 0; i < 2; i++)
11521 char *default_grid_button[6][2] =
11527 { "111222", " vv " },
11528 { "111222", " vv " }
11530 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11531 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11532 int min_xsize = MIN(6, grid_xsize);
11533 int min_ysize = MIN(6, grid_ysize);
11534 int startx = grid_xsize - min_xsize;
11535 int starty = grid_ysize - min_ysize;
11538 // virtual buttons grid can only be set to defaults if video is initialized
11539 // (this will be repeated if virtual buttons are not loaded from setup file)
11540 if (video.initialized)
11542 si->touch.grid_xsize[i] = grid_xsize;
11543 si->touch.grid_ysize[i] = grid_ysize;
11547 si->touch.grid_xsize[i] = -1;
11548 si->touch.grid_ysize[i] = -1;
11551 for (x = 0; x < MAX_GRID_XSIZE; x++)
11552 for (y = 0; y < MAX_GRID_YSIZE; y++)
11553 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11555 for (x = 0; x < min_xsize; x++)
11556 for (y = 0; y < min_ysize; y++)
11557 si->touch.grid_button[i][x][starty + y] =
11558 default_grid_button[y][0][x];
11560 for (x = 0; x < min_xsize; x++)
11561 for (y = 0; y < min_ysize; y++)
11562 si->touch.grid_button[i][startx + x][starty + y] =
11563 default_grid_button[y][1][x];
11566 si->touch.grid_initialized = video.initialized;
11568 si->touch.overlay_buttons = FALSE;
11570 si->editor.el_boulderdash = TRUE;
11571 si->editor.el_boulderdash_native = TRUE;
11572 si->editor.el_boulderdash_effects = TRUE;
11573 si->editor.el_emerald_mine = TRUE;
11574 si->editor.el_emerald_mine_club = TRUE;
11575 si->editor.el_more = TRUE;
11576 si->editor.el_sokoban = TRUE;
11577 si->editor.el_supaplex = TRUE;
11578 si->editor.el_diamond_caves = TRUE;
11579 si->editor.el_dx_boulderdash = TRUE;
11581 si->editor.el_mirror_magic = TRUE;
11582 si->editor.el_deflektor = TRUE;
11584 si->editor.el_chars = TRUE;
11585 si->editor.el_steel_chars = TRUE;
11587 si->editor.el_classic = TRUE;
11588 si->editor.el_custom = TRUE;
11590 si->editor.el_user_defined = FALSE;
11591 si->editor.el_dynamic = TRUE;
11593 si->editor.el_headlines = TRUE;
11595 si->editor.show_element_token = FALSE;
11597 si->editor.show_read_only_warning = TRUE;
11599 si->editor.use_template_for_new_levels = TRUE;
11601 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11602 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11603 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11604 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11605 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11607 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11608 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11609 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11610 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11611 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11613 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11614 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11615 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11616 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11617 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11618 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11620 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11621 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11622 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11624 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11625 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11626 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11627 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11629 for (i = 0; i < MAX_PLAYERS; i++)
11631 si->input[i].use_joystick = FALSE;
11632 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11633 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11634 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11635 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11636 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11637 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11638 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11639 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11640 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11641 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11642 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11643 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11644 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11645 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11646 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11649 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11650 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11651 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11652 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11654 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11655 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11656 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11657 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11658 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11659 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11660 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11662 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11664 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11665 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11666 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11668 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11669 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11670 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11672 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11673 si->internal.choose_from_top_leveldir = FALSE;
11674 si->internal.show_scaling_in_title = TRUE;
11675 si->internal.create_user_levelset = TRUE;
11676 si->internal.info_screens_from_main = FALSE;
11678 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11679 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11681 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11682 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11683 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11684 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11685 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11686 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11687 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11688 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11689 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11690 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11692 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11693 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11694 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11695 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11696 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11697 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11698 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11699 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11700 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11701 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11703 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11704 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11706 si->debug.show_frames_per_second = FALSE;
11708 si->debug.xsn_mode = AUTO;
11709 si->debug.xsn_percent = 0;
11711 si->options.verbose = FALSE;
11712 si->options.debug = FALSE;
11713 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11715 #if defined(PLATFORM_ANDROID)
11716 si->fullscreen = TRUE;
11717 si->touch.overlay_buttons = TRUE;
11720 setHideSetupEntry(&setup.debug.xsn_mode);
11723 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11725 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11728 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11730 si->player_uuid = NULL; // (will be set later)
11731 si->player_version = 1; // (will be set later)
11733 si->use_api_server = TRUE;
11734 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11735 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11736 si->ask_for_uploading_tapes = TRUE;
11737 si->ask_for_remaining_tapes = FALSE;
11738 si->provide_uploading_tapes = TRUE;
11739 si->ask_for_using_api_server = TRUE;
11740 si->has_remaining_tapes = FALSE;
11743 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11745 si->editor_cascade.el_bd = TRUE;
11746 si->editor_cascade.el_bd_native = TRUE;
11747 si->editor_cascade.el_bd_effects = FALSE;
11748 si->editor_cascade.el_em = TRUE;
11749 si->editor_cascade.el_emc = TRUE;
11750 si->editor_cascade.el_rnd = TRUE;
11751 si->editor_cascade.el_sb = TRUE;
11752 si->editor_cascade.el_sp = TRUE;
11753 si->editor_cascade.el_dc = TRUE;
11754 si->editor_cascade.el_dx = TRUE;
11756 si->editor_cascade.el_mm = TRUE;
11757 si->editor_cascade.el_df = TRUE;
11759 si->editor_cascade.el_chars = FALSE;
11760 si->editor_cascade.el_steel_chars = FALSE;
11761 si->editor_cascade.el_ce = FALSE;
11762 si->editor_cascade.el_ge = FALSE;
11763 si->editor_cascade.el_es = FALSE;
11764 si->editor_cascade.el_ref = FALSE;
11765 si->editor_cascade.el_user = FALSE;
11766 si->editor_cascade.el_dynamic = FALSE;
11769 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11771 static char *getHideSetupToken(void *setup_value)
11773 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11775 if (setup_value != NULL)
11776 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11778 return hide_setup_token;
11781 void setHideSetupEntry(void *setup_value)
11783 char *hide_setup_token = getHideSetupToken(setup_value);
11785 if (hide_setup_hash == NULL)
11786 hide_setup_hash = newSetupFileHash();
11788 if (setup_value != NULL)
11789 setHashEntry(hide_setup_hash, hide_setup_token, "");
11792 void removeHideSetupEntry(void *setup_value)
11794 char *hide_setup_token = getHideSetupToken(setup_value);
11796 if (setup_value != NULL)
11797 removeHashEntry(hide_setup_hash, hide_setup_token);
11800 boolean hideSetupEntry(void *setup_value)
11802 char *hide_setup_token = getHideSetupToken(setup_value);
11804 return (setup_value != NULL &&
11805 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11808 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11809 struct TokenInfo *token_info,
11810 int token_nr, char *token_text)
11812 char *token_hide_text = getStringCat2(token_text, ".hide");
11813 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11815 // set the value of this setup option in the setup option structure
11816 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11818 // check if this setup option should be hidden in the setup menu
11819 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11820 setHideSetupEntry(token_info[token_nr].value);
11822 free(token_hide_text);
11825 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11826 struct TokenInfo *token_info,
11829 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11830 token_info[token_nr].text);
11833 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11837 if (!setup_file_hash)
11840 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11841 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11843 setup.touch.grid_initialized = TRUE;
11844 for (i = 0; i < 2; i++)
11846 int grid_xsize = setup.touch.grid_xsize[i];
11847 int grid_ysize = setup.touch.grid_ysize[i];
11850 // if virtual buttons are not loaded from setup file, repeat initializing
11851 // virtual buttons grid with default values later when video is initialized
11852 if (grid_xsize == -1 ||
11855 setup.touch.grid_initialized = FALSE;
11860 for (y = 0; y < grid_ysize; y++)
11862 char token_string[MAX_LINE_LEN];
11864 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11866 char *value_string = getHashEntry(setup_file_hash, token_string);
11868 if (value_string == NULL)
11871 for (x = 0; x < grid_xsize; x++)
11873 char c = value_string[x];
11875 setup.touch.grid_button[i][x][y] =
11876 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11881 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11882 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11884 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11885 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11887 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11891 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11893 setup_input = setup.input[pnr];
11894 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11896 char full_token[100];
11898 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11899 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11902 setup.input[pnr] = setup_input;
11905 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11906 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11908 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11909 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11911 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11912 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11914 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11915 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11917 setHideRelatedSetupEntries();
11920 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11924 if (!setup_file_hash)
11927 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11928 setSetupInfo(auto_setup_tokens, i,
11929 getHashEntry(setup_file_hash,
11930 auto_setup_tokens[i].text));
11933 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11937 if (!setup_file_hash)
11940 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11941 setSetupInfo(server_setup_tokens, i,
11942 getHashEntry(setup_file_hash,
11943 server_setup_tokens[i].text));
11946 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11950 if (!setup_file_hash)
11953 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11954 setSetupInfo(editor_cascade_setup_tokens, i,
11955 getHashEntry(setup_file_hash,
11956 editor_cascade_setup_tokens[i].text));
11959 void LoadUserNames(void)
11961 int last_user_nr = user.nr;
11964 if (global.user_names != NULL)
11966 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11967 checked_free(global.user_names[i]);
11969 checked_free(global.user_names);
11972 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11974 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11978 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11980 if (setup_file_hash)
11982 char *player_name = getHashEntry(setup_file_hash, "player_name");
11984 global.user_names[i] = getFixedUserName(player_name);
11986 freeSetupFileHash(setup_file_hash);
11989 if (global.user_names[i] == NULL)
11990 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11993 user.nr = last_user_nr;
11996 void LoadSetupFromFilename(char *filename)
11998 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12000 if (setup_file_hash)
12002 decodeSetupFileHash_Default(setup_file_hash);
12004 freeSetupFileHash(setup_file_hash);
12008 Debug("setup", "using default setup values");
12012 static void LoadSetup_SpecialPostProcessing(void)
12014 char *player_name_new;
12016 // needed to work around problems with fixed length strings
12017 player_name_new = getFixedUserName(setup.player_name);
12018 free(setup.player_name);
12019 setup.player_name = player_name_new;
12021 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12022 if (setup.scroll_delay == FALSE)
12024 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12025 setup.scroll_delay = TRUE; // now always "on"
12028 // make sure that scroll delay value stays inside valid range
12029 setup.scroll_delay_value =
12030 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12033 void LoadSetup_Default(void)
12037 // always start with reliable default values
12038 setSetupInfoToDefaults(&setup);
12040 // try to load setup values from default setup file
12041 filename = getDefaultSetupFilename();
12043 if (fileExists(filename))
12044 LoadSetupFromFilename(filename);
12046 // try to load setup values from platform setup file
12047 filename = getPlatformSetupFilename();
12049 if (fileExists(filename))
12050 LoadSetupFromFilename(filename);
12052 // try to load setup values from user setup file
12053 filename = getSetupFilename();
12055 LoadSetupFromFilename(filename);
12057 LoadSetup_SpecialPostProcessing();
12060 void LoadSetup_AutoSetup(void)
12062 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12063 SetupFileHash *setup_file_hash = NULL;
12065 // always start with reliable default values
12066 setSetupInfoToDefaults_AutoSetup(&setup);
12068 setup_file_hash = loadSetupFileHash(filename);
12070 if (setup_file_hash)
12072 decodeSetupFileHash_AutoSetup(setup_file_hash);
12074 freeSetupFileHash(setup_file_hash);
12080 void LoadSetup_ServerSetup(void)
12082 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12083 SetupFileHash *setup_file_hash = NULL;
12085 // always start with reliable default values
12086 setSetupInfoToDefaults_ServerSetup(&setup);
12088 setup_file_hash = loadSetupFileHash(filename);
12090 if (setup_file_hash)
12092 decodeSetupFileHash_ServerSetup(setup_file_hash);
12094 freeSetupFileHash(setup_file_hash);
12099 if (setup.player_uuid == NULL)
12101 // player UUID does not yet exist in setup file
12102 setup.player_uuid = getStringCopy(getUUID());
12103 setup.player_version = 2;
12105 SaveSetup_ServerSetup();
12109 void LoadSetup_EditorCascade(void)
12111 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12112 SetupFileHash *setup_file_hash = NULL;
12114 // always start with reliable default values
12115 setSetupInfoToDefaults_EditorCascade(&setup);
12117 setup_file_hash = loadSetupFileHash(filename);
12119 if (setup_file_hash)
12121 decodeSetupFileHash_EditorCascade(setup_file_hash);
12123 freeSetupFileHash(setup_file_hash);
12129 void LoadSetup(void)
12131 LoadSetup_Default();
12132 LoadSetup_AutoSetup();
12133 LoadSetup_ServerSetup();
12134 LoadSetup_EditorCascade();
12137 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12138 char *mapping_line)
12140 char mapping_guid[MAX_LINE_LEN];
12141 char *mapping_start, *mapping_end;
12143 // get GUID from game controller mapping line: copy complete line
12144 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12145 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12147 // get GUID from game controller mapping line: cut after GUID part
12148 mapping_start = strchr(mapping_guid, ',');
12149 if (mapping_start != NULL)
12150 *mapping_start = '\0';
12152 // cut newline from game controller mapping line
12153 mapping_end = strchr(mapping_line, '\n');
12154 if (mapping_end != NULL)
12155 *mapping_end = '\0';
12157 // add mapping entry to game controller mappings hash
12158 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12161 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12166 if (!(file = fopen(filename, MODE_READ)))
12168 Warn("cannot read game controller mappings file '%s'", filename);
12173 while (!feof(file))
12175 char line[MAX_LINE_LEN];
12177 if (!fgets(line, MAX_LINE_LEN, file))
12180 addGameControllerMappingToHash(mappings_hash, line);
12186 void SaveSetup_Default(void)
12188 char *filename = getSetupFilename();
12192 InitUserDataDirectory();
12194 if (!(file = fopen(filename, MODE_WRITE)))
12196 Warn("cannot write setup file '%s'", filename);
12201 fprintFileHeader(file, SETUP_FILENAME);
12203 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12205 // just to make things nicer :)
12206 if (global_setup_tokens[i].value == &setup.multiple_users ||
12207 global_setup_tokens[i].value == &setup.sound ||
12208 global_setup_tokens[i].value == &setup.graphics_set ||
12209 global_setup_tokens[i].value == &setup.volume_simple ||
12210 global_setup_tokens[i].value == &setup.network_mode ||
12211 global_setup_tokens[i].value == &setup.touch.control_type ||
12212 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12213 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12214 fprintf(file, "\n");
12216 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12219 for (i = 0; i < 2; i++)
12221 int grid_xsize = setup.touch.grid_xsize[i];
12222 int grid_ysize = setup.touch.grid_ysize[i];
12225 fprintf(file, "\n");
12227 for (y = 0; y < grid_ysize; y++)
12229 char token_string[MAX_LINE_LEN];
12230 char value_string[MAX_LINE_LEN];
12232 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12234 for (x = 0; x < grid_xsize; x++)
12236 char c = setup.touch.grid_button[i][x][y];
12238 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12241 value_string[grid_xsize] = '\0';
12243 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12247 fprintf(file, "\n");
12248 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12249 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12251 fprintf(file, "\n");
12252 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12253 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12255 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12259 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12260 fprintf(file, "\n");
12262 setup_input = setup.input[pnr];
12263 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12264 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12267 fprintf(file, "\n");
12268 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12269 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12271 // (internal setup values not saved to user setup file)
12273 fprintf(file, "\n");
12274 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12275 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12276 setup.debug.xsn_mode != AUTO)
12277 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12279 fprintf(file, "\n");
12280 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12281 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12285 SetFilePermissions(filename, PERMS_PRIVATE);
12288 void SaveSetup_AutoSetup(void)
12290 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12294 InitUserDataDirectory();
12296 if (!(file = fopen(filename, MODE_WRITE)))
12298 Warn("cannot write auto setup file '%s'", filename);
12305 fprintFileHeader(file, AUTOSETUP_FILENAME);
12307 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12308 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12312 SetFilePermissions(filename, PERMS_PRIVATE);
12317 void SaveSetup_ServerSetup(void)
12319 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12323 InitUserDataDirectory();
12325 if (!(file = fopen(filename, MODE_WRITE)))
12327 Warn("cannot write server setup file '%s'", filename);
12334 fprintFileHeader(file, SERVERSETUP_FILENAME);
12336 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12338 // just to make things nicer :)
12339 if (server_setup_tokens[i].value == &setup.use_api_server)
12340 fprintf(file, "\n");
12342 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12347 SetFilePermissions(filename, PERMS_PRIVATE);
12352 void SaveSetup_EditorCascade(void)
12354 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12358 InitUserDataDirectory();
12360 if (!(file = fopen(filename, MODE_WRITE)))
12362 Warn("cannot write editor cascade state file '%s'", filename);
12369 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12371 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12372 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12376 SetFilePermissions(filename, PERMS_PRIVATE);
12381 void SaveSetup(void)
12383 SaveSetup_Default();
12384 SaveSetup_AutoSetup();
12385 SaveSetup_ServerSetup();
12386 SaveSetup_EditorCascade();
12389 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12394 if (!(file = fopen(filename, MODE_WRITE)))
12396 Warn("cannot write game controller mappings file '%s'", filename);
12401 BEGIN_HASH_ITERATION(mappings_hash, itr)
12403 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12405 END_HASH_ITERATION(mappings_hash, itr)
12410 void SaveSetup_AddGameControllerMapping(char *mapping)
12412 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12413 SetupFileHash *mappings_hash = newSetupFileHash();
12415 InitUserDataDirectory();
12417 // load existing personal game controller mappings
12418 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12420 // add new mapping to personal game controller mappings
12421 addGameControllerMappingToHash(mappings_hash, mapping);
12423 // save updated personal game controller mappings
12424 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12426 freeSetupFileHash(mappings_hash);
12430 void LoadCustomElementDescriptions(void)
12432 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12433 SetupFileHash *setup_file_hash;
12436 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12438 if (element_info[i].custom_description != NULL)
12440 free(element_info[i].custom_description);
12441 element_info[i].custom_description = NULL;
12445 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12448 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12450 char *token = getStringCat2(element_info[i].token_name, ".name");
12451 char *value = getHashEntry(setup_file_hash, token);
12454 element_info[i].custom_description = getStringCopy(value);
12459 freeSetupFileHash(setup_file_hash);
12462 static int getElementFromToken(char *token)
12464 char *value = getHashEntry(element_token_hash, token);
12467 return atoi(value);
12469 Warn("unknown element token '%s'", token);
12471 return EL_UNDEFINED;
12474 void FreeGlobalAnimEventInfo(void)
12476 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12478 if (gaei->event_list == NULL)
12483 for (i = 0; i < gaei->num_event_lists; i++)
12485 checked_free(gaei->event_list[i]->event_value);
12486 checked_free(gaei->event_list[i]);
12489 checked_free(gaei->event_list);
12491 gaei->event_list = NULL;
12492 gaei->num_event_lists = 0;
12495 static int AddGlobalAnimEventList(void)
12497 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12498 int list_pos = gaei->num_event_lists++;
12500 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12501 sizeof(struct GlobalAnimEventListInfo *));
12503 gaei->event_list[list_pos] =
12504 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12506 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12508 gaeli->event_value = NULL;
12509 gaeli->num_event_values = 0;
12514 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12516 // do not add empty global animation events
12517 if (event_value == ANIM_EVENT_NONE)
12520 // if list position is undefined, create new list
12521 if (list_pos == ANIM_EVENT_UNDEFINED)
12522 list_pos = AddGlobalAnimEventList();
12524 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12525 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12526 int value_pos = gaeli->num_event_values++;
12528 gaeli->event_value = checked_realloc(gaeli->event_value,
12529 gaeli->num_event_values * sizeof(int *));
12531 gaeli->event_value[value_pos] = event_value;
12536 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12538 if (list_pos == ANIM_EVENT_UNDEFINED)
12539 return ANIM_EVENT_NONE;
12541 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12542 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12544 return gaeli->event_value[value_pos];
12547 int GetGlobalAnimEventValueCount(int list_pos)
12549 if (list_pos == ANIM_EVENT_UNDEFINED)
12552 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12553 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12555 return gaeli->num_event_values;
12558 // This function checks if a string <s> of the format "string1, string2, ..."
12559 // exactly contains a string <s_contained>.
12561 static boolean string_has_parameter(char *s, char *s_contained)
12565 if (s == NULL || s_contained == NULL)
12568 if (strlen(s_contained) > strlen(s))
12571 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12573 char next_char = s[strlen(s_contained)];
12575 // check if next character is delimiter or whitespace
12576 if (next_char == ',' || next_char == '\0' ||
12577 next_char == ' ' || next_char == '\t')
12581 // check if string contains another parameter string after a comma
12582 substring = strchr(s, ',');
12583 if (substring == NULL) // string does not contain a comma
12586 // advance string pointer to next character after the comma
12589 // skip potential whitespaces after the comma
12590 while (*substring == ' ' || *substring == '\t')
12593 return string_has_parameter(substring, s_contained);
12596 static int get_anim_parameter_value_ce(char *s)
12599 char *pattern_1 = "ce_change:custom_";
12600 char *pattern_2 = ".page_";
12601 int pattern_1_len = strlen(pattern_1);
12602 char *matching_char = strstr(s_ptr, pattern_1);
12603 int result = ANIM_EVENT_NONE;
12605 if (matching_char == NULL)
12606 return ANIM_EVENT_NONE;
12608 result = ANIM_EVENT_CE_CHANGE;
12610 s_ptr = matching_char + pattern_1_len;
12612 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12613 if (*s_ptr >= '0' && *s_ptr <= '9')
12615 int gic_ce_nr = (*s_ptr++ - '0');
12617 if (*s_ptr >= '0' && *s_ptr <= '9')
12619 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12621 if (*s_ptr >= '0' && *s_ptr <= '9')
12622 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12625 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12626 return ANIM_EVENT_NONE;
12628 // custom element stored as 0 to 255
12631 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12635 // invalid custom element number specified
12637 return ANIM_EVENT_NONE;
12640 // check for change page number ("page_X" or "page_XX") (optional)
12641 if (strPrefix(s_ptr, pattern_2))
12643 s_ptr += strlen(pattern_2);
12645 if (*s_ptr >= '0' && *s_ptr <= '9')
12647 int gic_page_nr = (*s_ptr++ - '0');
12649 if (*s_ptr >= '0' && *s_ptr <= '9')
12650 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12652 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12653 return ANIM_EVENT_NONE;
12655 // change page stored as 1 to 32 (0 means "all change pages")
12657 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12661 // invalid animation part number specified
12663 return ANIM_EVENT_NONE;
12667 // discard result if next character is neither delimiter nor whitespace
12668 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12669 *s_ptr == ' ' || *s_ptr == '\t'))
12670 return ANIM_EVENT_NONE;
12675 static int get_anim_parameter_value(char *s)
12677 int event_value[] =
12685 char *pattern_1[] =
12693 char *pattern_2 = ".part_";
12694 char *matching_char = NULL;
12696 int pattern_1_len = 0;
12697 int result = ANIM_EVENT_NONE;
12700 result = get_anim_parameter_value_ce(s);
12702 if (result != ANIM_EVENT_NONE)
12705 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12707 matching_char = strstr(s_ptr, pattern_1[i]);
12708 pattern_1_len = strlen(pattern_1[i]);
12709 result = event_value[i];
12711 if (matching_char != NULL)
12715 if (matching_char == NULL)
12716 return ANIM_EVENT_NONE;
12718 s_ptr = matching_char + pattern_1_len;
12720 // check for main animation number ("anim_X" or "anim_XX")
12721 if (*s_ptr >= '0' && *s_ptr <= '9')
12723 int gic_anim_nr = (*s_ptr++ - '0');
12725 if (*s_ptr >= '0' && *s_ptr <= '9')
12726 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12728 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12729 return ANIM_EVENT_NONE;
12731 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12735 // invalid main animation number specified
12737 return ANIM_EVENT_NONE;
12740 // check for animation part number ("part_X" or "part_XX") (optional)
12741 if (strPrefix(s_ptr, pattern_2))
12743 s_ptr += strlen(pattern_2);
12745 if (*s_ptr >= '0' && *s_ptr <= '9')
12747 int gic_part_nr = (*s_ptr++ - '0');
12749 if (*s_ptr >= '0' && *s_ptr <= '9')
12750 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12752 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12753 return ANIM_EVENT_NONE;
12755 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12759 // invalid animation part number specified
12761 return ANIM_EVENT_NONE;
12765 // discard result if next character is neither delimiter nor whitespace
12766 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12767 *s_ptr == ' ' || *s_ptr == '\t'))
12768 return ANIM_EVENT_NONE;
12773 static int get_anim_parameter_values(char *s)
12775 int list_pos = ANIM_EVENT_UNDEFINED;
12776 int event_value = ANIM_EVENT_DEFAULT;
12778 if (string_has_parameter(s, "any"))
12779 event_value |= ANIM_EVENT_ANY;
12781 if (string_has_parameter(s, "click:self") ||
12782 string_has_parameter(s, "click") ||
12783 string_has_parameter(s, "self"))
12784 event_value |= ANIM_EVENT_SELF;
12786 if (string_has_parameter(s, "unclick:any"))
12787 event_value |= ANIM_EVENT_UNCLICK_ANY;
12789 // if animation event found, add it to global animation event list
12790 if (event_value != ANIM_EVENT_NONE)
12791 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12795 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12796 event_value = get_anim_parameter_value(s);
12798 // if animation event found, add it to global animation event list
12799 if (event_value != ANIM_EVENT_NONE)
12800 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12802 // continue with next part of the string, starting with next comma
12803 s = strchr(s + 1, ',');
12809 static int get_anim_action_parameter_value(char *token)
12811 // check most common default case first to massively speed things up
12812 if (strEqual(token, ARG_UNDEFINED))
12813 return ANIM_EVENT_ACTION_NONE;
12815 int result = getImageIDFromToken(token);
12819 char *gfx_token = getStringCat2("gfx.", token);
12821 result = getImageIDFromToken(gfx_token);
12823 checked_free(gfx_token);
12828 Key key = getKeyFromX11KeyName(token);
12830 if (key != KSYM_UNDEFINED)
12831 result = -(int)key;
12838 result = get_hash_from_string(token); // unsigned int => int
12839 result = ABS(result); // may be negative now
12840 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12842 setHashEntry(anim_url_hash, int2str(result, 0), token);
12847 result = ANIM_EVENT_ACTION_NONE;
12852 int get_parameter_value(char *value_raw, char *suffix, int type)
12854 char *value = getStringToLower(value_raw);
12855 int result = 0; // probably a save default value
12857 if (strEqual(suffix, ".direction"))
12859 result = (strEqual(value, "left") ? MV_LEFT :
12860 strEqual(value, "right") ? MV_RIGHT :
12861 strEqual(value, "up") ? MV_UP :
12862 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12864 else if (strEqual(suffix, ".position"))
12866 result = (strEqual(value, "left") ? POS_LEFT :
12867 strEqual(value, "right") ? POS_RIGHT :
12868 strEqual(value, "top") ? POS_TOP :
12869 strEqual(value, "upper") ? POS_UPPER :
12870 strEqual(value, "middle") ? POS_MIDDLE :
12871 strEqual(value, "lower") ? POS_LOWER :
12872 strEqual(value, "bottom") ? POS_BOTTOM :
12873 strEqual(value, "any") ? POS_ANY :
12874 strEqual(value, "ce") ? POS_CE :
12875 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12876 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12878 else if (strEqual(suffix, ".align"))
12880 result = (strEqual(value, "left") ? ALIGN_LEFT :
12881 strEqual(value, "right") ? ALIGN_RIGHT :
12882 strEqual(value, "center") ? ALIGN_CENTER :
12883 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12885 else if (strEqual(suffix, ".valign"))
12887 result = (strEqual(value, "top") ? VALIGN_TOP :
12888 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12889 strEqual(value, "middle") ? VALIGN_MIDDLE :
12890 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12892 else if (strEqual(suffix, ".anim_mode"))
12894 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12895 string_has_parameter(value, "loop") ? ANIM_LOOP :
12896 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12897 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12898 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12899 string_has_parameter(value, "random") ? ANIM_RANDOM :
12900 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12901 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12902 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12903 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12904 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12905 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12906 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12907 string_has_parameter(value, "all") ? ANIM_ALL :
12908 string_has_parameter(value, "tiled") ? ANIM_TILED :
12909 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12912 if (string_has_parameter(value, "once"))
12913 result |= ANIM_ONCE;
12915 if (string_has_parameter(value, "reverse"))
12916 result |= ANIM_REVERSE;
12918 if (string_has_parameter(value, "opaque_player"))
12919 result |= ANIM_OPAQUE_PLAYER;
12921 if (string_has_parameter(value, "static_panel"))
12922 result |= ANIM_STATIC_PANEL;
12924 else if (strEqual(suffix, ".init_event") ||
12925 strEqual(suffix, ".anim_event"))
12927 result = get_anim_parameter_values(value);
12929 else if (strEqual(suffix, ".init_delay_action") ||
12930 strEqual(suffix, ".anim_delay_action") ||
12931 strEqual(suffix, ".post_delay_action") ||
12932 strEqual(suffix, ".init_event_action") ||
12933 strEqual(suffix, ".anim_event_action"))
12935 result = get_anim_action_parameter_value(value_raw);
12937 else if (strEqual(suffix, ".class"))
12939 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12940 get_hash_from_string(value));
12942 else if (strEqual(suffix, ".style"))
12944 result = STYLE_DEFAULT;
12946 if (string_has_parameter(value, "accurate_borders"))
12947 result |= STYLE_ACCURATE_BORDERS;
12949 if (string_has_parameter(value, "inner_corners"))
12950 result |= STYLE_INNER_CORNERS;
12952 if (string_has_parameter(value, "reverse"))
12953 result |= STYLE_REVERSE;
12955 if (string_has_parameter(value, "leftmost_position"))
12956 result |= STYLE_LEFTMOST_POSITION;
12958 if (string_has_parameter(value, "block_clicks"))
12959 result |= STYLE_BLOCK;
12961 if (string_has_parameter(value, "passthrough_clicks"))
12962 result |= STYLE_PASSTHROUGH;
12964 if (string_has_parameter(value, "multiple_actions"))
12965 result |= STYLE_MULTIPLE_ACTIONS;
12967 if (string_has_parameter(value, "consume_ce_event"))
12968 result |= STYLE_CONSUME_CE_EVENT;
12970 else if (strEqual(suffix, ".fade_mode"))
12972 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12973 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12974 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12975 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12976 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12977 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12978 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12979 FADE_MODE_DEFAULT);
12981 else if (strEqual(suffix, ".auto_delay_unit"))
12983 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12984 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12985 AUTO_DELAY_UNIT_DEFAULT);
12987 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12989 result = gfx.get_font_from_token_function(value);
12991 else // generic parameter of type integer or boolean
12993 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12994 type == TYPE_INTEGER ? get_integer_from_string(value) :
12995 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12996 ARG_UNDEFINED_VALUE);
13004 static int get_token_parameter_value(char *token, char *value_raw)
13008 if (token == NULL || value_raw == NULL)
13009 return ARG_UNDEFINED_VALUE;
13011 suffix = strrchr(token, '.');
13012 if (suffix == NULL)
13015 if (strEqual(suffix, ".element"))
13016 return getElementFromToken(value_raw);
13018 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13019 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13022 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13023 boolean ignore_defaults)
13027 for (i = 0; image_config_vars[i].token != NULL; i++)
13029 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13031 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13032 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13036 *image_config_vars[i].value =
13037 get_token_parameter_value(image_config_vars[i].token, value);
13041 void InitMenuDesignSettings_Static(void)
13043 // always start with reliable default values from static default config
13044 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13047 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13051 // the following initializes hierarchical values from static configuration
13053 // special case: initialize "ARG_DEFAULT" values in static default config
13054 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13055 titlescreen_initial_first_default.fade_mode =
13056 title_initial_first_default.fade_mode;
13057 titlescreen_initial_first_default.fade_delay =
13058 title_initial_first_default.fade_delay;
13059 titlescreen_initial_first_default.post_delay =
13060 title_initial_first_default.post_delay;
13061 titlescreen_initial_first_default.auto_delay =
13062 title_initial_first_default.auto_delay;
13063 titlescreen_initial_first_default.auto_delay_unit =
13064 title_initial_first_default.auto_delay_unit;
13065 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13066 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13067 titlescreen_first_default.post_delay = title_first_default.post_delay;
13068 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13069 titlescreen_first_default.auto_delay_unit =
13070 title_first_default.auto_delay_unit;
13071 titlemessage_initial_first_default.fade_mode =
13072 title_initial_first_default.fade_mode;
13073 titlemessage_initial_first_default.fade_delay =
13074 title_initial_first_default.fade_delay;
13075 titlemessage_initial_first_default.post_delay =
13076 title_initial_first_default.post_delay;
13077 titlemessage_initial_first_default.auto_delay =
13078 title_initial_first_default.auto_delay;
13079 titlemessage_initial_first_default.auto_delay_unit =
13080 title_initial_first_default.auto_delay_unit;
13081 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13082 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13083 titlemessage_first_default.post_delay = title_first_default.post_delay;
13084 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13085 titlemessage_first_default.auto_delay_unit =
13086 title_first_default.auto_delay_unit;
13088 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13089 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13090 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13091 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13092 titlescreen_initial_default.auto_delay_unit =
13093 title_initial_default.auto_delay_unit;
13094 titlescreen_default.fade_mode = title_default.fade_mode;
13095 titlescreen_default.fade_delay = title_default.fade_delay;
13096 titlescreen_default.post_delay = title_default.post_delay;
13097 titlescreen_default.auto_delay = title_default.auto_delay;
13098 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13099 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13100 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13101 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13102 titlemessage_initial_default.auto_delay_unit =
13103 title_initial_default.auto_delay_unit;
13104 titlemessage_default.fade_mode = title_default.fade_mode;
13105 titlemessage_default.fade_delay = title_default.fade_delay;
13106 titlemessage_default.post_delay = title_default.post_delay;
13107 titlemessage_default.auto_delay = title_default.auto_delay;
13108 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13110 // special case: initialize "ARG_DEFAULT" values in static default config
13111 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13112 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13114 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13115 titlescreen_first[i] = titlescreen_first_default;
13116 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13117 titlemessage_first[i] = titlemessage_first_default;
13119 titlescreen_initial[i] = titlescreen_initial_default;
13120 titlescreen[i] = titlescreen_default;
13121 titlemessage_initial[i] = titlemessage_initial_default;
13122 titlemessage[i] = titlemessage_default;
13125 // special case: initialize "ARG_DEFAULT" values in static default config
13126 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13127 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13129 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13132 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13133 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13134 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13137 // special case: initialize "ARG_DEFAULT" values in static default config
13138 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13139 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13141 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13142 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13143 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13145 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13148 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13152 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13156 struct XY *dst, *src;
13158 game_buttons_xy[] =
13160 { &game.button.save, &game.button.stop },
13161 { &game.button.pause2, &game.button.pause },
13162 { &game.button.load, &game.button.play },
13163 { &game.button.undo, &game.button.stop },
13164 { &game.button.redo, &game.button.play },
13170 // special case: initialize later added SETUP list size from LEVELS value
13171 if (menu.list_size[GAME_MODE_SETUP] == -1)
13172 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13174 // set default position for snapshot buttons to stop/pause/play buttons
13175 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13176 if ((*game_buttons_xy[i].dst).x == -1 &&
13177 (*game_buttons_xy[i].dst).y == -1)
13178 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13180 // --------------------------------------------------------------------------
13181 // dynamic viewports (including playfield margins, borders and alignments)
13182 // --------------------------------------------------------------------------
13184 // dynamic viewports currently only supported for landscape mode
13185 int display_width = MAX(video.display_width, video.display_height);
13186 int display_height = MIN(video.display_width, video.display_height);
13188 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13190 struct RectWithBorder *vp_window = &viewport.window[i];
13191 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13192 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13193 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13194 boolean dynamic_window_width = (vp_window->min_width != -1);
13195 boolean dynamic_window_height = (vp_window->min_height != -1);
13196 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13197 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13199 // adjust window size if min/max width/height is specified
13201 if (vp_window->min_width != -1)
13203 int window_width = display_width;
13205 // when using static window height, use aspect ratio of display
13206 if (vp_window->min_height == -1)
13207 window_width = vp_window->height * display_width / display_height;
13209 vp_window->width = MAX(vp_window->min_width, window_width);
13212 if (vp_window->min_height != -1)
13214 int window_height = display_height;
13216 // when using static window width, use aspect ratio of display
13217 if (vp_window->min_width == -1)
13218 window_height = vp_window->width * display_height / display_width;
13220 vp_window->height = MAX(vp_window->min_height, window_height);
13223 if (vp_window->max_width != -1)
13224 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13226 if (vp_window->max_height != -1)
13227 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13229 int playfield_width = vp_window->width;
13230 int playfield_height = vp_window->height;
13232 // adjust playfield size and position according to specified margins
13234 playfield_width -= vp_playfield->margin_left;
13235 playfield_width -= vp_playfield->margin_right;
13237 playfield_height -= vp_playfield->margin_top;
13238 playfield_height -= vp_playfield->margin_bottom;
13240 // adjust playfield size if min/max width/height is specified
13242 if (vp_playfield->min_width != -1)
13243 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13245 if (vp_playfield->min_height != -1)
13246 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13248 if (vp_playfield->max_width != -1)
13249 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13251 if (vp_playfield->max_height != -1)
13252 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13254 // adjust playfield position according to specified alignment
13256 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13257 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13258 else if (vp_playfield->align == ALIGN_CENTER)
13259 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13260 else if (vp_playfield->align == ALIGN_RIGHT)
13261 vp_playfield->x += playfield_width - vp_playfield->width;
13263 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13264 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13265 else if (vp_playfield->valign == VALIGN_MIDDLE)
13266 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13267 else if (vp_playfield->valign == VALIGN_BOTTOM)
13268 vp_playfield->y += playfield_height - vp_playfield->height;
13270 vp_playfield->x += vp_playfield->margin_left;
13271 vp_playfield->y += vp_playfield->margin_top;
13273 // adjust individual playfield borders if only default border is specified
13275 if (vp_playfield->border_left == -1)
13276 vp_playfield->border_left = vp_playfield->border_size;
13277 if (vp_playfield->border_right == -1)
13278 vp_playfield->border_right = vp_playfield->border_size;
13279 if (vp_playfield->border_top == -1)
13280 vp_playfield->border_top = vp_playfield->border_size;
13281 if (vp_playfield->border_bottom == -1)
13282 vp_playfield->border_bottom = vp_playfield->border_size;
13284 // set dynamic playfield borders if borders are specified as undefined
13285 // (but only if window size was dynamic and playfield size was static)
13287 if (dynamic_window_width && !dynamic_playfield_width)
13289 if (vp_playfield->border_left == -1)
13291 vp_playfield->border_left = (vp_playfield->x -
13292 vp_playfield->margin_left);
13293 vp_playfield->x -= vp_playfield->border_left;
13294 vp_playfield->width += vp_playfield->border_left;
13297 if (vp_playfield->border_right == -1)
13299 vp_playfield->border_right = (vp_window->width -
13301 vp_playfield->width -
13302 vp_playfield->margin_right);
13303 vp_playfield->width += vp_playfield->border_right;
13307 if (dynamic_window_height && !dynamic_playfield_height)
13309 if (vp_playfield->border_top == -1)
13311 vp_playfield->border_top = (vp_playfield->y -
13312 vp_playfield->margin_top);
13313 vp_playfield->y -= vp_playfield->border_top;
13314 vp_playfield->height += vp_playfield->border_top;
13317 if (vp_playfield->border_bottom == -1)
13319 vp_playfield->border_bottom = (vp_window->height -
13321 vp_playfield->height -
13322 vp_playfield->margin_bottom);
13323 vp_playfield->height += vp_playfield->border_bottom;
13327 // adjust playfield size to be a multiple of a defined alignment tile size
13329 int align_size = vp_playfield->align_size;
13330 int playfield_xtiles = vp_playfield->width / align_size;
13331 int playfield_ytiles = vp_playfield->height / align_size;
13332 int playfield_width_corrected = playfield_xtiles * align_size;
13333 int playfield_height_corrected = playfield_ytiles * align_size;
13334 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13335 i == GFX_SPECIAL_ARG_EDITOR);
13337 if (is_playfield_mode &&
13338 dynamic_playfield_width &&
13339 vp_playfield->width != playfield_width_corrected)
13341 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13343 vp_playfield->width = playfield_width_corrected;
13345 if (vp_playfield->align == ALIGN_LEFT)
13347 vp_playfield->border_left += playfield_xdiff;
13349 else if (vp_playfield->align == ALIGN_RIGHT)
13351 vp_playfield->border_right += playfield_xdiff;
13353 else if (vp_playfield->align == ALIGN_CENTER)
13355 int border_left_diff = playfield_xdiff / 2;
13356 int border_right_diff = playfield_xdiff - border_left_diff;
13358 vp_playfield->border_left += border_left_diff;
13359 vp_playfield->border_right += border_right_diff;
13363 if (is_playfield_mode &&
13364 dynamic_playfield_height &&
13365 vp_playfield->height != playfield_height_corrected)
13367 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13369 vp_playfield->height = playfield_height_corrected;
13371 if (vp_playfield->valign == VALIGN_TOP)
13373 vp_playfield->border_top += playfield_ydiff;
13375 else if (vp_playfield->align == VALIGN_BOTTOM)
13377 vp_playfield->border_right += playfield_ydiff;
13379 else if (vp_playfield->align == VALIGN_MIDDLE)
13381 int border_top_diff = playfield_ydiff / 2;
13382 int border_bottom_diff = playfield_ydiff - border_top_diff;
13384 vp_playfield->border_top += border_top_diff;
13385 vp_playfield->border_bottom += border_bottom_diff;
13389 // adjust door positions according to specified alignment
13391 for (j = 0; j < 2; j++)
13393 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13395 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13396 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13397 else if (vp_door->align == ALIGN_CENTER)
13398 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13399 else if (vp_door->align == ALIGN_RIGHT)
13400 vp_door->x += vp_window->width - vp_door->width;
13402 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13403 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13404 else if (vp_door->valign == VALIGN_MIDDLE)
13405 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13406 else if (vp_door->valign == VALIGN_BOTTOM)
13407 vp_door->y += vp_window->height - vp_door->height;
13412 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13416 struct XYTileSize *dst, *src;
13419 editor_buttons_xy[] =
13422 &editor.button.element_left, &editor.palette.element_left,
13423 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13426 &editor.button.element_middle, &editor.palette.element_middle,
13427 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13430 &editor.button.element_right, &editor.palette.element_right,
13431 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13438 // set default position for element buttons to element graphics
13439 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13441 if ((*editor_buttons_xy[i].dst).x == -1 &&
13442 (*editor_buttons_xy[i].dst).y == -1)
13444 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13446 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13448 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13452 // adjust editor palette rows and columns if specified to be dynamic
13454 if (editor.palette.cols == -1)
13456 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13457 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13458 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13460 editor.palette.cols = (vp_width - sc_width) / bt_width;
13462 if (editor.palette.x == -1)
13464 int palette_width = editor.palette.cols * bt_width + sc_width;
13466 editor.palette.x = (vp_width - palette_width) / 2;
13470 if (editor.palette.rows == -1)
13472 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13473 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13474 int tx_height = getFontHeight(FONT_TEXT_2);
13476 editor.palette.rows = (vp_height - tx_height) / bt_height;
13478 if (editor.palette.y == -1)
13480 int palette_height = editor.palette.rows * bt_height + tx_height;
13482 editor.palette.y = (vp_height - palette_height) / 2;
13487 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13488 boolean initialize)
13490 // special case: check if network and preview player positions are redefined,
13491 // to compare this later against the main menu level preview being redefined
13492 struct TokenIntPtrInfo menu_config_players[] =
13494 { "main.network_players.x", &menu.main.network_players.redefined },
13495 { "main.network_players.y", &menu.main.network_players.redefined },
13496 { "main.preview_players.x", &menu.main.preview_players.redefined },
13497 { "main.preview_players.y", &menu.main.preview_players.redefined },
13498 { "preview.x", &preview.redefined },
13499 { "preview.y", &preview.redefined }
13505 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13506 *menu_config_players[i].value = FALSE;
13510 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13511 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13512 *menu_config_players[i].value = TRUE;
13516 static void InitMenuDesignSettings_PreviewPlayers(void)
13518 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13521 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13523 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13526 static void LoadMenuDesignSettingsFromFilename(char *filename)
13528 static struct TitleFadingInfo tfi;
13529 static struct TitleMessageInfo tmi;
13530 static struct TokenInfo title_tokens[] =
13532 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13533 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13534 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13535 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13536 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13540 static struct TokenInfo titlemessage_tokens[] =
13542 { TYPE_INTEGER, &tmi.x, ".x" },
13543 { TYPE_INTEGER, &tmi.y, ".y" },
13544 { TYPE_INTEGER, &tmi.width, ".width" },
13545 { TYPE_INTEGER, &tmi.height, ".height" },
13546 { TYPE_INTEGER, &tmi.chars, ".chars" },
13547 { TYPE_INTEGER, &tmi.lines, ".lines" },
13548 { TYPE_INTEGER, &tmi.align, ".align" },
13549 { TYPE_INTEGER, &tmi.valign, ".valign" },
13550 { TYPE_INTEGER, &tmi.font, ".font" },
13551 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13552 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13553 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13554 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13555 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13556 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13557 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13558 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13559 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13565 struct TitleFadingInfo *info;
13570 // initialize first titles from "enter screen" definitions, if defined
13571 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13572 { &title_first_default, "menu.enter_screen.TITLE" },
13574 // initialize title screens from "next screen" definitions, if defined
13575 { &title_initial_default, "menu.next_screen.TITLE" },
13576 { &title_default, "menu.next_screen.TITLE" },
13582 struct TitleMessageInfo *array;
13585 titlemessage_arrays[] =
13587 // initialize first titles from "enter screen" definitions, if defined
13588 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13589 { titlescreen_first, "menu.enter_screen.TITLE" },
13590 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13591 { titlemessage_first, "menu.enter_screen.TITLE" },
13593 // initialize titles from "next screen" definitions, if defined
13594 { titlescreen_initial, "menu.next_screen.TITLE" },
13595 { titlescreen, "menu.next_screen.TITLE" },
13596 { titlemessage_initial, "menu.next_screen.TITLE" },
13597 { titlemessage, "menu.next_screen.TITLE" },
13599 // overwrite titles with title definitions, if defined
13600 { titlescreen_initial_first, "[title_initial]" },
13601 { titlescreen_first, "[title]" },
13602 { titlemessage_initial_first, "[title_initial]" },
13603 { titlemessage_first, "[title]" },
13605 { titlescreen_initial, "[title_initial]" },
13606 { titlescreen, "[title]" },
13607 { titlemessage_initial, "[title_initial]" },
13608 { titlemessage, "[title]" },
13610 // overwrite titles with title screen/message definitions, if defined
13611 { titlescreen_initial_first, "[titlescreen_initial]" },
13612 { titlescreen_first, "[titlescreen]" },
13613 { titlemessage_initial_first, "[titlemessage_initial]" },
13614 { titlemessage_first, "[titlemessage]" },
13616 { titlescreen_initial, "[titlescreen_initial]" },
13617 { titlescreen, "[titlescreen]" },
13618 { titlemessage_initial, "[titlemessage_initial]" },
13619 { titlemessage, "[titlemessage]" },
13623 SetupFileHash *setup_file_hash;
13626 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13629 // the following initializes hierarchical values from dynamic configuration
13631 // special case: initialize with default values that may be overwritten
13632 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13633 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13635 struct TokenIntPtrInfo menu_config[] =
13637 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13638 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13639 { "menu.list_size", &menu.list_size[i] }
13642 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13644 char *token = menu_config[j].token;
13645 char *value = getHashEntry(setup_file_hash, token);
13648 *menu_config[j].value = get_integer_from_string(value);
13652 // special case: initialize with default values that may be overwritten
13653 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13654 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13656 struct TokenIntPtrInfo menu_config[] =
13658 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13659 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13660 { "menu.list_size.INFO", &menu.list_size_info[i] },
13661 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13662 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13665 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13667 char *token = menu_config[j].token;
13668 char *value = getHashEntry(setup_file_hash, token);
13671 *menu_config[j].value = get_integer_from_string(value);
13675 // special case: initialize with default values that may be overwritten
13676 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13677 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13679 struct TokenIntPtrInfo menu_config[] =
13681 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13682 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13685 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13687 char *token = menu_config[j].token;
13688 char *value = getHashEntry(setup_file_hash, token);
13691 *menu_config[j].value = get_integer_from_string(value);
13695 // special case: initialize with default values that may be overwritten
13696 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13697 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13699 struct TokenIntPtrInfo menu_config[] =
13701 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13702 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13703 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13704 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13705 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13706 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13707 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13708 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13709 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13710 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13713 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13715 char *token = menu_config[j].token;
13716 char *value = getHashEntry(setup_file_hash, token);
13719 *menu_config[j].value = get_integer_from_string(value);
13723 // special case: initialize with default values that may be overwritten
13724 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13725 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13727 struct TokenIntPtrInfo menu_config[] =
13729 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13730 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13731 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13732 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13733 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13734 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13735 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13736 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13737 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13740 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13742 char *token = menu_config[j].token;
13743 char *value = getHashEntry(setup_file_hash, token);
13746 *menu_config[j].value = get_token_parameter_value(token, value);
13750 // special case: initialize with default values that may be overwritten
13751 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13752 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13756 char *token_prefix;
13757 struct RectWithBorder *struct_ptr;
13761 { "viewport.window", &viewport.window[i] },
13762 { "viewport.playfield", &viewport.playfield[i] },
13763 { "viewport.door_1", &viewport.door_1[i] },
13764 { "viewport.door_2", &viewport.door_2[i] }
13767 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13769 struct TokenIntPtrInfo vp_config[] =
13771 { ".x", &vp_struct[j].struct_ptr->x },
13772 { ".y", &vp_struct[j].struct_ptr->y },
13773 { ".width", &vp_struct[j].struct_ptr->width },
13774 { ".height", &vp_struct[j].struct_ptr->height },
13775 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13776 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13777 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13778 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13779 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13780 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13781 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13782 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13783 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13784 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13785 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13786 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13787 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13788 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13789 { ".align", &vp_struct[j].struct_ptr->align },
13790 { ".valign", &vp_struct[j].struct_ptr->valign }
13793 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13795 char *token = getStringCat2(vp_struct[j].token_prefix,
13796 vp_config[k].token);
13797 char *value = getHashEntry(setup_file_hash, token);
13800 *vp_config[k].value = get_token_parameter_value(token, value);
13807 // special case: initialize with default values that may be overwritten
13808 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13809 for (i = 0; title_info[i].info != NULL; i++)
13811 struct TitleFadingInfo *info = title_info[i].info;
13812 char *base_token = title_info[i].text;
13814 for (j = 0; title_tokens[j].type != -1; j++)
13816 char *token = getStringCat2(base_token, title_tokens[j].text);
13817 char *value = getHashEntry(setup_file_hash, token);
13821 int parameter_value = get_token_parameter_value(token, value);
13825 *(int *)title_tokens[j].value = (int)parameter_value;
13834 // special case: initialize with default values that may be overwritten
13835 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13836 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13838 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13839 char *base_token = titlemessage_arrays[i].text;
13841 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13843 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13844 char *value = getHashEntry(setup_file_hash, token);
13848 int parameter_value = get_token_parameter_value(token, value);
13850 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13854 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13855 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13857 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13867 // read (and overwrite with) values that may be specified in config file
13868 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13870 // special case: check if network and preview player positions are redefined
13871 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13873 freeSetupFileHash(setup_file_hash);
13876 void LoadMenuDesignSettings(void)
13878 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13880 InitMenuDesignSettings_Static();
13881 InitMenuDesignSettings_SpecialPreProcessing();
13882 InitMenuDesignSettings_PreviewPlayers();
13884 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13886 // first look for special settings configured in level series config
13887 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13889 if (fileExists(filename_base))
13890 LoadMenuDesignSettingsFromFilename(filename_base);
13893 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13895 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13896 LoadMenuDesignSettingsFromFilename(filename_local);
13898 InitMenuDesignSettings_SpecialPostProcessing();
13901 void LoadMenuDesignSettings_AfterGraphics(void)
13903 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13906 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13907 boolean ignore_defaults)
13911 for (i = 0; sound_config_vars[i].token != NULL; i++)
13913 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13915 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13916 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13920 *sound_config_vars[i].value =
13921 get_token_parameter_value(sound_config_vars[i].token, value);
13925 void InitSoundSettings_Static(void)
13927 // always start with reliable default values from static default config
13928 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13931 static void LoadSoundSettingsFromFilename(char *filename)
13933 SetupFileHash *setup_file_hash;
13935 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13938 // read (and overwrite with) values that may be specified in config file
13939 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13941 freeSetupFileHash(setup_file_hash);
13944 void LoadSoundSettings(void)
13946 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13948 InitSoundSettings_Static();
13950 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13952 // first look for special settings configured in level series config
13953 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13955 if (fileExists(filename_base))
13956 LoadSoundSettingsFromFilename(filename_base);
13959 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13961 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13962 LoadSoundSettingsFromFilename(filename_local);
13965 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13967 char *filename = getEditorSetupFilename();
13968 SetupFileList *setup_file_list, *list;
13969 SetupFileHash *element_hash;
13970 int num_unknown_tokens = 0;
13973 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13976 element_hash = newSetupFileHash();
13978 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13979 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13981 // determined size may be larger than needed (due to unknown elements)
13983 for (list = setup_file_list; list != NULL; list = list->next)
13986 // add space for up to 3 more elements for padding that may be needed
13987 *num_elements += 3;
13989 // free memory for old list of elements, if needed
13990 checked_free(*elements);
13992 // allocate memory for new list of elements
13993 *elements = checked_malloc(*num_elements * sizeof(int));
13996 for (list = setup_file_list; list != NULL; list = list->next)
13998 char *value = getHashEntry(element_hash, list->token);
14000 if (value == NULL) // try to find obsolete token mapping
14002 char *mapped_token = get_mapped_token(list->token);
14004 if (mapped_token != NULL)
14006 value = getHashEntry(element_hash, mapped_token);
14008 free(mapped_token);
14014 (*elements)[(*num_elements)++] = atoi(value);
14018 if (num_unknown_tokens == 0)
14021 Warn("unknown token(s) found in config file:");
14022 Warn("- config file: '%s'", filename);
14024 num_unknown_tokens++;
14027 Warn("- token: '%s'", list->token);
14031 if (num_unknown_tokens > 0)
14034 while (*num_elements % 4) // pad with empty elements, if needed
14035 (*elements)[(*num_elements)++] = EL_EMPTY;
14037 freeSetupFileList(setup_file_list);
14038 freeSetupFileHash(element_hash);
14041 for (i = 0; i < *num_elements; i++)
14042 Debug("editor", "element '%s' [%d]\n",
14043 element_info[(*elements)[i]].token_name, (*elements)[i]);
14047 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14050 SetupFileHash *setup_file_hash = NULL;
14051 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14052 char *filename_music, *filename_prefix, *filename_info;
14058 token_to_value_ptr[] =
14060 { "title_header", &tmp_music_file_info.title_header },
14061 { "artist_header", &tmp_music_file_info.artist_header },
14062 { "album_header", &tmp_music_file_info.album_header },
14063 { "year_header", &tmp_music_file_info.year_header },
14064 { "played_header", &tmp_music_file_info.played_header },
14066 { "title", &tmp_music_file_info.title },
14067 { "artist", &tmp_music_file_info.artist },
14068 { "album", &tmp_music_file_info.album },
14069 { "year", &tmp_music_file_info.year },
14070 { "played", &tmp_music_file_info.played },
14076 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14077 getCustomMusicFilename(basename));
14079 if (filename_music == NULL)
14082 // ---------- try to replace file extension ----------
14084 filename_prefix = getStringCopy(filename_music);
14085 if (strrchr(filename_prefix, '.') != NULL)
14086 *strrchr(filename_prefix, '.') = '\0';
14087 filename_info = getStringCat2(filename_prefix, ".txt");
14089 if (fileExists(filename_info))
14090 setup_file_hash = loadSetupFileHash(filename_info);
14092 free(filename_prefix);
14093 free(filename_info);
14095 if (setup_file_hash == NULL)
14097 // ---------- try to add file extension ----------
14099 filename_prefix = getStringCopy(filename_music);
14100 filename_info = getStringCat2(filename_prefix, ".txt");
14102 if (fileExists(filename_info))
14103 setup_file_hash = loadSetupFileHash(filename_info);
14105 free(filename_prefix);
14106 free(filename_info);
14109 if (setup_file_hash == NULL)
14112 // ---------- music file info found ----------
14114 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14116 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14118 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14120 *token_to_value_ptr[i].value_ptr =
14121 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14124 tmp_music_file_info.basename = getStringCopy(basename);
14125 tmp_music_file_info.music = music;
14126 tmp_music_file_info.is_sound = is_sound;
14128 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14129 *new_music_file_info = tmp_music_file_info;
14131 return new_music_file_info;
14134 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14136 return get_music_file_info_ext(basename, music, FALSE);
14139 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14141 return get_music_file_info_ext(basename, sound, TRUE);
14144 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14145 char *basename, boolean is_sound)
14147 for (; list != NULL; list = list->next)
14148 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14154 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14156 return music_info_listed_ext(list, basename, FALSE);
14159 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14161 return music_info_listed_ext(list, basename, TRUE);
14164 void LoadMusicInfo(void)
14166 int num_music_noconf = getMusicListSize_NoConf();
14167 int num_music = getMusicListSize();
14168 int num_sounds = getSoundListSize();
14169 struct FileInfo *music, *sound;
14170 struct MusicFileInfo *next, **new;
14174 while (music_file_info != NULL)
14176 next = music_file_info->next;
14178 checked_free(music_file_info->basename);
14180 checked_free(music_file_info->title_header);
14181 checked_free(music_file_info->artist_header);
14182 checked_free(music_file_info->album_header);
14183 checked_free(music_file_info->year_header);
14184 checked_free(music_file_info->played_header);
14186 checked_free(music_file_info->title);
14187 checked_free(music_file_info->artist);
14188 checked_free(music_file_info->album);
14189 checked_free(music_file_info->year);
14190 checked_free(music_file_info->played);
14192 free(music_file_info);
14194 music_file_info = next;
14197 new = &music_file_info;
14199 // get (configured or unconfigured) music file info for all levels
14200 for (i = leveldir_current->first_level;
14201 i <= leveldir_current->last_level; i++)
14205 if (levelset.music[i] != MUS_UNDEFINED)
14207 // get music file info for configured level music
14208 music_nr = levelset.music[i];
14210 else if (num_music_noconf > 0)
14212 // get music file info for unconfigured level music
14213 int level_pos = i - leveldir_current->first_level;
14215 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14222 char *basename = getMusicInfoEntryFilename(music_nr);
14224 if (basename == NULL)
14227 if (!music_info_listed(music_file_info, basename))
14229 *new = get_music_file_info(basename, music_nr);
14232 new = &(*new)->next;
14236 // get music file info for all remaining configured music files
14237 for (i = 0; i < num_music; i++)
14239 music = getMusicListEntry(i);
14241 if (music->filename == NULL)
14244 if (strEqual(music->filename, UNDEFINED_FILENAME))
14247 // a configured file may be not recognized as music
14248 if (!FileIsMusic(music->filename))
14251 if (!music_info_listed(music_file_info, music->filename))
14253 *new = get_music_file_info(music->filename, i);
14256 new = &(*new)->next;
14260 // get sound file info for all configured sound files
14261 for (i = 0; i < num_sounds; i++)
14263 sound = getSoundListEntry(i);
14265 if (sound->filename == NULL)
14268 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14271 // a configured file may be not recognized as sound
14272 if (!FileIsSound(sound->filename))
14275 if (!sound_info_listed(music_file_info, sound->filename))
14277 *new = get_sound_file_info(sound->filename, i);
14279 new = &(*new)->next;
14283 // add pointers to previous list nodes
14285 struct MusicFileInfo *node = music_file_info;
14287 while (node != NULL)
14290 node->next->prev = node;
14296 static void add_helpanim_entry(int element, int action, int direction,
14297 int delay, int *num_list_entries)
14299 struct HelpAnimInfo *new_list_entry;
14300 (*num_list_entries)++;
14303 checked_realloc(helpanim_info,
14304 *num_list_entries * sizeof(struct HelpAnimInfo));
14305 new_list_entry = &helpanim_info[*num_list_entries - 1];
14307 new_list_entry->element = element;
14308 new_list_entry->action = action;
14309 new_list_entry->direction = direction;
14310 new_list_entry->delay = delay;
14313 static void print_unknown_token(char *filename, char *token, int token_nr)
14318 Warn("unknown token(s) found in config file:");
14319 Warn("- config file: '%s'", filename);
14322 Warn("- token: '%s'", token);
14325 static void print_unknown_token_end(int token_nr)
14331 void LoadHelpAnimInfo(void)
14333 char *filename = getHelpAnimFilename();
14334 SetupFileList *setup_file_list = NULL, *list;
14335 SetupFileHash *element_hash, *action_hash, *direction_hash;
14336 int num_list_entries = 0;
14337 int num_unknown_tokens = 0;
14340 if (fileExists(filename))
14341 setup_file_list = loadSetupFileList(filename);
14343 if (setup_file_list == NULL)
14345 // use reliable default values from static configuration
14346 SetupFileList *insert_ptr;
14348 insert_ptr = setup_file_list =
14349 newSetupFileList(helpanim_config[0].token,
14350 helpanim_config[0].value);
14352 for (i = 1; helpanim_config[i].token; i++)
14353 insert_ptr = addListEntry(insert_ptr,
14354 helpanim_config[i].token,
14355 helpanim_config[i].value);
14358 element_hash = newSetupFileHash();
14359 action_hash = newSetupFileHash();
14360 direction_hash = newSetupFileHash();
14362 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14363 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14365 for (i = 0; i < NUM_ACTIONS; i++)
14366 setHashEntry(action_hash, element_action_info[i].suffix,
14367 i_to_a(element_action_info[i].value));
14369 // do not store direction index (bit) here, but direction value!
14370 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14371 setHashEntry(direction_hash, element_direction_info[i].suffix,
14372 i_to_a(1 << element_direction_info[i].value));
14374 for (list = setup_file_list; list != NULL; list = list->next)
14376 char *element_token, *action_token, *direction_token;
14377 char *element_value, *action_value, *direction_value;
14378 int delay = atoi(list->value);
14380 if (strEqual(list->token, "end"))
14382 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14387 /* first try to break element into element/action/direction parts;
14388 if this does not work, also accept combined "element[.act][.dir]"
14389 elements (like "dynamite.active"), which are unique elements */
14391 if (strchr(list->token, '.') == NULL) // token contains no '.'
14393 element_value = getHashEntry(element_hash, list->token);
14394 if (element_value != NULL) // element found
14395 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14396 &num_list_entries);
14399 // no further suffixes found -- this is not an element
14400 print_unknown_token(filename, list->token, num_unknown_tokens++);
14406 // token has format "<prefix>.<something>"
14408 action_token = strchr(list->token, '.'); // suffix may be action ...
14409 direction_token = action_token; // ... or direction
14411 element_token = getStringCopy(list->token);
14412 *strchr(element_token, '.') = '\0';
14414 element_value = getHashEntry(element_hash, element_token);
14416 if (element_value == NULL) // this is no element
14418 element_value = getHashEntry(element_hash, list->token);
14419 if (element_value != NULL) // combined element found
14420 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14421 &num_list_entries);
14423 print_unknown_token(filename, list->token, num_unknown_tokens++);
14425 free(element_token);
14430 action_value = getHashEntry(action_hash, action_token);
14432 if (action_value != NULL) // action found
14434 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14435 &num_list_entries);
14437 free(element_token);
14442 direction_value = getHashEntry(direction_hash, direction_token);
14444 if (direction_value != NULL) // direction found
14446 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14447 &num_list_entries);
14449 free(element_token);
14454 if (strchr(action_token + 1, '.') == NULL)
14456 // no further suffixes found -- this is not an action nor direction
14458 element_value = getHashEntry(element_hash, list->token);
14459 if (element_value != NULL) // combined element found
14460 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14461 &num_list_entries);
14463 print_unknown_token(filename, list->token, num_unknown_tokens++);
14465 free(element_token);
14470 // token has format "<prefix>.<suffix>.<something>"
14472 direction_token = strchr(action_token + 1, '.');
14474 action_token = getStringCopy(action_token);
14475 *strchr(action_token + 1, '.') = '\0';
14477 action_value = getHashEntry(action_hash, action_token);
14479 if (action_value == NULL) // this is no action
14481 element_value = getHashEntry(element_hash, list->token);
14482 if (element_value != NULL) // combined element found
14483 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14484 &num_list_entries);
14486 print_unknown_token(filename, list->token, num_unknown_tokens++);
14488 free(element_token);
14489 free(action_token);
14494 direction_value = getHashEntry(direction_hash, direction_token);
14496 if (direction_value != NULL) // direction found
14498 add_helpanim_entry(atoi(element_value), atoi(action_value),
14499 atoi(direction_value), delay, &num_list_entries);
14501 free(element_token);
14502 free(action_token);
14507 // this is no direction
14509 element_value = getHashEntry(element_hash, list->token);
14510 if (element_value != NULL) // combined element found
14511 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14512 &num_list_entries);
14514 print_unknown_token(filename, list->token, num_unknown_tokens++);
14516 free(element_token);
14517 free(action_token);
14520 print_unknown_token_end(num_unknown_tokens);
14522 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14523 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14525 freeSetupFileList(setup_file_list);
14526 freeSetupFileHash(element_hash);
14527 freeSetupFileHash(action_hash);
14528 freeSetupFileHash(direction_hash);
14531 for (i = 0; i < num_list_entries; i++)
14532 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14533 EL_NAME(helpanim_info[i].element),
14534 helpanim_info[i].element,
14535 helpanim_info[i].action,
14536 helpanim_info[i].direction,
14537 helpanim_info[i].delay);
14541 void LoadHelpTextInfo(void)
14543 char *filename = getHelpTextFilename();
14546 if (helptext_info != NULL)
14548 freeSetupFileHash(helptext_info);
14549 helptext_info = NULL;
14552 if (fileExists(filename))
14553 helptext_info = loadSetupFileHash(filename);
14555 if (helptext_info == NULL)
14557 // use reliable default values from static configuration
14558 helptext_info = newSetupFileHash();
14560 for (i = 0; helptext_config[i].token; i++)
14561 setHashEntry(helptext_info,
14562 helptext_config[i].token,
14563 helptext_config[i].value);
14567 BEGIN_HASH_ITERATION(helptext_info, itr)
14569 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14570 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14572 END_HASH_ITERATION(hash, itr)
14577 // ----------------------------------------------------------------------------
14579 // ----------------------------------------------------------------------------
14581 #define MAX_NUM_CONVERT_LEVELS 1000
14583 void ConvertLevels(void)
14585 static LevelDirTree *convert_leveldir = NULL;
14586 static int convert_level_nr = -1;
14587 static int num_levels_handled = 0;
14588 static int num_levels_converted = 0;
14589 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14592 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14593 global.convert_leveldir);
14595 if (convert_leveldir == NULL)
14596 Fail("no such level identifier: '%s'", global.convert_leveldir);
14598 leveldir_current = convert_leveldir;
14600 if (global.convert_level_nr != -1)
14602 convert_leveldir->first_level = global.convert_level_nr;
14603 convert_leveldir->last_level = global.convert_level_nr;
14606 convert_level_nr = convert_leveldir->first_level;
14608 PrintLine("=", 79);
14609 Print("Converting levels\n");
14610 PrintLine("-", 79);
14611 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14612 Print("Level series name: '%s'\n", convert_leveldir->name);
14613 Print("Level series author: '%s'\n", convert_leveldir->author);
14614 Print("Number of levels: %d\n", convert_leveldir->levels);
14615 PrintLine("=", 79);
14618 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14619 levels_failed[i] = FALSE;
14621 while (convert_level_nr <= convert_leveldir->last_level)
14623 char *level_filename;
14626 level_nr = convert_level_nr++;
14628 Print("Level %03d: ", level_nr);
14630 LoadLevel(level_nr);
14631 if (level.no_level_file || level.no_valid_file)
14633 Print("(no level)\n");
14637 Print("converting level ... ");
14640 // special case: conversion of some EMC levels as requested by ACME
14641 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14644 level_filename = getDefaultLevelFilename(level_nr);
14645 new_level = !fileExists(level_filename);
14649 SaveLevel(level_nr);
14651 num_levels_converted++;
14653 Print("converted.\n");
14657 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14658 levels_failed[level_nr] = TRUE;
14660 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14663 num_levels_handled++;
14667 PrintLine("=", 79);
14668 Print("Number of levels handled: %d\n", num_levels_handled);
14669 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14670 (num_levels_handled ?
14671 num_levels_converted * 100 / num_levels_handled : 0));
14672 PrintLine("-", 79);
14673 Print("Summary (for automatic parsing by scripts):\n");
14674 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14675 convert_leveldir->identifier, num_levels_converted,
14676 num_levels_handled,
14677 (num_levels_handled ?
14678 num_levels_converted * 100 / num_levels_handled : 0));
14680 if (num_levels_handled != num_levels_converted)
14682 Print(", FAILED:");
14683 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14684 if (levels_failed[i])
14689 PrintLine("=", 79);
14691 CloseAllAndExit(0);
14695 // ----------------------------------------------------------------------------
14696 // create and save images for use in level sketches (raw BMP format)
14697 // ----------------------------------------------------------------------------
14699 void CreateLevelSketchImages(void)
14705 InitElementPropertiesGfxElement();
14707 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14708 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14710 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14712 int element = getMappedElement(i);
14713 char basename1[16];
14714 char basename2[16];
14718 sprintf(basename1, "%04d.bmp", i);
14719 sprintf(basename2, "%04ds.bmp", i);
14721 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14722 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14724 DrawSizedElement(0, 0, element, TILESIZE);
14725 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14727 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14728 Fail("cannot save level sketch image file '%s'", filename1);
14730 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14731 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14733 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14734 Fail("cannot save level sketch image file '%s'", filename2);
14739 // create corresponding SQL statements (for normal and small images)
14742 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14743 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14746 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14747 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14749 // optional: create content for forum level sketch demonstration post
14751 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14754 FreeBitmap(bitmap1);
14755 FreeBitmap(bitmap2);
14758 fprintf(stderr, "\n");
14760 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14762 CloseAllAndExit(0);
14766 // ----------------------------------------------------------------------------
14767 // create and save images for element collecting animations (raw BMP format)
14768 // ----------------------------------------------------------------------------
14770 static boolean createCollectImage(int element)
14772 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14775 void CreateCollectElementImages(void)
14779 int anim_frames = num_steps - 1;
14780 int tile_size = TILESIZE;
14781 int anim_width = tile_size * anim_frames;
14782 int anim_height = tile_size;
14783 int num_collect_images = 0;
14784 int pos_collect_images = 0;
14786 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14787 if (createCollectImage(i))
14788 num_collect_images++;
14790 Info("Creating %d element collecting animation images ...",
14791 num_collect_images);
14793 int dst_width = anim_width * 2;
14794 int dst_height = anim_height * num_collect_images / 2;
14795 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14796 char *basename_bmp = "RocksCollect.bmp";
14797 char *basename_png = "RocksCollect.png";
14798 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14799 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14800 int len_filename_bmp = strlen(filename_bmp);
14801 int len_filename_png = strlen(filename_png);
14802 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14803 char cmd_convert[max_command_len];
14805 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14809 // force using RGBA surface for destination bitmap
14810 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14811 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14813 dst_bitmap->surface =
14814 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14816 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14818 if (!createCollectImage(i))
14821 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14822 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14823 int graphic = el2img(i);
14824 char *token_name = element_info[i].token_name;
14825 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14826 Bitmap *src_bitmap;
14829 Info("- creating collecting image for '%s' ...", token_name);
14831 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14833 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14834 tile_size, tile_size, 0, 0);
14836 // force using RGBA surface for temporary bitmap (using transparent black)
14837 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14838 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14840 tmp_bitmap->surface =
14841 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14843 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14845 for (j = 0; j < anim_frames; j++)
14847 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14848 int frame_size = frame_size_final * num_steps;
14849 int offset = (tile_size - frame_size_final) / 2;
14850 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14852 while (frame_size > frame_size_final)
14856 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14858 FreeBitmap(frame_bitmap);
14860 frame_bitmap = half_bitmap;
14863 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14864 frame_size_final, frame_size_final,
14865 dst_x + j * tile_size + offset, dst_y + offset);
14867 FreeBitmap(frame_bitmap);
14870 tmp_bitmap->surface_masked = NULL;
14872 FreeBitmap(tmp_bitmap);
14874 pos_collect_images++;
14877 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14878 Fail("cannot save element collecting image file '%s'", filename_bmp);
14880 FreeBitmap(dst_bitmap);
14882 Info("Converting image file from BMP to PNG ...");
14884 if (system(cmd_convert) != 0)
14885 Fail("converting image file failed");
14887 unlink(filename_bmp);
14891 CloseAllAndExit(0);
14895 // ----------------------------------------------------------------------------
14896 // create and save images for custom and group elements (raw BMP format)
14897 // ----------------------------------------------------------------------------
14899 void CreateCustomElementImages(char *directory)
14901 char *src_basename = "RocksCE-template.ilbm";
14902 char *dst_basename = "RocksCE.bmp";
14903 char *src_filename = getPath2(directory, src_basename);
14904 char *dst_filename = getPath2(directory, dst_basename);
14905 Bitmap *src_bitmap;
14907 int yoffset_ce = 0;
14908 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14911 InitVideoDefaults();
14913 ReCreateBitmap(&backbuffer, video.width, video.height);
14915 src_bitmap = LoadImage(src_filename);
14917 bitmap = CreateBitmap(TILEX * 16 * 2,
14918 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14921 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14928 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14929 TILEX * x, TILEY * y + yoffset_ce);
14931 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14933 TILEX * x + TILEX * 16,
14934 TILEY * y + yoffset_ce);
14936 for (j = 2; j >= 0; j--)
14940 BlitBitmap(src_bitmap, bitmap,
14941 TILEX + c * 7, 0, 6, 10,
14942 TILEX * x + 6 + j * 7,
14943 TILEY * y + 11 + yoffset_ce);
14945 BlitBitmap(src_bitmap, bitmap,
14946 TILEX + c * 8, TILEY, 6, 10,
14947 TILEX * 16 + TILEX * x + 6 + j * 8,
14948 TILEY * y + 10 + yoffset_ce);
14954 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14961 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14962 TILEX * x, TILEY * y + yoffset_ge);
14964 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14966 TILEX * x + TILEX * 16,
14967 TILEY * y + yoffset_ge);
14969 for (j = 1; j >= 0; j--)
14973 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14974 TILEX * x + 6 + j * 10,
14975 TILEY * y + 11 + yoffset_ge);
14977 BlitBitmap(src_bitmap, bitmap,
14978 TILEX + c * 8, TILEY + 12, 6, 10,
14979 TILEX * 16 + TILEX * x + 10 + j * 8,
14980 TILEY * y + 10 + yoffset_ge);
14986 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14987 Fail("cannot save CE graphics file '%s'", dst_filename);
14989 FreeBitmap(bitmap);
14991 CloseAllAndExit(0);