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_BOOLEAN, CONF_VALUE_8_BIT(3),
743 &li.bd_magic_wall_zero_infinite, TRUE
746 EL_BD_MAGIC_WALL, -1,
747 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
748 &li.bd_magic_wall_break_scan, FALSE
751 EL_BD_MAGIC_WALL, -1,
752 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
753 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
756 EL_BD_MAGIC_WALL, -1,
757 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
758 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
761 EL_BD_MAGIC_WALL, -1,
762 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
763 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
766 EL_BD_MAGIC_WALL, -1,
767 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
768 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
771 EL_BD_MAGIC_WALL, -1,
772 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
773 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
776 EL_BD_MAGIC_WALL, -1,
777 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
778 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
781 EL_BD_MAGIC_WALL, -1,
782 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
783 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
788 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
789 &li.bd_clock_extra_time, 30
793 EL_BD_VOODOO_DOLL, -1,
794 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
795 &li.bd_voodoo_collects_diamonds, FALSE
798 EL_BD_VOODOO_DOLL, -1,
799 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
800 &li.bd_voodoo_hurt_kills_player, FALSE
803 EL_BD_VOODOO_DOLL, -1,
804 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
805 &li.bd_voodoo_dies_by_rock, FALSE
808 EL_BD_VOODOO_DOLL, -1,
809 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
810 &li.bd_voodoo_vanish_by_explosion, TRUE
813 EL_BD_VOODOO_DOLL, -1,
814 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
815 &li.bd_voodoo_penalty_time, 30
820 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
821 &li.bd_slime_is_predictable, TRUE
825 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
826 &li.bd_slime_permeability_rate, 100
830 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
831 &li.bd_slime_permeability_bits_c64, 0
835 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
836 &li.bd_slime_random_seed_c64, -1
840 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
841 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
845 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
846 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
850 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
851 &li.bd_slime_eats_element_2, EL_BD_ROCK
855 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
856 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
860 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
861 &li.bd_slime_eats_element_3, EL_BD_NUT
865 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
866 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
871 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
872 &li.bd_acid_eats_element, EL_BD_SAND
876 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
877 &li.bd_acid_spread_rate, 3
881 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
882 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
887 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
888 &li.bd_biter_move_delay, 0
892 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
893 &li.bd_biter_eats_element, EL_BD_DIAMOND
898 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
899 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
903 EL_BD_EXPANDABLE_WALL_ANY, -1,
904 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
905 &li.bd_change_expanding_wall, FALSE
908 EL_BD_EXPANDABLE_WALL_ANY, -1,
909 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
910 &li.bd_expanding_wall_looks_like, EL_BD_WALL
914 EL_BD_REPLICATOR, -1,
915 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
916 &li.bd_replicators_active, TRUE
919 EL_BD_REPLICATOR, -1,
920 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
921 &li.bd_replicator_create_delay, 4
925 EL_BD_CONVEYOR_LEFT, -1,
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
927 &li.bd_conveyor_belts_active, TRUE
930 EL_BD_CONVEYOR_LEFT, -1,
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.bd_conveyor_belts_changed, FALSE
937 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
938 &li.bd_water_cannot_flow_down, FALSE
943 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
944 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
948 EL_BD_PNEUMATIC_HAMMER, -1,
949 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
950 &li.bd_hammer_walls_break_delay, 5
953 EL_BD_PNEUMATIC_HAMMER, -1,
954 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
955 &li.bd_hammer_walls_reappear, FALSE
958 EL_BD_PNEUMATIC_HAMMER, -1,
959 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
960 &li.bd_hammer_walls_reappear_delay, 100
964 EL_BD_ROCKET_LAUNCHER, -1,
965 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
966 &li.bd_infinite_rockets, FALSE
971 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
972 &li.bd_num_skeletons_needed_for_pot, 5
976 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
977 &li.bd_skeleton_worth_num_diamonds, 0
981 EL_BD_CREATURE_SWITCH, -1,
982 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
983 &li.bd_creatures_start_backwards, FALSE
986 EL_BD_CREATURE_SWITCH, -1,
987 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
988 &li.bd_creatures_turn_on_hatching, FALSE
991 EL_BD_CREATURE_SWITCH, -1,
992 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
993 &li.bd_creatures_auto_turn_delay, 0
997 EL_BD_GRAVITY_SWITCH, -1,
998 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
999 &li.bd_gravity_direction, GD_MV_DOWN
1002 EL_BD_GRAVITY_SWITCH, -1,
1003 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1004 &li.bd_gravity_switch_active, FALSE
1007 EL_BD_GRAVITY_SWITCH, -1,
1008 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1009 &li.bd_gravity_switch_delay, 10
1012 EL_BD_GRAVITY_SWITCH, -1,
1013 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1014 &li.bd_gravity_affects_all, TRUE
1017 // (the following values are related to various game elements)
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1022 &li.score[SC_EMERALD], 10
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1028 &li.score[SC_DIAMOND], 10
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 &li.score[SC_BUG], 10
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1040 &li.score[SC_SPACESHIP], 10
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1046 &li.score[SC_PACMAN], 10
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1052 &li.score[SC_NUT], 10
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.score[SC_DYNAMITE], 10
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.score[SC_KEY], 10
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1070 &li.score[SC_PEARL], 10
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1076 &li.score[SC_CRYSTAL], 10
1079 // (amoeba values used by R'n'D game engine only)
1082 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1083 &li.amoeba_content, EL_DIAMOND
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1088 &li.amoeba_speed, 10
1092 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1093 &li.grow_into_diggable, TRUE
1095 // (amoeba values used by BD game engine only)
1098 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1099 &li.bd_amoeba_wait_for_hatching, FALSE
1103 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1104 &li.bd_amoeba_start_immediately, TRUE
1108 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1109 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1113 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1114 &li.bd_amoeba_threshold_too_big, 200
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1119 &li.bd_amoeba_slow_growth_time, 200
1123 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1124 &li.bd_amoeba_slow_growth_rate, 3
1128 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1129 &li.bd_amoeba_fast_growth_rate, 25
1133 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1134 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1138 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1139 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1145 &li.bd_amoeba_2_threshold_too_big, 200
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1150 &li.bd_amoeba_2_slow_growth_time, 200
1154 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1155 &li.bd_amoeba_2_slow_growth_rate, 3
1159 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1160 &li.bd_amoeba_2_fast_growth_rate, 25
1164 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1165 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1169 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1170 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1175 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1179 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1180 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1185 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1186 &li.yamyam_content, EL_ROCK, NULL,
1187 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1191 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1192 &li.score[SC_YAMYAM], 10
1197 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1198 &li.score[SC_ROBOT], 10
1202 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1208 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1214 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1215 &li.time_magic_wall, 10
1219 EL_GAME_OF_LIFE, -1,
1220 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1221 &li.game_of_life[0], 2
1224 EL_GAME_OF_LIFE, -1,
1225 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1226 &li.game_of_life[1], 3
1229 EL_GAME_OF_LIFE, -1,
1230 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1231 &li.game_of_life[2], 3
1234 EL_GAME_OF_LIFE, -1,
1235 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1236 &li.game_of_life[3], 3
1239 EL_GAME_OF_LIFE, -1,
1240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1241 &li.use_life_bugs, FALSE
1246 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1251 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1256 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1266 EL_TIMEGATE_SWITCH, -1,
1267 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1268 &li.time_timegate, 10
1272 EL_LIGHT_SWITCH_ACTIVE, -1,
1273 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1278 EL_SHIELD_NORMAL, -1,
1279 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1280 &li.shield_normal_time, 10
1283 EL_SHIELD_NORMAL, -1,
1284 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1285 &li.score[SC_SHIELD], 10
1289 EL_SHIELD_DEADLY, -1,
1290 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1291 &li.shield_deadly_time, 10
1294 EL_SHIELD_DEADLY, -1,
1295 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1296 &li.score[SC_SHIELD], 10
1301 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1306 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1307 &li.extra_time_score, 10
1311 EL_TIME_ORB_FULL, -1,
1312 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1313 &li.time_orb_time, 10
1316 EL_TIME_ORB_FULL, -1,
1317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1318 &li.use_time_orb_bug, FALSE
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1324 &li.use_spring_bug, FALSE
1329 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1330 &li.android_move_time, 10
1334 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1335 &li.android_clone_time, 10
1338 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1339 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1340 &li.android_clone_element[0], EL_EMPTY, NULL,
1341 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1345 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1346 &li.android_clone_element[0], EL_EMPTY, NULL,
1347 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1353 &li.lenses_score, 10
1357 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1362 EL_EMC_MAGNIFIER, -1,
1363 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1364 &li.magnify_score, 10
1367 EL_EMC_MAGNIFIER, -1,
1368 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1369 &li.magnify_time, 10
1373 EL_EMC_MAGIC_BALL, -1,
1374 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1378 EL_EMC_MAGIC_BALL, -1,
1379 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1380 &li.ball_random, FALSE
1383 EL_EMC_MAGIC_BALL, -1,
1384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1385 &li.ball_active_initial, FALSE
1388 EL_EMC_MAGIC_BALL, -1,
1389 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1390 &li.ball_content, EL_EMPTY, NULL,
1391 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1395 EL_SOKOBAN_FIELD_EMPTY, -1,
1396 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1397 &li.sb_fields_needed, TRUE
1401 EL_SOKOBAN_OBJECT, -1,
1402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1403 &li.sb_objects_needed, TRUE
1408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1409 &li.mm_laser_red, FALSE
1413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1414 &li.mm_laser_green, FALSE
1418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1419 &li.mm_laser_blue, TRUE
1424 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1425 &li.df_laser_red, TRUE
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1430 &li.df_laser_green, TRUE
1434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1435 &li.df_laser_blue, FALSE
1439 EL_MM_FUSE_ACTIVE, -1,
1440 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1441 &li.mm_time_fuse, 25
1445 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1446 &li.mm_time_bomb, 75
1450 EL_MM_GRAY_BALL, -1,
1451 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1452 &li.mm_time_ball, 75
1455 EL_MM_GRAY_BALL, -1,
1456 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1457 &li.mm_ball_choice_mode, ANIM_RANDOM
1460 EL_MM_GRAY_BALL, -1,
1461 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1462 &li.mm_ball_content, EL_EMPTY, NULL,
1463 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1466 EL_MM_GRAY_BALL, -1,
1467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1468 &li.rotate_mm_ball_content, TRUE
1471 EL_MM_GRAY_BALL, -1,
1472 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1473 &li.explode_mm_ball, FALSE
1477 EL_MM_STEEL_BLOCK, -1,
1478 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1479 &li.mm_time_block, 75
1482 EL_MM_LIGHTBALL, -1,
1483 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1484 &li.score[SC_ELEM_BONUS], 10
1494 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1498 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1499 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1503 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1504 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1510 &xx_envelope.autowrap, FALSE
1514 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1515 &xx_envelope.centered, FALSE
1520 TYPE_STRING, CONF_VALUE_BYTES(1),
1521 &xx_envelope.text, -1, NULL,
1522 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1523 &xx_default_string_empty[0]
1533 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1537 TYPE_STRING, CONF_VALUE_BYTES(1),
1538 &xx_ei.description[0], -1,
1539 &yy_ei.description[0],
1540 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1541 &xx_default_description[0]
1546 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1547 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1548 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1550 #if ENABLE_RESERVED_CODE
1551 // (reserved for later use)
1554 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1555 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1556 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1562 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1563 &xx_ei.use_gfx_element, FALSE,
1564 &yy_ei.use_gfx_element
1568 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1569 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1570 &yy_ei.gfx_element_initial
1575 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1576 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1577 &yy_ei.access_direction
1582 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1583 &xx_ei.collect_score_initial, 10,
1584 &yy_ei.collect_score_initial
1588 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1589 &xx_ei.collect_count_initial, 1,
1590 &yy_ei.collect_count_initial
1595 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1596 &xx_ei.ce_value_fixed_initial, 0,
1597 &yy_ei.ce_value_fixed_initial
1601 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1602 &xx_ei.ce_value_random_initial, 0,
1603 &yy_ei.ce_value_random_initial
1607 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1608 &xx_ei.use_last_ce_value, FALSE,
1609 &yy_ei.use_last_ce_value
1614 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1615 &xx_ei.push_delay_fixed, 8,
1616 &yy_ei.push_delay_fixed
1620 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1621 &xx_ei.push_delay_random, 8,
1622 &yy_ei.push_delay_random
1626 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1627 &xx_ei.drop_delay_fixed, 0,
1628 &yy_ei.drop_delay_fixed
1632 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1633 &xx_ei.drop_delay_random, 0,
1634 &yy_ei.drop_delay_random
1638 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1639 &xx_ei.move_delay_fixed, 0,
1640 &yy_ei.move_delay_fixed
1644 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1645 &xx_ei.move_delay_random, 0,
1646 &yy_ei.move_delay_random
1650 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1651 &xx_ei.step_delay_fixed, 0,
1652 &yy_ei.step_delay_fixed
1656 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1657 &xx_ei.step_delay_random, 0,
1658 &yy_ei.step_delay_random
1663 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1664 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1669 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1670 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1671 &yy_ei.move_direction_initial
1675 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1676 &xx_ei.move_stepsize, TILEX / 8,
1677 &yy_ei.move_stepsize
1682 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1683 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1684 &yy_ei.move_enter_element
1688 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1689 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1690 &yy_ei.move_leave_element
1694 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1695 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1696 &yy_ei.move_leave_type
1701 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1702 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1703 &yy_ei.slippery_type
1708 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1709 &xx_ei.explosion_type, EXPLODES_3X3,
1710 &yy_ei.explosion_type
1714 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1715 &xx_ei.explosion_delay, 16,
1716 &yy_ei.explosion_delay
1720 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1721 &xx_ei.ignition_delay, 8,
1722 &yy_ei.ignition_delay
1727 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1728 &xx_ei.content, EL_EMPTY_SPACE,
1730 &xx_num_contents, 1, 1
1733 // ---------- "num_change_pages" must be the last entry ---------------------
1736 -1, SAVE_CONF_ALWAYS,
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1738 &xx_ei.num_change_pages, 1,
1739 &yy_ei.num_change_pages
1750 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1752 // ---------- "current_change_page" must be the first entry -----------------
1755 -1, SAVE_CONF_ALWAYS,
1756 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1757 &xx_current_change_page, -1
1760 // ---------- (the remaining entries can be in any order) -------------------
1764 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1765 &xx_change.can_change, FALSE
1770 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1771 &xx_event_bits[0], 0
1775 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1776 &xx_event_bits[1], 0
1781 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1782 &xx_change.trigger_player, CH_PLAYER_ANY
1786 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1787 &xx_change.trigger_side, CH_SIDE_ANY
1791 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1792 &xx_change.trigger_page, CH_PAGE_ANY
1797 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1798 &xx_change.target_element, EL_EMPTY_SPACE
1803 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1804 &xx_change.delay_fixed, 0
1808 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1809 &xx_change.delay_random, 0
1813 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1814 &xx_change.delay_frames, FRAMES_PER_SECOND
1819 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1820 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1825 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1826 &xx_change.explode, FALSE
1830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1831 &xx_change.use_target_content, FALSE
1835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1836 &xx_change.only_if_complete, FALSE
1840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1841 &xx_change.use_random_replace, FALSE
1845 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1846 &xx_change.random_percentage, 100
1850 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1851 &xx_change.replace_when, CP_WHEN_EMPTY
1856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1857 &xx_change.has_action, FALSE
1861 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1862 &xx_change.action_type, CA_NO_ACTION
1866 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1867 &xx_change.action_mode, CA_MODE_UNDEFINED
1871 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1872 &xx_change.action_arg, CA_ARG_UNDEFINED
1877 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1878 &xx_change.action_element, EL_EMPTY_SPACE
1883 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1884 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1885 &xx_num_contents, 1, 1
1895 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1899 TYPE_STRING, CONF_VALUE_BYTES(1),
1900 &xx_ei.description[0], -1, NULL,
1901 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1902 &xx_default_description[0]
1907 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1908 &xx_ei.use_gfx_element, FALSE
1912 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1913 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1918 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1919 &xx_group.choice_mode, ANIM_RANDOM
1924 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1925 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1926 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1936 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1941 &xx_ei.use_gfx_element, FALSE
1945 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1946 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1956 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1960 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1961 &li.block_snap_field, TRUE
1965 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1966 &li.continuous_snapping, TRUE
1970 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1971 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1975 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1976 &li.use_start_element[0], FALSE
1980 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1981 &li.start_element[0], EL_PLAYER_1
1985 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1986 &li.use_artwork_element[0], FALSE
1990 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1991 &li.artwork_element[0], EL_PLAYER_1
1995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1996 &li.use_explosion_element[0], FALSE
2000 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
2001 &li.explosion_element[0], EL_PLAYER_1
2016 filetype_id_list[] =
2018 { LEVEL_FILE_TYPE_RND, "RND" },
2019 { LEVEL_FILE_TYPE_BD, "BD" },
2020 { LEVEL_FILE_TYPE_EM, "EM" },
2021 { LEVEL_FILE_TYPE_SP, "SP" },
2022 { LEVEL_FILE_TYPE_DX, "DX" },
2023 { LEVEL_FILE_TYPE_SB, "SB" },
2024 { LEVEL_FILE_TYPE_DC, "DC" },
2025 { LEVEL_FILE_TYPE_MM, "MM" },
2026 { LEVEL_FILE_TYPE_MM, "DF" },
2031 // ============================================================================
2032 // level file functions
2033 // ============================================================================
2035 static boolean check_special_flags(char *flag)
2037 if (strEqual(options.special_flags, flag) ||
2038 strEqual(leveldir_current->special_flags, flag))
2044 static struct DateInfo getCurrentDate(void)
2046 time_t epoch_seconds = time(NULL);
2047 struct tm *now = localtime(&epoch_seconds);
2048 struct DateInfo date;
2050 date.year = now->tm_year + 1900;
2051 date.month = now->tm_mon + 1;
2052 date.day = now->tm_mday;
2054 date.src = DATE_SRC_CLOCK;
2059 static void resetEventFlags(struct ElementChangeInfo *change)
2063 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2064 change->has_event[i] = FALSE;
2067 static void resetEventBits(void)
2071 for (i = 0; i < NUM_CE_BITFIELDS; i++)
2072 xx_event_bits[i] = 0;
2075 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2079 /* important: only change event flag if corresponding event bit is set
2080 (this is because all xx_event_bits[] values are loaded separately,
2081 and all xx_event_bits[] values are set back to zero before loading
2082 another value xx_event_bits[x] (each value representing 32 flags)) */
2084 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2085 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2086 change->has_event[i] = TRUE;
2089 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2093 /* in contrast to the above function setEventFlagsFromEventBits(), it
2094 would also be possible to set all bits in xx_event_bits[] to 0 or 1
2095 depending on the corresponding change->has_event[i] values here, as
2096 all xx_event_bits[] values are reset in resetEventBits() before */
2098 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2099 if (change->has_event[i])
2100 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2103 static char *getDefaultElementDescription(struct ElementInfo *ei)
2105 static char description[MAX_ELEMENT_NAME_LEN + 1];
2106 char *default_description = (ei->custom_description != NULL ?
2107 ei->custom_description :
2108 ei->editor_description);
2111 // always start with reliable default values
2112 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2113 description[i] = '\0';
2115 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2116 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2118 return &description[0];
2121 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2123 char *default_description = getDefaultElementDescription(ei);
2126 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2127 ei->description[i] = default_description[i];
2130 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2134 for (i = 0; conf[i].data_type != -1; i++)
2136 int default_value = conf[i].default_value;
2137 int data_type = conf[i].data_type;
2138 int conf_type = conf[i].conf_type;
2139 int byte_mask = conf_type & CONF_MASK_BYTES;
2141 if (byte_mask == CONF_MASK_MULTI_BYTES)
2143 int default_num_entities = conf[i].default_num_entities;
2144 int max_num_entities = conf[i].max_num_entities;
2146 *(int *)(conf[i].num_entities) = default_num_entities;
2148 if (data_type == TYPE_STRING)
2150 char *default_string = conf[i].default_string;
2151 char *string = (char *)(conf[i].value);
2153 strncpy(string, default_string, max_num_entities);
2155 else if (data_type == TYPE_ELEMENT_LIST)
2157 int *element_array = (int *)(conf[i].value);
2160 for (j = 0; j < max_num_entities; j++)
2161 element_array[j] = default_value;
2163 else if (data_type == TYPE_CONTENT_LIST)
2165 struct Content *content = (struct Content *)(conf[i].value);
2168 for (c = 0; c < max_num_entities; c++)
2169 for (y = 0; y < 3; y++)
2170 for (x = 0; x < 3; x++)
2171 content[c].e[x][y] = default_value;
2174 else // constant size configuration data (1, 2 or 4 bytes)
2176 if (data_type == TYPE_BOOLEAN)
2177 *(boolean *)(conf[i].value) = default_value;
2179 *(int *) (conf[i].value) = default_value;
2184 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2188 for (i = 0; conf[i].data_type != -1; i++)
2190 int data_type = conf[i].data_type;
2191 int conf_type = conf[i].conf_type;
2192 int byte_mask = conf_type & CONF_MASK_BYTES;
2194 if (byte_mask == CONF_MASK_MULTI_BYTES)
2196 int max_num_entities = conf[i].max_num_entities;
2198 if (data_type == TYPE_STRING)
2200 char *string = (char *)(conf[i].value);
2201 char *string_copy = (char *)(conf[i].value_copy);
2203 strncpy(string_copy, string, max_num_entities);
2205 else if (data_type == TYPE_ELEMENT_LIST)
2207 int *element_array = (int *)(conf[i].value);
2208 int *element_array_copy = (int *)(conf[i].value_copy);
2211 for (j = 0; j < max_num_entities; j++)
2212 element_array_copy[j] = element_array[j];
2214 else if (data_type == TYPE_CONTENT_LIST)
2216 struct Content *content = (struct Content *)(conf[i].value);
2217 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2220 for (c = 0; c < max_num_entities; c++)
2221 for (y = 0; y < 3; y++)
2222 for (x = 0; x < 3; x++)
2223 content_copy[c].e[x][y] = content[c].e[x][y];
2226 else // constant size configuration data (1, 2 or 4 bytes)
2228 if (data_type == TYPE_BOOLEAN)
2229 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2231 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2236 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2240 xx_ei = *ei_from; // copy element data into temporary buffer
2241 yy_ei = *ei_to; // copy element data into temporary buffer
2243 copyConfigFromConfigList(chunk_config_CUSX_base);
2248 // ---------- reinitialize and copy change pages ----------
2250 ei_to->num_change_pages = ei_from->num_change_pages;
2251 ei_to->current_change_page = ei_from->current_change_page;
2253 setElementChangePages(ei_to, ei_to->num_change_pages);
2255 for (i = 0; i < ei_to->num_change_pages; i++)
2256 ei_to->change_page[i] = ei_from->change_page[i];
2258 // ---------- copy group element info ----------
2259 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2260 *ei_to->group = *ei_from->group;
2262 // mark this custom element as modified
2263 ei_to->modified_settings = TRUE;
2266 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2268 int change_page_size = sizeof(struct ElementChangeInfo);
2270 ei->num_change_pages = MAX(1, change_pages);
2273 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2275 if (ei->current_change_page >= ei->num_change_pages)
2276 ei->current_change_page = ei->num_change_pages - 1;
2278 ei->change = &ei->change_page[ei->current_change_page];
2281 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2283 xx_change = *change; // copy change data into temporary buffer
2285 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2287 *change = xx_change;
2289 resetEventFlags(change);
2291 change->direct_action = 0;
2292 change->other_action = 0;
2294 change->pre_change_function = NULL;
2295 change->change_function = NULL;
2296 change->post_change_function = NULL;
2299 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2303 li = *level; // copy level data into temporary buffer
2304 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2305 *level = li; // copy temporary buffer back to level data
2307 setLevelInfoToDefaults_BD();
2308 setLevelInfoToDefaults_EM();
2309 setLevelInfoToDefaults_SP();
2310 setLevelInfoToDefaults_MM();
2312 level->native_bd_level = &native_bd_level;
2313 level->native_em_level = &native_em_level;
2314 level->native_sp_level = &native_sp_level;
2315 level->native_mm_level = &native_mm_level;
2317 level->file_version = FILE_VERSION_ACTUAL;
2318 level->game_version = GAME_VERSION_ACTUAL;
2320 level->creation_date = getCurrentDate();
2322 level->encoding_16bit_field = TRUE;
2323 level->encoding_16bit_yamyam = TRUE;
2324 level->encoding_16bit_amoeba = TRUE;
2326 // clear level name and level author string buffers
2327 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2328 level->name[i] = '\0';
2329 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2330 level->author[i] = '\0';
2332 // set level name and level author to default values
2333 strcpy(level->name, NAMELESS_LEVEL_NAME);
2334 strcpy(level->author, ANONYMOUS_NAME);
2336 // set level playfield to playable default level with player and exit
2337 for (x = 0; x < MAX_LEV_FIELDX; x++)
2338 for (y = 0; y < MAX_LEV_FIELDY; y++)
2339 level->field[x][y] = EL_SAND;
2341 level->field[0][0] = EL_PLAYER_1;
2342 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2344 BorderElement = EL_STEELWALL;
2346 // detect custom elements when loading them
2347 level->file_has_custom_elements = FALSE;
2349 // set all bug compatibility flags to "false" => do not emulate this bug
2350 level->use_action_after_change_bug = FALSE;
2352 if (leveldir_current)
2354 // try to determine better author name than 'anonymous'
2355 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2357 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2358 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2362 switch (LEVELCLASS(leveldir_current))
2364 case LEVELCLASS_TUTORIAL:
2365 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2368 case LEVELCLASS_CONTRIB:
2369 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2370 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2373 case LEVELCLASS_PRIVATE:
2374 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2375 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2379 // keep default value
2386 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2388 static boolean clipboard_elements_initialized = FALSE;
2391 InitElementPropertiesStatic();
2393 li = *level; // copy level data into temporary buffer
2394 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2395 *level = li; // copy temporary buffer back to level data
2397 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2400 struct ElementInfo *ei = &element_info[element];
2402 if (element == EL_MM_GRAY_BALL)
2404 struct LevelInfo_MM *level_mm = level->native_mm_level;
2407 for (j = 0; j < level->num_mm_ball_contents; j++)
2408 level->mm_ball_content[j] =
2409 map_element_MM_to_RND(level_mm->ball_content[j]);
2412 // never initialize clipboard elements after the very first time
2413 // (to be able to use clipboard elements between several levels)
2414 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2417 if (IS_ENVELOPE(element))
2419 int envelope_nr = element - EL_ENVELOPE_1;
2421 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2423 level->envelope[envelope_nr] = xx_envelope;
2426 if (IS_CUSTOM_ELEMENT(element) ||
2427 IS_GROUP_ELEMENT(element) ||
2428 IS_INTERNAL_ELEMENT(element))
2430 xx_ei = *ei; // copy element data into temporary buffer
2432 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2437 setElementChangePages(ei, 1);
2438 setElementChangeInfoToDefaults(ei->change);
2440 if (IS_CUSTOM_ELEMENT(element) ||
2441 IS_GROUP_ELEMENT(element))
2443 setElementDescriptionToDefault(ei);
2445 ei->modified_settings = FALSE;
2448 if (IS_CUSTOM_ELEMENT(element) ||
2449 IS_INTERNAL_ELEMENT(element))
2451 // internal values used in level editor
2453 ei->access_type = 0;
2454 ei->access_layer = 0;
2455 ei->access_protected = 0;
2456 ei->walk_to_action = 0;
2457 ei->smash_targets = 0;
2460 ei->can_explode_by_fire = FALSE;
2461 ei->can_explode_smashed = FALSE;
2462 ei->can_explode_impact = FALSE;
2464 ei->current_change_page = 0;
2467 if (IS_GROUP_ELEMENT(element) ||
2468 IS_INTERNAL_ELEMENT(element))
2470 struct ElementGroupInfo *group;
2472 // initialize memory for list of elements in group
2473 if (ei->group == NULL)
2474 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2478 xx_group = *group; // copy group data into temporary buffer
2480 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2485 if (IS_EMPTY_ELEMENT(element) ||
2486 IS_INTERNAL_ELEMENT(element))
2488 xx_ei = *ei; // copy element data into temporary buffer
2490 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2496 clipboard_elements_initialized = TRUE;
2499 static void setLevelInfoToDefaults(struct LevelInfo *level,
2500 boolean level_info_only,
2501 boolean reset_file_status)
2503 setLevelInfoToDefaults_Level(level);
2505 if (!level_info_only)
2506 setLevelInfoToDefaults_Elements(level);
2508 if (reset_file_status)
2510 level->no_valid_file = FALSE;
2511 level->no_level_file = FALSE;
2514 level->changed = FALSE;
2517 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2519 level_file_info->nr = 0;
2520 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2521 level_file_info->packed = FALSE;
2523 setString(&level_file_info->basename, NULL);
2524 setString(&level_file_info->filename, NULL);
2527 int getMappedElement_SB(int, boolean);
2529 static void ActivateLevelTemplate(void)
2533 if (check_special_flags("load_xsb_to_ces"))
2535 // fill smaller playfields with padding "beyond border wall" elements
2536 if (level.fieldx < level_template.fieldx ||
2537 level.fieldy < level_template.fieldy)
2539 short field[level.fieldx][level.fieldy];
2540 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2541 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2542 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2543 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2545 // copy old playfield (which is smaller than the visible area)
2546 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2547 field[x][y] = level.field[x][y];
2549 // fill new, larger playfield with "beyond border wall" elements
2550 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2551 level.field[x][y] = getMappedElement_SB('_', TRUE);
2553 // copy the old playfield to the middle of the new playfield
2554 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2555 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2557 level.fieldx = new_fieldx;
2558 level.fieldy = new_fieldy;
2562 // Currently there is no special action needed to activate the template
2563 // data, because 'element_info' property settings overwrite the original
2564 // level data, while all other variables do not change.
2566 // Exception: 'from_level_template' elements in the original level playfield
2567 // are overwritten with the corresponding elements at the same position in
2568 // playfield from the level template.
2570 for (x = 0; x < level.fieldx; x++)
2571 for (y = 0; y < level.fieldy; y++)
2572 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2573 level.field[x][y] = level_template.field[x][y];
2575 if (check_special_flags("load_xsb_to_ces"))
2577 struct LevelInfo level_backup = level;
2579 // overwrite all individual level settings from template level settings
2580 level = level_template;
2582 // restore level file info
2583 level.file_info = level_backup.file_info;
2585 // restore playfield size
2586 level.fieldx = level_backup.fieldx;
2587 level.fieldy = level_backup.fieldy;
2589 // restore playfield content
2590 for (x = 0; x < level.fieldx; x++)
2591 for (y = 0; y < level.fieldy; y++)
2592 level.field[x][y] = level_backup.field[x][y];
2594 // restore name and author from individual level
2595 strcpy(level.name, level_backup.name);
2596 strcpy(level.author, level_backup.author);
2598 // restore flag "use_custom_template"
2599 level.use_custom_template = level_backup.use_custom_template;
2603 static boolean checkForPackageFromBasename_BD(char *basename)
2605 // check for native BD level file extensions
2606 if (!strSuffixLower(basename, ".bd") &&
2607 !strSuffixLower(basename, ".bdr") &&
2608 !strSuffixLower(basename, ".brc") &&
2609 !strSuffixLower(basename, ".gds"))
2612 // check for standard single-level BD files (like "001.bd")
2613 if (strSuffixLower(basename, ".bd") &&
2614 strlen(basename) == 6 &&
2615 basename[0] >= '0' && basename[0] <= '9' &&
2616 basename[1] >= '0' && basename[1] <= '9' &&
2617 basename[2] >= '0' && basename[2] <= '9')
2620 // this is a level package in native BD file format
2624 static char *getLevelFilenameFromBasename(char *basename)
2626 static char *filename = NULL;
2628 checked_free(filename);
2630 filename = getPath2(getCurrentLevelDir(), basename);
2635 static int getFileTypeFromBasename(char *basename)
2637 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2639 static char *filename = NULL;
2640 struct stat file_status;
2642 // ---------- try to determine file type from filename ----------
2644 // check for typical filename of a Supaplex level package file
2645 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2646 return LEVEL_FILE_TYPE_SP;
2648 // check for typical filename of a Diamond Caves II level package file
2649 if (strSuffixLower(basename, ".dc") ||
2650 strSuffixLower(basename, ".dc2"))
2651 return LEVEL_FILE_TYPE_DC;
2653 // check for typical filename of a Sokoban level package file
2654 if (strSuffixLower(basename, ".xsb") &&
2655 strchr(basename, '%') == NULL)
2656 return LEVEL_FILE_TYPE_SB;
2658 // check for typical filename of a Boulder Dash (GDash) level package file
2659 if (checkForPackageFromBasename_BD(basename))
2660 return LEVEL_FILE_TYPE_BD;
2662 // ---------- try to determine file type from filesize ----------
2664 checked_free(filename);
2665 filename = getPath2(getCurrentLevelDir(), basename);
2667 if (stat(filename, &file_status) == 0)
2669 // check for typical filesize of a Supaplex level package file
2670 if (file_status.st_size == 170496)
2671 return LEVEL_FILE_TYPE_SP;
2674 return LEVEL_FILE_TYPE_UNKNOWN;
2677 static int getFileTypeFromMagicBytes(char *filename, int type)
2681 if ((file = openFile(filename, MODE_READ)))
2683 char chunk_name[CHUNK_ID_LEN + 1];
2685 getFileChunkBE(file, chunk_name, NULL);
2687 if (strEqual(chunk_name, "MMII") ||
2688 strEqual(chunk_name, "MIRR"))
2689 type = LEVEL_FILE_TYPE_MM;
2697 static boolean checkForPackageFromBasename(char *basename)
2699 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2700 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2702 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2705 static char *getSingleLevelBasenameExt(int nr, char *extension)
2707 static char basename[MAX_FILENAME_LEN];
2710 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2712 sprintf(basename, "%03d.%s", nr, extension);
2717 static char *getSingleLevelBasename(int nr)
2719 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2722 static char *getPackedLevelBasename(int type)
2724 static char basename[MAX_FILENAME_LEN];
2725 char *directory = getCurrentLevelDir();
2727 DirectoryEntry *dir_entry;
2729 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2731 if ((dir = openDirectory(directory)) == NULL)
2733 Warn("cannot read current level directory '%s'", directory);
2738 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2740 char *entry_basename = dir_entry->basename;
2741 int entry_type = getFileTypeFromBasename(entry_basename);
2743 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2745 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2748 strcpy(basename, entry_basename);
2755 closeDirectory(dir);
2760 static char *getSingleLevelFilename(int nr)
2762 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2765 #if ENABLE_UNUSED_CODE
2766 static char *getPackedLevelFilename(int type)
2768 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2772 char *getDefaultLevelFilename(int nr)
2774 return getSingleLevelFilename(nr);
2777 #if ENABLE_UNUSED_CODE
2778 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2782 lfi->packed = FALSE;
2784 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2785 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2789 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2790 int type, char *format, ...)
2792 static char basename[MAX_FILENAME_LEN];
2795 va_start(ap, format);
2796 vsprintf(basename, format, ap);
2800 lfi->packed = FALSE;
2802 setString(&lfi->basename, basename);
2803 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2806 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2812 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2813 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2816 static int getFiletypeFromID(char *filetype_id)
2818 char *filetype_id_lower;
2819 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2822 if (filetype_id == NULL)
2823 return LEVEL_FILE_TYPE_UNKNOWN;
2825 filetype_id_lower = getStringToLower(filetype_id);
2827 for (i = 0; filetype_id_list[i].id != NULL; i++)
2829 char *id_lower = getStringToLower(filetype_id_list[i].id);
2831 if (strEqual(filetype_id_lower, id_lower))
2832 filetype = filetype_id_list[i].filetype;
2836 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2840 free(filetype_id_lower);
2845 char *getLocalLevelTemplateFilename(void)
2847 return getDefaultLevelFilename(-1);
2850 char *getGlobalLevelTemplateFilename(void)
2852 // global variable "leveldir_current" must be modified in the loop below
2853 LevelDirTree *leveldir_current_last = leveldir_current;
2854 char *filename = NULL;
2856 // check for template level in path from current to topmost tree node
2858 while (leveldir_current != NULL)
2860 filename = getDefaultLevelFilename(-1);
2862 if (fileExists(filename))
2865 leveldir_current = leveldir_current->node_parent;
2868 // restore global variable "leveldir_current" modified in above loop
2869 leveldir_current = leveldir_current_last;
2874 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2878 // special case: level number is negative => check for level template file
2881 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2882 getSingleLevelBasename(-1));
2884 // replace local level template filename with global template filename
2885 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2887 // no fallback if template file not existing
2891 // special case: check for file name/pattern specified in "levelinfo.conf"
2892 if (leveldir_current->level_filename != NULL)
2894 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2896 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2897 leveldir_current->level_filename, nr);
2899 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2901 if (fileExists(lfi->filename))
2904 else if (leveldir_current->level_filetype != NULL)
2906 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2908 // check for specified native level file with standard file name
2909 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2910 "%03d.%s", nr, LEVELFILE_EXTENSION);
2911 if (fileExists(lfi->filename))
2915 // check for native Rocks'n'Diamonds level file
2916 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2917 "%03d.%s", nr, LEVELFILE_EXTENSION);
2918 if (fileExists(lfi->filename))
2921 // check for native Boulder Dash level file
2922 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2923 if (fileExists(lfi->filename))
2926 // check for Emerald Mine level file (V1)
2927 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2928 'a' + (nr / 10) % 26, '0' + nr % 10);
2929 if (fileExists(lfi->filename))
2931 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2932 'A' + (nr / 10) % 26, '0' + nr % 10);
2933 if (fileExists(lfi->filename))
2936 // check for Emerald Mine level file (V2 to V5)
2937 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2938 if (fileExists(lfi->filename))
2941 // check for Emerald Mine level file (V6 / single mode)
2942 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2943 if (fileExists(lfi->filename))
2945 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2946 if (fileExists(lfi->filename))
2949 // check for Emerald Mine level file (V6 / teamwork mode)
2950 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2951 if (fileExists(lfi->filename))
2953 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2954 if (fileExists(lfi->filename))
2957 // check for various packed level file formats
2958 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2959 if (fileExists(lfi->filename))
2962 // no known level file found -- use default values (and fail later)
2963 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2964 "%03d.%s", nr, LEVELFILE_EXTENSION);
2967 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2969 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2970 lfi->type = getFileTypeFromBasename(lfi->basename);
2972 if (lfi->type == LEVEL_FILE_TYPE_RND)
2973 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2976 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2978 // always start with reliable default values
2979 setFileInfoToDefaults(level_file_info);
2981 level_file_info->nr = nr; // set requested level number
2983 determineLevelFileInfo_Filename(level_file_info);
2984 determineLevelFileInfo_Filetype(level_file_info);
2987 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2988 struct LevelFileInfo *lfi_to)
2990 lfi_to->nr = lfi_from->nr;
2991 lfi_to->type = lfi_from->type;
2992 lfi_to->packed = lfi_from->packed;
2994 setString(&lfi_to->basename, lfi_from->basename);
2995 setString(&lfi_to->filename, lfi_from->filename);
2998 // ----------------------------------------------------------------------------
2999 // functions for loading R'n'D level
3000 // ----------------------------------------------------------------------------
3002 int getMappedElement(int element)
3004 // remap some (historic, now obsolete) elements
3008 case EL_PLAYER_OBSOLETE:
3009 element = EL_PLAYER_1;
3012 case EL_KEY_OBSOLETE:
3016 case EL_EM_KEY_1_FILE_OBSOLETE:
3017 element = EL_EM_KEY_1;
3020 case EL_EM_KEY_2_FILE_OBSOLETE:
3021 element = EL_EM_KEY_2;
3024 case EL_EM_KEY_3_FILE_OBSOLETE:
3025 element = EL_EM_KEY_3;
3028 case EL_EM_KEY_4_FILE_OBSOLETE:
3029 element = EL_EM_KEY_4;
3032 case EL_ENVELOPE_OBSOLETE:
3033 element = EL_ENVELOPE_1;
3041 if (element >= NUM_FILE_ELEMENTS)
3043 Warn("invalid level element %d", element);
3045 element = EL_UNKNOWN;
3053 static int getMappedElementByVersion(int element, int game_version)
3055 // remap some elements due to certain game version
3057 if (game_version <= VERSION_IDENT(2,2,0,0))
3059 // map game font elements
3060 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
3061 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3062 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
3063 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
3066 if (game_version < VERSION_IDENT(3,0,0,0))
3068 // map Supaplex gravity tube elements
3069 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
3070 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3071 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
3072 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
3079 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3081 level->file_version = getFileVersion(file);
3082 level->game_version = getFileVersion(file);
3087 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3089 level->creation_date.year = getFile16BitBE(file);
3090 level->creation_date.month = getFile8Bit(file);
3091 level->creation_date.day = getFile8Bit(file);
3093 level->creation_date.src = DATE_SRC_LEVELFILE;
3098 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3100 int initial_player_stepsize;
3101 int initial_player_gravity;
3104 level->fieldx = getFile8Bit(file);
3105 level->fieldy = getFile8Bit(file);
3107 level->time = getFile16BitBE(file);
3108 level->gems_needed = getFile16BitBE(file);
3110 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3111 level->name[i] = getFile8Bit(file);
3112 level->name[MAX_LEVEL_NAME_LEN] = 0;
3114 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3115 level->score[i] = getFile8Bit(file);
3117 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3118 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3119 for (y = 0; y < 3; y++)
3120 for (x = 0; x < 3; x++)
3121 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3123 level->amoeba_speed = getFile8Bit(file);
3124 level->time_magic_wall = getFile8Bit(file);
3125 level->time_wheel = getFile8Bit(file);
3126 level->amoeba_content = getMappedElement(getFile8Bit(file));
3128 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3131 for (i = 0; i < MAX_PLAYERS; i++)
3132 level->initial_player_stepsize[i] = initial_player_stepsize;
3134 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3136 for (i = 0; i < MAX_PLAYERS; i++)
3137 level->initial_player_gravity[i] = initial_player_gravity;
3139 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3142 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3144 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3145 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3146 level->can_move_into_acid_bits = getFile32BitBE(file);
3147 level->dont_collide_with_bits = getFile8Bit(file);
3149 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3150 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3152 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3153 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3154 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3156 level->game_engine_type = getFile8Bit(file);
3158 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3163 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3167 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3168 level->name[i] = getFile8Bit(file);
3169 level->name[MAX_LEVEL_NAME_LEN] = 0;
3174 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3178 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3179 level->author[i] = getFile8Bit(file);
3180 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3185 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3188 int chunk_size_expected = level->fieldx * level->fieldy;
3190 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3191 stored with 16-bit encoding (and should be twice as big then).
3192 Even worse, playfield data was stored 16-bit when only yamyam content
3193 contained 16-bit elements and vice versa. */
3195 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3196 chunk_size_expected *= 2;
3198 if (chunk_size_expected != chunk_size)
3200 ReadUnusedBytesFromFile(file, chunk_size);
3201 return chunk_size_expected;
3204 for (y = 0; y < level->fieldy; y++)
3205 for (x = 0; x < level->fieldx; x++)
3206 level->field[x][y] =
3207 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3212 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3215 int header_size = 4;
3216 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3217 int chunk_size_expected = header_size + content_size;
3219 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3220 stored with 16-bit encoding (and should be twice as big then).
3221 Even worse, playfield data was stored 16-bit when only yamyam content
3222 contained 16-bit elements and vice versa. */
3224 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3225 chunk_size_expected += content_size;
3227 if (chunk_size_expected != chunk_size)
3229 ReadUnusedBytesFromFile(file, chunk_size);
3230 return chunk_size_expected;
3234 level->num_yamyam_contents = getFile8Bit(file);
3238 // correct invalid number of content fields -- should never happen
3239 if (level->num_yamyam_contents < 1 ||
3240 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3241 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3243 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3244 for (y = 0; y < 3; y++)
3245 for (x = 0; x < 3; x++)
3246 level->yamyam_content[i].e[x][y] =
3247 getMappedElement(level->encoding_16bit_field ?
3248 getFile16BitBE(file) : getFile8Bit(file));
3252 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3257 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3259 element = getMappedElement(getFile16BitBE(file));
3260 num_contents = getFile8Bit(file);
3262 getFile8Bit(file); // content x size (unused)
3263 getFile8Bit(file); // content y size (unused)
3265 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3267 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3268 for (y = 0; y < 3; y++)
3269 for (x = 0; x < 3; x++)
3270 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3272 // correct invalid number of content fields -- should never happen
3273 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3274 num_contents = STD_ELEMENT_CONTENTS;
3276 if (element == EL_YAMYAM)
3278 level->num_yamyam_contents = num_contents;
3280 for (i = 0; i < num_contents; i++)
3281 for (y = 0; y < 3; y++)
3282 for (x = 0; x < 3; x++)
3283 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3285 else if (element == EL_BD_AMOEBA)
3287 level->amoeba_content = content_array[0][0][0];
3291 Warn("cannot load content for element '%d'", element);
3297 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3303 int chunk_size_expected;
3305 element = getMappedElement(getFile16BitBE(file));
3306 if (!IS_ENVELOPE(element))
3307 element = EL_ENVELOPE_1;
3309 envelope_nr = element - EL_ENVELOPE_1;
3311 envelope_len = getFile16BitBE(file);
3313 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3314 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3316 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3318 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3319 if (chunk_size_expected != chunk_size)
3321 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3322 return chunk_size_expected;
3325 for (i = 0; i < envelope_len; i++)
3326 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3331 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3333 int num_changed_custom_elements = getFile16BitBE(file);
3334 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3337 if (chunk_size_expected != chunk_size)
3339 ReadUnusedBytesFromFile(file, chunk_size - 2);
3340 return chunk_size_expected;
3343 for (i = 0; i < num_changed_custom_elements; i++)
3345 int element = getMappedElement(getFile16BitBE(file));
3346 int properties = getFile32BitBE(file);
3348 if (IS_CUSTOM_ELEMENT(element))
3349 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3351 Warn("invalid custom element number %d", element);
3353 // older game versions that wrote level files with CUS1 chunks used
3354 // different default push delay values (not yet stored in level file)
3355 element_info[element].push_delay_fixed = 2;
3356 element_info[element].push_delay_random = 8;
3359 level->file_has_custom_elements = TRUE;
3364 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3366 int num_changed_custom_elements = getFile16BitBE(file);
3367 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3370 if (chunk_size_expected != chunk_size)
3372 ReadUnusedBytesFromFile(file, chunk_size - 2);
3373 return chunk_size_expected;
3376 for (i = 0; i < num_changed_custom_elements; i++)
3378 int element = getMappedElement(getFile16BitBE(file));
3379 int custom_target_element = getMappedElement(getFile16BitBE(file));
3381 if (IS_CUSTOM_ELEMENT(element))
3382 element_info[element].change->target_element = custom_target_element;
3384 Warn("invalid custom element number %d", element);
3387 level->file_has_custom_elements = TRUE;
3392 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3394 int num_changed_custom_elements = getFile16BitBE(file);
3395 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3398 if (chunk_size_expected != chunk_size)
3400 ReadUnusedBytesFromFile(file, chunk_size - 2);
3401 return chunk_size_expected;
3404 for (i = 0; i < num_changed_custom_elements; i++)
3406 int element = getMappedElement(getFile16BitBE(file));
3407 struct ElementInfo *ei = &element_info[element];
3408 unsigned int event_bits;
3410 if (!IS_CUSTOM_ELEMENT(element))
3412 Warn("invalid custom element number %d", element);
3414 element = EL_INTERNAL_DUMMY;
3417 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3418 ei->description[j] = getFile8Bit(file);
3419 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3421 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3423 // some free bytes for future properties and padding
3424 ReadUnusedBytesFromFile(file, 7);
3426 ei->use_gfx_element = getFile8Bit(file);
3427 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3429 ei->collect_score_initial = getFile8Bit(file);
3430 ei->collect_count_initial = getFile8Bit(file);
3432 ei->push_delay_fixed = getFile16BitBE(file);
3433 ei->push_delay_random = getFile16BitBE(file);
3434 ei->move_delay_fixed = getFile16BitBE(file);
3435 ei->move_delay_random = getFile16BitBE(file);
3437 ei->move_pattern = getFile16BitBE(file);
3438 ei->move_direction_initial = getFile8Bit(file);
3439 ei->move_stepsize = getFile8Bit(file);
3441 for (y = 0; y < 3; y++)
3442 for (x = 0; x < 3; x++)
3443 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3445 // bits 0 - 31 of "has_event[]"
3446 event_bits = getFile32BitBE(file);
3447 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3448 if (event_bits & (1u << j))
3449 ei->change->has_event[j] = TRUE;
3451 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3453 ei->change->delay_fixed = getFile16BitBE(file);
3454 ei->change->delay_random = getFile16BitBE(file);
3455 ei->change->delay_frames = getFile16BitBE(file);
3457 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3459 ei->change->explode = getFile8Bit(file);
3460 ei->change->use_target_content = getFile8Bit(file);
3461 ei->change->only_if_complete = getFile8Bit(file);
3462 ei->change->use_random_replace = getFile8Bit(file);
3464 ei->change->random_percentage = getFile8Bit(file);
3465 ei->change->replace_when = getFile8Bit(file);
3467 for (y = 0; y < 3; y++)
3468 for (x = 0; x < 3; x++)
3469 ei->change->target_content.e[x][y] =
3470 getMappedElement(getFile16BitBE(file));
3472 ei->slippery_type = getFile8Bit(file);
3474 // some free bytes for future properties and padding
3475 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3477 // mark that this custom element has been modified
3478 ei->modified_settings = TRUE;
3481 level->file_has_custom_elements = TRUE;
3486 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3488 struct ElementInfo *ei;
3489 int chunk_size_expected;
3493 // ---------- custom element base property values (96 bytes) ----------------
3495 element = getMappedElement(getFile16BitBE(file));
3497 if (!IS_CUSTOM_ELEMENT(element))
3499 Warn("invalid custom element number %d", element);
3501 ReadUnusedBytesFromFile(file, chunk_size - 2);
3506 ei = &element_info[element];
3508 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3509 ei->description[i] = getFile8Bit(file);
3510 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3512 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3514 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3516 ei->num_change_pages = getFile8Bit(file);
3518 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3519 if (chunk_size_expected != chunk_size)
3521 ReadUnusedBytesFromFile(file, chunk_size - 43);
3522 return chunk_size_expected;
3525 ei->ce_value_fixed_initial = getFile16BitBE(file);
3526 ei->ce_value_random_initial = getFile16BitBE(file);
3527 ei->use_last_ce_value = getFile8Bit(file);
3529 ei->use_gfx_element = getFile8Bit(file);
3530 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3532 ei->collect_score_initial = getFile8Bit(file);
3533 ei->collect_count_initial = getFile8Bit(file);
3535 ei->drop_delay_fixed = getFile8Bit(file);
3536 ei->push_delay_fixed = getFile8Bit(file);
3537 ei->drop_delay_random = getFile8Bit(file);
3538 ei->push_delay_random = getFile8Bit(file);
3539 ei->move_delay_fixed = getFile16BitBE(file);
3540 ei->move_delay_random = getFile16BitBE(file);
3542 // bits 0 - 15 of "move_pattern" ...
3543 ei->move_pattern = getFile16BitBE(file);
3544 ei->move_direction_initial = getFile8Bit(file);
3545 ei->move_stepsize = getFile8Bit(file);
3547 ei->slippery_type = getFile8Bit(file);
3549 for (y = 0; y < 3; y++)
3550 for (x = 0; x < 3; x++)
3551 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3553 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3554 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3555 ei->move_leave_type = getFile8Bit(file);
3557 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3558 ei->move_pattern |= (getFile16BitBE(file) << 16);
3560 ei->access_direction = getFile8Bit(file);
3562 ei->explosion_delay = getFile8Bit(file);
3563 ei->ignition_delay = getFile8Bit(file);
3564 ei->explosion_type = getFile8Bit(file);
3566 // some free bytes for future custom property values and padding
3567 ReadUnusedBytesFromFile(file, 1);
3569 // ---------- change page property values (48 bytes) ------------------------
3571 setElementChangePages(ei, ei->num_change_pages);
3573 for (i = 0; i < ei->num_change_pages; i++)
3575 struct ElementChangeInfo *change = &ei->change_page[i];
3576 unsigned int event_bits;
3578 // always start with reliable default values
3579 setElementChangeInfoToDefaults(change);
3581 // bits 0 - 31 of "has_event[]" ...
3582 event_bits = getFile32BitBE(file);
3583 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3584 if (event_bits & (1u << j))
3585 change->has_event[j] = TRUE;
3587 change->target_element = getMappedElement(getFile16BitBE(file));
3589 change->delay_fixed = getFile16BitBE(file);
3590 change->delay_random = getFile16BitBE(file);
3591 change->delay_frames = getFile16BitBE(file);
3593 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3595 change->explode = getFile8Bit(file);
3596 change->use_target_content = getFile8Bit(file);
3597 change->only_if_complete = getFile8Bit(file);
3598 change->use_random_replace = getFile8Bit(file);
3600 change->random_percentage = getFile8Bit(file);
3601 change->replace_when = getFile8Bit(file);
3603 for (y = 0; y < 3; y++)
3604 for (x = 0; x < 3; x++)
3605 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3607 change->can_change = getFile8Bit(file);
3609 change->trigger_side = getFile8Bit(file);
3611 change->trigger_player = getFile8Bit(file);
3612 change->trigger_page = getFile8Bit(file);
3614 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3615 CH_PAGE_ANY : (1 << change->trigger_page));
3617 change->has_action = getFile8Bit(file);
3618 change->action_type = getFile8Bit(file);
3619 change->action_mode = getFile8Bit(file);
3620 change->action_arg = getFile16BitBE(file);
3622 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3623 event_bits = getFile8Bit(file);
3624 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3625 if (event_bits & (1u << (j - 32)))
3626 change->has_event[j] = TRUE;
3629 // mark this custom element as modified
3630 ei->modified_settings = TRUE;
3632 level->file_has_custom_elements = TRUE;
3637 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3639 struct ElementInfo *ei;
3640 struct ElementGroupInfo *group;
3644 element = getMappedElement(getFile16BitBE(file));
3646 if (!IS_GROUP_ELEMENT(element))
3648 Warn("invalid group element number %d", element);
3650 ReadUnusedBytesFromFile(file, chunk_size - 2);
3655 ei = &element_info[element];
3657 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3658 ei->description[i] = getFile8Bit(file);
3659 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3661 group = element_info[element].group;
3663 group->num_elements = getFile8Bit(file);
3665 ei->use_gfx_element = getFile8Bit(file);
3666 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3668 group->choice_mode = getFile8Bit(file);
3670 // some free bytes for future values and padding
3671 ReadUnusedBytesFromFile(file, 3);
3673 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3674 group->element[i] = getMappedElement(getFile16BitBE(file));
3676 // mark this group element as modified
3677 element_info[element].modified_settings = TRUE;
3679 level->file_has_custom_elements = TRUE;
3684 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3685 int element, int real_element)
3687 int micro_chunk_size = 0;
3688 int conf_type = getFile8Bit(file);
3689 int byte_mask = conf_type & CONF_MASK_BYTES;
3690 boolean element_found = FALSE;
3693 micro_chunk_size += 1;
3695 if (byte_mask == CONF_MASK_MULTI_BYTES)
3697 int num_bytes = getFile16BitBE(file);
3698 byte *buffer = checked_malloc(num_bytes);
3700 ReadBytesFromFile(file, buffer, num_bytes);
3702 for (i = 0; conf[i].data_type != -1; i++)
3704 if (conf[i].element == element &&
3705 conf[i].conf_type == conf_type)
3707 int data_type = conf[i].data_type;
3708 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3709 int max_num_entities = conf[i].max_num_entities;
3711 if (num_entities > max_num_entities)
3713 Warn("truncating number of entities for element %d from %d to %d",
3714 element, num_entities, max_num_entities);
3716 num_entities = max_num_entities;
3719 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3720 data_type == TYPE_CONTENT_LIST))
3722 // for element and content lists, zero entities are not allowed
3723 Warn("found empty list of entities for element %d", element);
3725 // do not set "num_entities" here to prevent reading behind buffer
3727 *(int *)(conf[i].num_entities) = 1; // at least one is required
3731 *(int *)(conf[i].num_entities) = num_entities;
3734 element_found = TRUE;
3736 if (data_type == TYPE_STRING)
3738 char *string = (char *)(conf[i].value);
3741 for (j = 0; j < max_num_entities; j++)
3742 string[j] = (j < num_entities ? buffer[j] : '\0');
3744 else if (data_type == TYPE_ELEMENT_LIST)
3746 int *element_array = (int *)(conf[i].value);
3749 for (j = 0; j < num_entities; j++)
3751 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3753 else if (data_type == TYPE_CONTENT_LIST)
3755 struct Content *content= (struct Content *)(conf[i].value);
3758 for (c = 0; c < num_entities; c++)
3759 for (y = 0; y < 3; y++)
3760 for (x = 0; x < 3; x++)
3761 content[c].e[x][y] =
3762 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3765 element_found = FALSE;
3771 checked_free(buffer);
3773 micro_chunk_size += 2 + num_bytes;
3775 else // constant size configuration data (1, 2 or 4 bytes)
3777 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3778 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3779 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3781 for (i = 0; conf[i].data_type != -1; i++)
3783 if (conf[i].element == element &&
3784 conf[i].conf_type == conf_type)
3786 int data_type = conf[i].data_type;
3788 if (data_type == TYPE_ELEMENT)
3789 value = getMappedElement(value);
3791 if (data_type == TYPE_BOOLEAN)
3792 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3794 *(int *) (conf[i].value) = value;
3796 element_found = TRUE;
3802 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3807 char *error_conf_chunk_bytes =
3808 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3809 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3810 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3811 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3812 int error_element = real_element;
3814 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3815 error_conf_chunk_bytes, error_conf_chunk_token,
3816 error_element, EL_NAME(error_element));
3819 return micro_chunk_size;
3822 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3824 int real_chunk_size = 0;
3826 li = *level; // copy level data into temporary buffer
3828 while (!checkEndOfFile(file))
3830 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3832 if (real_chunk_size >= chunk_size)
3836 *level = li; // copy temporary buffer back to level data
3838 return real_chunk_size;
3841 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3843 int real_chunk_size = 0;
3845 li = *level; // copy level data into temporary buffer
3847 while (!checkEndOfFile(file))
3849 int element = getMappedElement(getFile16BitBE(file));
3851 real_chunk_size += 2;
3852 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3854 if (real_chunk_size >= chunk_size)
3858 *level = li; // copy temporary buffer back to level data
3860 return real_chunk_size;
3863 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3865 int real_chunk_size = 0;
3867 li = *level; // copy level data into temporary buffer
3869 while (!checkEndOfFile(file))
3871 int element = getMappedElement(getFile16BitBE(file));
3873 real_chunk_size += 2;
3874 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3876 if (real_chunk_size >= chunk_size)
3880 *level = li; // copy temporary buffer back to level data
3882 return real_chunk_size;
3885 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3887 int element = getMappedElement(getFile16BitBE(file));
3888 int envelope_nr = element - EL_ENVELOPE_1;
3889 int real_chunk_size = 2;
3891 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3893 while (!checkEndOfFile(file))
3895 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3898 if (real_chunk_size >= chunk_size)
3902 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3904 return real_chunk_size;
3907 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3909 int element = getMappedElement(getFile16BitBE(file));
3910 int real_chunk_size = 2;
3911 struct ElementInfo *ei = &element_info[element];
3914 xx_ei = *ei; // copy element data into temporary buffer
3916 xx_ei.num_change_pages = -1;
3918 while (!checkEndOfFile(file))
3920 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3922 if (xx_ei.num_change_pages != -1)
3925 if (real_chunk_size >= chunk_size)
3931 if (ei->num_change_pages == -1)
3933 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3936 ei->num_change_pages = 1;
3938 setElementChangePages(ei, 1);
3939 setElementChangeInfoToDefaults(ei->change);
3941 return real_chunk_size;
3944 // initialize number of change pages stored for this custom element
3945 setElementChangePages(ei, ei->num_change_pages);
3946 for (i = 0; i < ei->num_change_pages; i++)
3947 setElementChangeInfoToDefaults(&ei->change_page[i]);
3949 // start with reading properties for the first change page
3950 xx_current_change_page = 0;
3952 while (!checkEndOfFile(file))
3954 // level file might contain invalid change page number
3955 if (xx_current_change_page >= ei->num_change_pages)
3958 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3960 xx_change = *change; // copy change data into temporary buffer
3962 resetEventBits(); // reset bits; change page might have changed
3964 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3967 *change = xx_change;
3969 setEventFlagsFromEventBits(change);
3971 if (real_chunk_size >= chunk_size)
3975 level->file_has_custom_elements = TRUE;
3977 return real_chunk_size;
3980 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3982 int element = getMappedElement(getFile16BitBE(file));
3983 int real_chunk_size = 2;
3984 struct ElementInfo *ei = &element_info[element];
3985 struct ElementGroupInfo *group = ei->group;
3990 xx_ei = *ei; // copy element data into temporary buffer
3991 xx_group = *group; // copy group data into temporary buffer
3993 while (!checkEndOfFile(file))
3995 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3998 if (real_chunk_size >= chunk_size)
4005 level->file_has_custom_elements = TRUE;
4007 return real_chunk_size;
4010 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4012 int element = getMappedElement(getFile16BitBE(file));
4013 int real_chunk_size = 2;
4014 struct ElementInfo *ei = &element_info[element];
4016 xx_ei = *ei; // copy element data into temporary buffer
4018 while (!checkEndOfFile(file))
4020 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4023 if (real_chunk_size >= chunk_size)
4029 level->file_has_custom_elements = TRUE;
4031 return real_chunk_size;
4034 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4035 struct LevelFileInfo *level_file_info,
4036 boolean level_info_only)
4038 char *filename = level_file_info->filename;
4039 char cookie[MAX_LINE_LEN];
4040 char chunk_name[CHUNK_ID_LEN + 1];
4044 if (!(file = openFile(filename, MODE_READ)))
4046 level->no_valid_file = TRUE;
4047 level->no_level_file = TRUE;
4049 if (level_info_only)
4052 Warn("cannot read level '%s' -- using empty level", filename);
4054 if (!setup.editor.use_template_for_new_levels)
4057 // if level file not found, try to initialize level data from template
4058 filename = getGlobalLevelTemplateFilename();
4060 if (!(file = openFile(filename, MODE_READ)))
4063 // default: for empty levels, use level template for custom elements
4064 level->use_custom_template = TRUE;
4066 level->no_valid_file = FALSE;
4069 getFileChunkBE(file, chunk_name, NULL);
4070 if (strEqual(chunk_name, "RND1"))
4072 getFile32BitBE(file); // not used
4074 getFileChunkBE(file, chunk_name, NULL);
4075 if (!strEqual(chunk_name, "CAVE"))
4077 level->no_valid_file = TRUE;
4079 Warn("unknown format of level file '%s'", filename);
4086 else // check for pre-2.0 file format with cookie string
4088 strcpy(cookie, chunk_name);
4089 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4091 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4092 cookie[strlen(cookie) - 1] = '\0';
4094 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4096 level->no_valid_file = TRUE;
4098 Warn("unknown format of level file '%s'", filename);
4105 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4107 level->no_valid_file = TRUE;
4109 Warn("unsupported version of level file '%s'", filename);
4116 // pre-2.0 level files have no game version, so use file version here
4117 level->game_version = level->file_version;
4120 if (level->file_version < FILE_VERSION_1_2)
4122 // level files from versions before 1.2.0 without chunk structure
4123 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4124 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4132 int (*loader)(File *, int, struct LevelInfo *);
4136 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4137 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4138 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4139 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4140 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4141 { "INFO", -1, LoadLevel_INFO },
4142 { "BODY", -1, LoadLevel_BODY },
4143 { "CONT", -1, LoadLevel_CONT },
4144 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4145 { "CNT3", -1, LoadLevel_CNT3 },
4146 { "CUS1", -1, LoadLevel_CUS1 },
4147 { "CUS2", -1, LoadLevel_CUS2 },
4148 { "CUS3", -1, LoadLevel_CUS3 },
4149 { "CUS4", -1, LoadLevel_CUS4 },
4150 { "GRP1", -1, LoadLevel_GRP1 },
4151 { "CONF", -1, LoadLevel_CONF },
4152 { "ELEM", -1, LoadLevel_ELEM },
4153 { "NOTE", -1, LoadLevel_NOTE },
4154 { "CUSX", -1, LoadLevel_CUSX },
4155 { "GRPX", -1, LoadLevel_GRPX },
4156 { "EMPX", -1, LoadLevel_EMPX },
4161 while (getFileChunkBE(file, chunk_name, &chunk_size))
4165 while (chunk_info[i].name != NULL &&
4166 !strEqual(chunk_name, chunk_info[i].name))
4169 if (chunk_info[i].name == NULL)
4171 Warn("unknown chunk '%s' in level file '%s'",
4172 chunk_name, filename);
4174 ReadUnusedBytesFromFile(file, chunk_size);
4176 else if (chunk_info[i].size != -1 &&
4177 chunk_info[i].size != chunk_size)
4179 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4180 chunk_size, chunk_name, filename);
4182 ReadUnusedBytesFromFile(file, chunk_size);
4186 // call function to load this level chunk
4187 int chunk_size_expected =
4188 (chunk_info[i].loader)(file, chunk_size, level);
4190 if (chunk_size_expected < 0)
4192 Warn("error reading chunk '%s' in level file '%s'",
4193 chunk_name, filename);
4198 // the size of some chunks cannot be checked before reading other
4199 // chunks first (like "HEAD" and "BODY") that contain some header
4200 // information, so check them here
4201 if (chunk_size_expected != chunk_size)
4203 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4204 chunk_size, chunk_name, filename);
4216 // ----------------------------------------------------------------------------
4217 // functions for loading BD level
4218 // ----------------------------------------------------------------------------
4220 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4221 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4223 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4225 struct LevelInfo_BD *level_bd = level->native_bd_level;
4226 GdCave *cave = NULL; // will be changed below
4227 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4228 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4231 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4233 // cave and map newly allocated when set to defaults above
4234 cave = level_bd->cave;
4237 cave->intermission = level->bd_intermission;
4240 cave->level_time[0] = level->time;
4241 cave->level_diamonds[0] = level->gems_needed;
4244 cave->scheduling = level->bd_scheduling_type;
4245 cave->pal_timing = level->bd_pal_timing;
4246 cave->level_speed[0] = level->bd_cycle_delay_ms;
4247 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4248 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4249 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4252 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4253 cave->diamond_value = level->score[SC_EMERALD];
4254 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4256 // compatibility settings
4257 cave->lineshift = level->bd_line_shifting_borders;
4258 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4259 cave->short_explosions = level->bd_short_explosions;
4261 // player properties
4262 cave->diagonal_movements = level->bd_diagonal_movements;
4263 cave->active_is_first_found = level->bd_topmost_player_active;
4264 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4265 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4266 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4267 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4269 // element properties
4270 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4271 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4272 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4273 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4274 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4275 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4276 cave->level_magic_wall_time[0] = level->time_magic_wall;
4277 cave->magic_timer_zero_is_infinite = level->bd_magic_wall_zero_infinite;
4278 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4279 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4280 cave->magic_wall_breakscan = level->bd_magic_wall_break_scan;
4282 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4283 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4284 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4285 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4286 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4287 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4288 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4290 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4291 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4292 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4293 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4294 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4295 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4296 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4297 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4298 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4299 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4300 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4302 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4303 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4304 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4305 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4306 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4307 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4309 cave->slime_predictable = level->bd_slime_is_predictable;
4310 cave->slime_correct_random = level->bd_slime_correct_random;
4311 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4312 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4313 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4314 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4315 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4316 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4317 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4318 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4319 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4320 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4322 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4323 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4324 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4326 cave->biter_delay_frame = level->bd_biter_move_delay;
4327 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4329 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4331 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4333 cave->replicators_active = level->bd_replicators_active;
4334 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4336 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4337 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4339 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4341 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4343 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4344 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4345 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4347 cave->infinite_rockets = level->bd_infinite_rockets;
4349 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4350 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4352 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4353 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4355 cave->creatures_backwards = level->bd_creatures_start_backwards;
4356 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4357 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4359 cave->gravity = level->bd_gravity_direction;
4360 cave->gravity_switch_active = level->bd_gravity_switch_active;
4361 cave->gravity_change_time = level->bd_gravity_switch_delay;
4362 cave->gravity_affects_all = level->bd_gravity_affects_all;
4364 cave->stone_falling_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4365 cave->stone_bouncing_effect = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4366 cave->diamond_falling_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4367 cave->diamond_bouncing_effect = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4369 cave->firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4370 cave->alt_firefly_explode_to = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4371 cave->butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4372 cave->alt_butterfly_explode_to = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4373 cave->stonefly_explode_to = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4374 cave->dragonfly_explode_to = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4376 cave->diamond_birth_effect = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4377 cave->bomb_explosion_effect = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4378 cave->nitro_explosion_effect = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4379 cave->explosion_effect = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4382 strncpy(cave->name, level->name, sizeof(GdString));
4383 cave->name[sizeof(GdString) - 1] = '\0';
4385 // playfield elements
4386 for (x = 0; x < cave->w; x++)
4387 for (y = 0; y < cave->h; y++)
4388 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4391 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4393 struct LevelInfo_BD *level_bd = level->native_bd_level;
4394 GdCave *cave = level_bd->cave;
4395 int bd_level_nr = level_bd->level_nr;
4398 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4399 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4402 level->bd_intermission = cave->intermission;
4405 level->time = cave->level_time[bd_level_nr];
4406 level->gems_needed = cave->level_diamonds[bd_level_nr];
4409 level->bd_scheduling_type = cave->scheduling;
4410 level->bd_pal_timing = cave->pal_timing;
4411 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4412 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4413 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4414 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4417 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4418 level->score[SC_EMERALD] = cave->diamond_value;
4419 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4421 // compatibility settings
4422 level->bd_line_shifting_borders = cave->lineshift;
4423 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4424 level->bd_short_explosions = cave->short_explosions;
4426 // player properties
4427 level->bd_diagonal_movements = cave->diagonal_movements;
4428 level->bd_topmost_player_active = cave->active_is_first_found;
4429 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4430 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4431 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4432 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4434 // element properties
4435 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4436 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4437 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4438 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4439 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4440 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4441 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4442 level->bd_magic_wall_zero_infinite = cave->magic_timer_zero_is_infinite;
4443 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4444 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4445 level->bd_magic_wall_break_scan = cave->magic_wall_breakscan;
4447 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4448 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4449 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4450 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4451 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4452 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4453 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4455 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4456 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4457 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4458 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4459 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4460 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4461 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4462 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4463 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4464 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4465 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4467 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4468 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4469 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4470 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4471 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4472 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4474 level->bd_slime_is_predictable = cave->slime_predictable;
4475 level->bd_slime_correct_random = cave->slime_correct_random;
4476 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4477 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4478 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4479 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4480 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4481 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4482 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4483 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4484 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4485 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4487 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4488 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4489 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4491 level->bd_biter_move_delay = cave->biter_delay_frame;
4492 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4494 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4496 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4498 level->bd_replicators_active = cave->replicators_active;
4499 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4501 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4502 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4504 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4506 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4508 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4509 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4510 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4512 level->bd_infinite_rockets = cave->infinite_rockets;
4514 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4515 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4517 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4518 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4520 level->bd_creatures_start_backwards = cave->creatures_backwards;
4521 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4522 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4524 level->bd_gravity_direction = cave->gravity;
4525 level->bd_gravity_switch_active = cave->gravity_switch_active;
4526 level->bd_gravity_switch_delay = cave->gravity_change_time;
4527 level->bd_gravity_affects_all = cave->gravity_affects_all;
4529 level->bd_rock_turns_to_on_falling = CAVE_TO_LEVEL(cave->stone_falling_effect);
4530 level->bd_rock_turns_to_on_impact = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4531 level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4532 level->bd_diamond_turns_to_on_impact = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4534 level->bd_firefly_explodes_to = CAVE_TO_LEVEL(cave->firefly_explode_to);
4535 level->bd_firefly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4536 level->bd_butterfly_explodes_to = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4537 level->bd_butterfly_2_explodes_to = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4538 level->bd_stonefly_explodes_to = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4539 level->bd_dragonfly_explodes_to = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4541 level->bd_diamond_birth_turns_to = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4542 level->bd_bomb_explosion_turns_to = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4543 level->bd_nitro_explosion_turns_to = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4544 level->bd_explosion_turns_to = CAVE_TO_LEVEL(cave->explosion_effect);
4547 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4549 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4550 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4552 // playfield elements
4553 for (x = 0; x < level->fieldx; x++)
4554 for (y = 0; y < level->fieldy; y++)
4555 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4557 checked_free(cave_name);
4560 static void setTapeInfoToDefaults(void);
4562 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4564 struct LevelInfo_BD *level_bd = level->native_bd_level;
4565 GdCave *cave = level_bd->cave;
4566 GdReplay *replay = level_bd->replay;
4572 // always start with reliable default values
4573 setTapeInfoToDefaults();
4575 tape.level_nr = level_nr; // (currently not used)
4576 tape.random_seed = replay->seed;
4578 TapeSetDateFromIsoDateString(replay->date);
4581 tape.pos[tape.counter].delay = 0;
4583 tape.bd_replay = TRUE;
4585 // all time calculations only used to display approximate tape time
4586 int cave_speed = cave->speed;
4587 int milliseconds_game = 0;
4588 int milliseconds_elapsed = 20;
4590 for (i = 0; i < replay->movements->len; i++)
4592 int replay_action = replay->movements->data[i];
4593 int tape_action = map_action_BD_to_RND(replay_action);
4594 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4595 boolean success = 0;
4599 success = TapeAddAction(action);
4601 milliseconds_game += milliseconds_elapsed;
4603 if (milliseconds_game >= cave_speed)
4605 milliseconds_game -= cave_speed;
4612 tape.pos[tape.counter].delay = 0;
4613 tape.pos[tape.counter].action[0] = 0;
4617 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4623 TapeHaltRecording();
4625 if (!replay->success)
4626 Warn("BD replay is marked as not successful");
4630 // ----------------------------------------------------------------------------
4631 // functions for loading EM level
4632 // ----------------------------------------------------------------------------
4634 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4636 static int ball_xy[8][2] =
4647 struct LevelInfo_EM *level_em = level->native_em_level;
4648 struct CAVE *cav = level_em->cav;
4651 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4652 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4654 cav->time_seconds = level->time;
4655 cav->gems_needed = level->gems_needed;
4657 cav->emerald_score = level->score[SC_EMERALD];
4658 cav->diamond_score = level->score[SC_DIAMOND];
4659 cav->alien_score = level->score[SC_ROBOT];
4660 cav->tank_score = level->score[SC_SPACESHIP];
4661 cav->bug_score = level->score[SC_BUG];
4662 cav->eater_score = level->score[SC_YAMYAM];
4663 cav->nut_score = level->score[SC_NUT];
4664 cav->dynamite_score = level->score[SC_DYNAMITE];
4665 cav->key_score = level->score[SC_KEY];
4666 cav->exit_score = level->score[SC_TIME_BONUS];
4668 cav->num_eater_arrays = level->num_yamyam_contents;
4670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4671 for (y = 0; y < 3; y++)
4672 for (x = 0; x < 3; x++)
4673 cav->eater_array[i][y * 3 + x] =
4674 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4676 cav->amoeba_time = level->amoeba_speed;
4677 cav->wonderwall_time = level->time_magic_wall;
4678 cav->wheel_time = level->time_wheel;
4680 cav->android_move_time = level->android_move_time;
4681 cav->android_clone_time = level->android_clone_time;
4682 cav->ball_random = level->ball_random;
4683 cav->ball_active = level->ball_active_initial;
4684 cav->ball_time = level->ball_time;
4685 cav->num_ball_arrays = level->num_ball_contents;
4687 cav->lenses_score = level->lenses_score;
4688 cav->magnify_score = level->magnify_score;
4689 cav->slurp_score = level->slurp_score;
4691 cav->lenses_time = level->lenses_time;
4692 cav->magnify_time = level->magnify_time;
4694 cav->wind_time = 9999;
4695 cav->wind_direction =
4696 map_direction_RND_to_EM(level->wind_direction_initial);
4698 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4699 for (j = 0; j < 8; j++)
4700 cav->ball_array[i][j] =
4701 map_element_RND_to_EM_cave(level->ball_content[i].
4702 e[ball_xy[j][0]][ball_xy[j][1]]);
4704 map_android_clone_elements_RND_to_EM(level);
4706 // first fill the complete playfield with the empty space element
4707 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4708 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4709 cav->cave[x][y] = Cblank;
4711 // then copy the real level contents from level file into the playfield
4712 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4714 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4716 if (level->field[x][y] == EL_AMOEBA_DEAD)
4717 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4719 cav->cave[x][y] = new_element;
4722 for (i = 0; i < MAX_PLAYERS; i++)
4724 cav->player_x[i] = -1;
4725 cav->player_y[i] = -1;
4728 // initialize player positions and delete players from the playfield
4729 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4731 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4733 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4735 cav->player_x[player_nr] = x;
4736 cav->player_y[player_nr] = y;
4738 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4743 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4745 static int ball_xy[8][2] =
4756 struct LevelInfo_EM *level_em = level->native_em_level;
4757 struct CAVE *cav = level_em->cav;
4760 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4761 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4763 level->time = cav->time_seconds;
4764 level->gems_needed = cav->gems_needed;
4766 sprintf(level->name, "Level %d", level->file_info.nr);
4768 level->score[SC_EMERALD] = cav->emerald_score;
4769 level->score[SC_DIAMOND] = cav->diamond_score;
4770 level->score[SC_ROBOT] = cav->alien_score;
4771 level->score[SC_SPACESHIP] = cav->tank_score;
4772 level->score[SC_BUG] = cav->bug_score;
4773 level->score[SC_YAMYAM] = cav->eater_score;
4774 level->score[SC_NUT] = cav->nut_score;
4775 level->score[SC_DYNAMITE] = cav->dynamite_score;
4776 level->score[SC_KEY] = cav->key_score;
4777 level->score[SC_TIME_BONUS] = cav->exit_score;
4779 level->num_yamyam_contents = cav->num_eater_arrays;
4781 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4782 for (y = 0; y < 3; y++)
4783 for (x = 0; x < 3; x++)
4784 level->yamyam_content[i].e[x][y] =
4785 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4787 level->amoeba_speed = cav->amoeba_time;
4788 level->time_magic_wall = cav->wonderwall_time;
4789 level->time_wheel = cav->wheel_time;
4791 level->android_move_time = cav->android_move_time;
4792 level->android_clone_time = cav->android_clone_time;
4793 level->ball_random = cav->ball_random;
4794 level->ball_active_initial = cav->ball_active;
4795 level->ball_time = cav->ball_time;
4796 level->num_ball_contents = cav->num_ball_arrays;
4798 level->lenses_score = cav->lenses_score;
4799 level->magnify_score = cav->magnify_score;
4800 level->slurp_score = cav->slurp_score;
4802 level->lenses_time = cav->lenses_time;
4803 level->magnify_time = cav->magnify_time;
4805 level->wind_direction_initial =
4806 map_direction_EM_to_RND(cav->wind_direction);
4808 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4809 for (j = 0; j < 8; j++)
4810 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4811 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4813 map_android_clone_elements_EM_to_RND(level);
4815 // convert the playfield (some elements need special treatment)
4816 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4818 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4820 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4821 new_element = EL_AMOEBA_DEAD;
4823 level->field[x][y] = new_element;
4826 for (i = 0; i < MAX_PLAYERS; i++)
4828 // in case of all players set to the same field, use the first player
4829 int nr = MAX_PLAYERS - i - 1;
4830 int jx = cav->player_x[nr];
4831 int jy = cav->player_y[nr];
4833 if (jx != -1 && jy != -1)
4834 level->field[jx][jy] = EL_PLAYER_1 + nr;
4837 // time score is counted for each 10 seconds left in Emerald Mine levels
4838 level->time_score_base = 10;
4842 // ----------------------------------------------------------------------------
4843 // functions for loading SP level
4844 // ----------------------------------------------------------------------------
4846 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4848 struct LevelInfo_SP *level_sp = level->native_sp_level;
4849 LevelInfoType *header = &level_sp->header;
4852 level_sp->width = level->fieldx;
4853 level_sp->height = level->fieldy;
4855 for (x = 0; x < level->fieldx; x++)
4856 for (y = 0; y < level->fieldy; y++)
4857 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4859 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4861 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4862 header->LevelTitle[i] = level->name[i];
4863 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4865 header->InfotronsNeeded = level->gems_needed;
4867 header->SpecialPortCount = 0;
4869 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4871 boolean gravity_port_found = FALSE;
4872 boolean gravity_port_valid = FALSE;
4873 int gravity_port_flag;
4874 int gravity_port_base_element;
4875 int element = level->field[x][y];
4877 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4878 element <= EL_SP_GRAVITY_ON_PORT_UP)
4880 gravity_port_found = TRUE;
4881 gravity_port_valid = TRUE;
4882 gravity_port_flag = 1;
4883 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4885 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4886 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4888 gravity_port_found = TRUE;
4889 gravity_port_valid = TRUE;
4890 gravity_port_flag = 0;
4891 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4893 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4894 element <= EL_SP_GRAVITY_PORT_UP)
4896 // change R'n'D style gravity inverting special port to normal port
4897 // (there are no gravity inverting ports in native Supaplex engine)
4899 gravity_port_found = TRUE;
4900 gravity_port_valid = FALSE;
4901 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4904 if (gravity_port_found)
4906 if (gravity_port_valid &&
4907 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4909 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4911 port->PortLocation = (y * level->fieldx + x) * 2;
4912 port->Gravity = gravity_port_flag;
4914 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4916 header->SpecialPortCount++;
4920 // change special gravity port to normal port
4922 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4925 level_sp->playfield[x][y] = element - EL_SP_START;
4930 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4932 struct LevelInfo_SP *level_sp = level->native_sp_level;
4933 LevelInfoType *header = &level_sp->header;
4934 boolean num_invalid_elements = 0;
4937 level->fieldx = level_sp->width;
4938 level->fieldy = level_sp->height;
4940 for (x = 0; x < level->fieldx; x++)
4942 for (y = 0; y < level->fieldy; y++)
4944 int element_old = level_sp->playfield[x][y];
4945 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4947 if (element_new == EL_UNKNOWN)
4949 num_invalid_elements++;
4951 Debug("level:native:SP", "invalid element %d at position %d, %d",
4955 level->field[x][y] = element_new;
4959 if (num_invalid_elements > 0)
4960 Warn("found %d invalid elements%s", num_invalid_elements,
4961 (!options.debug ? " (use '--debug' for more details)" : ""));
4963 for (i = 0; i < MAX_PLAYERS; i++)
4964 level->initial_player_gravity[i] =
4965 (header->InitialGravity == 1 ? TRUE : FALSE);
4967 // skip leading spaces
4968 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4969 if (header->LevelTitle[i] != ' ')
4973 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4974 level->name[j] = header->LevelTitle[i];
4975 level->name[j] = '\0';
4977 // cut trailing spaces
4979 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4980 level->name[j - 1] = '\0';
4982 level->gems_needed = header->InfotronsNeeded;
4984 for (i = 0; i < header->SpecialPortCount; i++)
4986 SpecialPortType *port = &header->SpecialPort[i];
4987 int port_location = port->PortLocation;
4988 int gravity = port->Gravity;
4989 int port_x, port_y, port_element;
4991 port_x = (port_location / 2) % level->fieldx;
4992 port_y = (port_location / 2) / level->fieldx;
4994 if (port_x < 0 || port_x >= level->fieldx ||
4995 port_y < 0 || port_y >= level->fieldy)
4997 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5002 port_element = level->field[port_x][port_y];
5004 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5005 port_element > EL_SP_GRAVITY_PORT_UP)
5007 Warn("no special port at position (%d, %d)", port_x, port_y);
5012 // change previous (wrong) gravity inverting special port to either
5013 // gravity enabling special port or gravity disabling special port
5014 level->field[port_x][port_y] +=
5015 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5016 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5019 // change special gravity ports without database entries to normal ports
5020 for (x = 0; x < level->fieldx; x++)
5021 for (y = 0; y < level->fieldy; y++)
5022 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5023 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5024 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5026 level->time = 0; // no time limit
5027 level->amoeba_speed = 0;
5028 level->time_magic_wall = 0;
5029 level->time_wheel = 0;
5030 level->amoeba_content = EL_EMPTY;
5032 // original Supaplex does not use score values -- rate by playing time
5033 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5034 level->score[i] = 0;
5036 level->rate_time_over_score = TRUE;
5038 // there are no yamyams in supaplex levels
5039 for (i = 0; i < level->num_yamyam_contents; i++)
5040 for (x = 0; x < 3; x++)
5041 for (y = 0; y < 3; y++)
5042 level->yamyam_content[i].e[x][y] = EL_EMPTY;
5045 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5047 struct LevelInfo_SP *level_sp = level->native_sp_level;
5048 struct DemoInfo_SP *demo = &level_sp->demo;
5051 // always start with reliable default values
5052 demo->is_available = FALSE;
5055 if (TAPE_IS_EMPTY(tape))
5058 demo->level_nr = tape.level_nr; // (currently not used)
5060 level_sp->header.DemoRandomSeed = tape.random_seed;
5064 for (i = 0; i < tape.length; i++)
5066 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5067 int demo_repeat = tape.pos[i].delay;
5068 int demo_entries = (demo_repeat + 15) / 16;
5070 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5072 Warn("tape truncated: size exceeds maximum SP demo size %d",
5078 for (j = 0; j < demo_repeat / 16; j++)
5079 demo->data[demo->length++] = 0xf0 | demo_action;
5081 if (demo_repeat % 16)
5082 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5085 demo->is_available = TRUE;
5088 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5090 struct LevelInfo_SP *level_sp = level->native_sp_level;
5091 struct DemoInfo_SP *demo = &level_sp->demo;
5092 char *filename = level->file_info.filename;
5095 // always start with reliable default values
5096 setTapeInfoToDefaults();
5098 if (!demo->is_available)
5101 tape.level_nr = demo->level_nr; // (currently not used)
5102 tape.random_seed = level_sp->header.DemoRandomSeed;
5104 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5107 tape.pos[tape.counter].delay = 0;
5109 for (i = 0; i < demo->length; i++)
5111 int demo_action = demo->data[i] & 0x0f;
5112 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5113 int tape_action = map_key_SP_to_RND(demo_action);
5114 int tape_repeat = demo_repeat + 1;
5115 byte action[MAX_TAPE_ACTIONS] = { tape_action };
5116 boolean success = 0;
5119 for (j = 0; j < tape_repeat; j++)
5120 success = TapeAddAction(action);
5124 Warn("SP demo truncated: size exceeds maximum tape size %d",
5131 TapeHaltRecording();
5135 // ----------------------------------------------------------------------------
5136 // functions for loading MM level
5137 // ----------------------------------------------------------------------------
5139 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5141 struct LevelInfo_MM *level_mm = level->native_mm_level;
5144 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5145 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5147 level_mm->time = level->time;
5148 level_mm->kettles_needed = level->gems_needed;
5149 level_mm->auto_count_kettles = level->auto_count_gems;
5151 level_mm->mm_laser_red = level->mm_laser_red;
5152 level_mm->mm_laser_green = level->mm_laser_green;
5153 level_mm->mm_laser_blue = level->mm_laser_blue;
5155 level_mm->df_laser_red = level->df_laser_red;
5156 level_mm->df_laser_green = level->df_laser_green;
5157 level_mm->df_laser_blue = level->df_laser_blue;
5159 strcpy(level_mm->name, level->name);
5160 strcpy(level_mm->author, level->author);
5162 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5163 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5164 level_mm->score[SC_KEY] = level->score[SC_KEY];
5165 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5166 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5168 level_mm->amoeba_speed = level->amoeba_speed;
5169 level_mm->time_fuse = level->mm_time_fuse;
5170 level_mm->time_bomb = level->mm_time_bomb;
5171 level_mm->time_ball = level->mm_time_ball;
5172 level_mm->time_block = level->mm_time_block;
5174 level_mm->num_ball_contents = level->num_mm_ball_contents;
5175 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5176 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5177 level_mm->explode_ball = level->explode_mm_ball;
5179 for (i = 0; i < level->num_mm_ball_contents; i++)
5180 level_mm->ball_content[i] =
5181 map_element_RND_to_MM(level->mm_ball_content[i]);
5183 for (x = 0; x < level->fieldx; x++)
5184 for (y = 0; y < level->fieldy; y++)
5186 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5189 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5191 struct LevelInfo_MM *level_mm = level->native_mm_level;
5194 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5195 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5197 level->time = level_mm->time;
5198 level->gems_needed = level_mm->kettles_needed;
5199 level->auto_count_gems = level_mm->auto_count_kettles;
5201 level->mm_laser_red = level_mm->mm_laser_red;
5202 level->mm_laser_green = level_mm->mm_laser_green;
5203 level->mm_laser_blue = level_mm->mm_laser_blue;
5205 level->df_laser_red = level_mm->df_laser_red;
5206 level->df_laser_green = level_mm->df_laser_green;
5207 level->df_laser_blue = level_mm->df_laser_blue;
5209 strcpy(level->name, level_mm->name);
5211 // only overwrite author from 'levelinfo.conf' if author defined in level
5212 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5213 strcpy(level->author, level_mm->author);
5215 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5216 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5217 level->score[SC_KEY] = level_mm->score[SC_KEY];
5218 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5219 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5221 level->amoeba_speed = level_mm->amoeba_speed;
5222 level->mm_time_fuse = level_mm->time_fuse;
5223 level->mm_time_bomb = level_mm->time_bomb;
5224 level->mm_time_ball = level_mm->time_ball;
5225 level->mm_time_block = level_mm->time_block;
5227 level->num_mm_ball_contents = level_mm->num_ball_contents;
5228 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5229 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5230 level->explode_mm_ball = level_mm->explode_ball;
5232 for (i = 0; i < level->num_mm_ball_contents; i++)
5233 level->mm_ball_content[i] =
5234 map_element_MM_to_RND(level_mm->ball_content[i]);
5236 for (x = 0; x < level->fieldx; x++)
5237 for (y = 0; y < level->fieldy; y++)
5238 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5242 // ----------------------------------------------------------------------------
5243 // functions for loading DC level
5244 // ----------------------------------------------------------------------------
5246 #define DC_LEVEL_HEADER_SIZE 344
5248 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5251 static int last_data_encoded;
5255 int diff_hi, diff_lo;
5256 int data_hi, data_lo;
5257 unsigned short data_decoded;
5261 last_data_encoded = 0;
5268 diff = data_encoded - last_data_encoded;
5269 diff_hi = diff & ~0xff;
5270 diff_lo = diff & 0xff;
5274 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5275 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5276 data_hi = data_hi & 0xff00;
5278 data_decoded = data_hi | data_lo;
5280 last_data_encoded = data_encoded;
5282 offset1 = (offset1 + 1) % 31;
5283 offset2 = offset2 & 0xff;
5285 return data_decoded;
5288 static int getMappedElement_DC(int element)
5296 // 0x0117 - 0x036e: (?)
5299 // 0x042d - 0x0684: (?)
5315 element = EL_CRYSTAL;
5318 case 0x0e77: // quicksand (boulder)
5319 element = EL_QUICKSAND_FAST_FULL;
5322 case 0x0e99: // slow quicksand (boulder)
5323 element = EL_QUICKSAND_FULL;
5327 element = EL_EM_EXIT_OPEN;
5331 element = EL_EM_EXIT_CLOSED;
5335 element = EL_EM_STEEL_EXIT_OPEN;
5339 element = EL_EM_STEEL_EXIT_CLOSED;
5342 case 0x0f4f: // dynamite (lit 1)
5343 element = EL_EM_DYNAMITE_ACTIVE;
5346 case 0x0f57: // dynamite (lit 2)
5347 element = EL_EM_DYNAMITE_ACTIVE;
5350 case 0x0f5f: // dynamite (lit 3)
5351 element = EL_EM_DYNAMITE_ACTIVE;
5354 case 0x0f67: // dynamite (lit 4)
5355 element = EL_EM_DYNAMITE_ACTIVE;
5362 element = EL_AMOEBA_WET;
5366 element = EL_AMOEBA_DROP;
5370 element = EL_DC_MAGIC_WALL;
5374 element = EL_SPACESHIP_UP;
5378 element = EL_SPACESHIP_DOWN;
5382 element = EL_SPACESHIP_LEFT;
5386 element = EL_SPACESHIP_RIGHT;
5390 element = EL_BUG_UP;
5394 element = EL_BUG_DOWN;
5398 element = EL_BUG_LEFT;
5402 element = EL_BUG_RIGHT;
5406 element = EL_MOLE_UP;
5410 element = EL_MOLE_DOWN;
5414 element = EL_MOLE_LEFT;
5418 element = EL_MOLE_RIGHT;
5426 element = EL_YAMYAM_UP;
5430 element = EL_SWITCHGATE_OPEN;
5434 element = EL_SWITCHGATE_CLOSED;
5438 element = EL_DC_SWITCHGATE_SWITCH_UP;
5442 element = EL_TIMEGATE_CLOSED;
5445 case 0x144c: // conveyor belt switch (green)
5446 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5449 case 0x144f: // conveyor belt switch (red)
5450 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5453 case 0x1452: // conveyor belt switch (blue)
5454 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5458 element = EL_CONVEYOR_BELT_3_MIDDLE;
5462 element = EL_CONVEYOR_BELT_3_LEFT;
5466 element = EL_CONVEYOR_BELT_3_RIGHT;
5470 element = EL_CONVEYOR_BELT_1_MIDDLE;
5474 element = EL_CONVEYOR_BELT_1_LEFT;
5478 element = EL_CONVEYOR_BELT_1_RIGHT;
5482 element = EL_CONVEYOR_BELT_4_MIDDLE;
5486 element = EL_CONVEYOR_BELT_4_LEFT;
5490 element = EL_CONVEYOR_BELT_4_RIGHT;
5494 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5498 element = EL_EXPANDABLE_WALL_VERTICAL;
5502 element = EL_EXPANDABLE_WALL_ANY;
5505 case 0x14ce: // growing steel wall (left/right)
5506 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5509 case 0x14df: // growing steel wall (up/down)
5510 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5513 case 0x14e8: // growing steel wall (up/down/left/right)
5514 element = EL_EXPANDABLE_STEELWALL_ANY;
5518 element = EL_SHIELD_DEADLY;
5522 element = EL_EXTRA_TIME;
5530 element = EL_EMPTY_SPACE;
5533 case 0x1578: // quicksand (empty)
5534 element = EL_QUICKSAND_FAST_EMPTY;
5537 case 0x1579: // slow quicksand (empty)
5538 element = EL_QUICKSAND_EMPTY;
5548 element = EL_EM_DYNAMITE;
5551 case 0x15a1: // key (red)
5552 element = EL_EM_KEY_1;
5555 case 0x15a2: // key (yellow)
5556 element = EL_EM_KEY_2;
5559 case 0x15a3: // key (blue)
5560 element = EL_EM_KEY_4;
5563 case 0x15a4: // key (green)
5564 element = EL_EM_KEY_3;
5567 case 0x15a5: // key (white)
5568 element = EL_DC_KEY_WHITE;
5572 element = EL_WALL_SLIPPERY;
5579 case 0x15a8: // wall (not round)
5583 case 0x15a9: // (blue)
5584 element = EL_CHAR_A;
5587 case 0x15aa: // (blue)
5588 element = EL_CHAR_B;
5591 case 0x15ab: // (blue)
5592 element = EL_CHAR_C;
5595 case 0x15ac: // (blue)
5596 element = EL_CHAR_D;
5599 case 0x15ad: // (blue)
5600 element = EL_CHAR_E;
5603 case 0x15ae: // (blue)
5604 element = EL_CHAR_F;
5607 case 0x15af: // (blue)
5608 element = EL_CHAR_G;
5611 case 0x15b0: // (blue)
5612 element = EL_CHAR_H;
5615 case 0x15b1: // (blue)
5616 element = EL_CHAR_I;
5619 case 0x15b2: // (blue)
5620 element = EL_CHAR_J;
5623 case 0x15b3: // (blue)
5624 element = EL_CHAR_K;
5627 case 0x15b4: // (blue)
5628 element = EL_CHAR_L;
5631 case 0x15b5: // (blue)
5632 element = EL_CHAR_M;
5635 case 0x15b6: // (blue)
5636 element = EL_CHAR_N;
5639 case 0x15b7: // (blue)
5640 element = EL_CHAR_O;
5643 case 0x15b8: // (blue)
5644 element = EL_CHAR_P;
5647 case 0x15b9: // (blue)
5648 element = EL_CHAR_Q;
5651 case 0x15ba: // (blue)
5652 element = EL_CHAR_R;
5655 case 0x15bb: // (blue)
5656 element = EL_CHAR_S;
5659 case 0x15bc: // (blue)
5660 element = EL_CHAR_T;
5663 case 0x15bd: // (blue)
5664 element = EL_CHAR_U;
5667 case 0x15be: // (blue)
5668 element = EL_CHAR_V;
5671 case 0x15bf: // (blue)
5672 element = EL_CHAR_W;
5675 case 0x15c0: // (blue)
5676 element = EL_CHAR_X;
5679 case 0x15c1: // (blue)
5680 element = EL_CHAR_Y;
5683 case 0x15c2: // (blue)
5684 element = EL_CHAR_Z;
5687 case 0x15c3: // (blue)
5688 element = EL_CHAR_AUMLAUT;
5691 case 0x15c4: // (blue)
5692 element = EL_CHAR_OUMLAUT;
5695 case 0x15c5: // (blue)
5696 element = EL_CHAR_UUMLAUT;
5699 case 0x15c6: // (blue)
5700 element = EL_CHAR_0;
5703 case 0x15c7: // (blue)
5704 element = EL_CHAR_1;
5707 case 0x15c8: // (blue)
5708 element = EL_CHAR_2;
5711 case 0x15c9: // (blue)
5712 element = EL_CHAR_3;
5715 case 0x15ca: // (blue)
5716 element = EL_CHAR_4;
5719 case 0x15cb: // (blue)
5720 element = EL_CHAR_5;
5723 case 0x15cc: // (blue)
5724 element = EL_CHAR_6;
5727 case 0x15cd: // (blue)
5728 element = EL_CHAR_7;
5731 case 0x15ce: // (blue)
5732 element = EL_CHAR_8;
5735 case 0x15cf: // (blue)
5736 element = EL_CHAR_9;
5739 case 0x15d0: // (blue)
5740 element = EL_CHAR_PERIOD;
5743 case 0x15d1: // (blue)
5744 element = EL_CHAR_EXCLAM;
5747 case 0x15d2: // (blue)
5748 element = EL_CHAR_COLON;
5751 case 0x15d3: // (blue)
5752 element = EL_CHAR_LESS;
5755 case 0x15d4: // (blue)
5756 element = EL_CHAR_GREATER;
5759 case 0x15d5: // (blue)
5760 element = EL_CHAR_QUESTION;
5763 case 0x15d6: // (blue)
5764 element = EL_CHAR_COPYRIGHT;
5767 case 0x15d7: // (blue)
5768 element = EL_CHAR_UP;
5771 case 0x15d8: // (blue)
5772 element = EL_CHAR_DOWN;
5775 case 0x15d9: // (blue)
5776 element = EL_CHAR_BUTTON;
5779 case 0x15da: // (blue)
5780 element = EL_CHAR_PLUS;
5783 case 0x15db: // (blue)
5784 element = EL_CHAR_MINUS;
5787 case 0x15dc: // (blue)
5788 element = EL_CHAR_APOSTROPHE;
5791 case 0x15dd: // (blue)
5792 element = EL_CHAR_PARENLEFT;
5795 case 0x15de: // (blue)
5796 element = EL_CHAR_PARENRIGHT;
5799 case 0x15df: // (green)
5800 element = EL_CHAR_A;
5803 case 0x15e0: // (green)
5804 element = EL_CHAR_B;
5807 case 0x15e1: // (green)
5808 element = EL_CHAR_C;
5811 case 0x15e2: // (green)
5812 element = EL_CHAR_D;
5815 case 0x15e3: // (green)
5816 element = EL_CHAR_E;
5819 case 0x15e4: // (green)
5820 element = EL_CHAR_F;
5823 case 0x15e5: // (green)
5824 element = EL_CHAR_G;
5827 case 0x15e6: // (green)
5828 element = EL_CHAR_H;
5831 case 0x15e7: // (green)
5832 element = EL_CHAR_I;
5835 case 0x15e8: // (green)
5836 element = EL_CHAR_J;
5839 case 0x15e9: // (green)
5840 element = EL_CHAR_K;
5843 case 0x15ea: // (green)
5844 element = EL_CHAR_L;
5847 case 0x15eb: // (green)
5848 element = EL_CHAR_M;
5851 case 0x15ec: // (green)
5852 element = EL_CHAR_N;
5855 case 0x15ed: // (green)
5856 element = EL_CHAR_O;
5859 case 0x15ee: // (green)
5860 element = EL_CHAR_P;
5863 case 0x15ef: // (green)
5864 element = EL_CHAR_Q;
5867 case 0x15f0: // (green)
5868 element = EL_CHAR_R;
5871 case 0x15f1: // (green)
5872 element = EL_CHAR_S;
5875 case 0x15f2: // (green)
5876 element = EL_CHAR_T;
5879 case 0x15f3: // (green)
5880 element = EL_CHAR_U;
5883 case 0x15f4: // (green)
5884 element = EL_CHAR_V;
5887 case 0x15f5: // (green)
5888 element = EL_CHAR_W;
5891 case 0x15f6: // (green)
5892 element = EL_CHAR_X;
5895 case 0x15f7: // (green)
5896 element = EL_CHAR_Y;
5899 case 0x15f8: // (green)
5900 element = EL_CHAR_Z;
5903 case 0x15f9: // (green)
5904 element = EL_CHAR_AUMLAUT;
5907 case 0x15fa: // (green)
5908 element = EL_CHAR_OUMLAUT;
5911 case 0x15fb: // (green)
5912 element = EL_CHAR_UUMLAUT;
5915 case 0x15fc: // (green)
5916 element = EL_CHAR_0;
5919 case 0x15fd: // (green)
5920 element = EL_CHAR_1;
5923 case 0x15fe: // (green)
5924 element = EL_CHAR_2;
5927 case 0x15ff: // (green)
5928 element = EL_CHAR_3;
5931 case 0x1600: // (green)
5932 element = EL_CHAR_4;
5935 case 0x1601: // (green)
5936 element = EL_CHAR_5;
5939 case 0x1602: // (green)
5940 element = EL_CHAR_6;
5943 case 0x1603: // (green)
5944 element = EL_CHAR_7;
5947 case 0x1604: // (green)
5948 element = EL_CHAR_8;
5951 case 0x1605: // (green)
5952 element = EL_CHAR_9;
5955 case 0x1606: // (green)
5956 element = EL_CHAR_PERIOD;
5959 case 0x1607: // (green)
5960 element = EL_CHAR_EXCLAM;
5963 case 0x1608: // (green)
5964 element = EL_CHAR_COLON;
5967 case 0x1609: // (green)
5968 element = EL_CHAR_LESS;
5971 case 0x160a: // (green)
5972 element = EL_CHAR_GREATER;
5975 case 0x160b: // (green)
5976 element = EL_CHAR_QUESTION;
5979 case 0x160c: // (green)
5980 element = EL_CHAR_COPYRIGHT;
5983 case 0x160d: // (green)
5984 element = EL_CHAR_UP;
5987 case 0x160e: // (green)
5988 element = EL_CHAR_DOWN;
5991 case 0x160f: // (green)
5992 element = EL_CHAR_BUTTON;
5995 case 0x1610: // (green)
5996 element = EL_CHAR_PLUS;
5999 case 0x1611: // (green)
6000 element = EL_CHAR_MINUS;
6003 case 0x1612: // (green)
6004 element = EL_CHAR_APOSTROPHE;
6007 case 0x1613: // (green)
6008 element = EL_CHAR_PARENLEFT;
6011 case 0x1614: // (green)
6012 element = EL_CHAR_PARENRIGHT;
6015 case 0x1615: // (blue steel)
6016 element = EL_STEEL_CHAR_A;
6019 case 0x1616: // (blue steel)
6020 element = EL_STEEL_CHAR_B;
6023 case 0x1617: // (blue steel)
6024 element = EL_STEEL_CHAR_C;
6027 case 0x1618: // (blue steel)
6028 element = EL_STEEL_CHAR_D;
6031 case 0x1619: // (blue steel)
6032 element = EL_STEEL_CHAR_E;
6035 case 0x161a: // (blue steel)
6036 element = EL_STEEL_CHAR_F;
6039 case 0x161b: // (blue steel)
6040 element = EL_STEEL_CHAR_G;
6043 case 0x161c: // (blue steel)
6044 element = EL_STEEL_CHAR_H;
6047 case 0x161d: // (blue steel)
6048 element = EL_STEEL_CHAR_I;
6051 case 0x161e: // (blue steel)
6052 element = EL_STEEL_CHAR_J;
6055 case 0x161f: // (blue steel)
6056 element = EL_STEEL_CHAR_K;
6059 case 0x1620: // (blue steel)
6060 element = EL_STEEL_CHAR_L;
6063 case 0x1621: // (blue steel)
6064 element = EL_STEEL_CHAR_M;
6067 case 0x1622: // (blue steel)
6068 element = EL_STEEL_CHAR_N;
6071 case 0x1623: // (blue steel)
6072 element = EL_STEEL_CHAR_O;
6075 case 0x1624: // (blue steel)
6076 element = EL_STEEL_CHAR_P;
6079 case 0x1625: // (blue steel)
6080 element = EL_STEEL_CHAR_Q;
6083 case 0x1626: // (blue steel)
6084 element = EL_STEEL_CHAR_R;
6087 case 0x1627: // (blue steel)
6088 element = EL_STEEL_CHAR_S;
6091 case 0x1628: // (blue steel)
6092 element = EL_STEEL_CHAR_T;
6095 case 0x1629: // (blue steel)
6096 element = EL_STEEL_CHAR_U;
6099 case 0x162a: // (blue steel)
6100 element = EL_STEEL_CHAR_V;
6103 case 0x162b: // (blue steel)
6104 element = EL_STEEL_CHAR_W;
6107 case 0x162c: // (blue steel)
6108 element = EL_STEEL_CHAR_X;
6111 case 0x162d: // (blue steel)
6112 element = EL_STEEL_CHAR_Y;
6115 case 0x162e: // (blue steel)
6116 element = EL_STEEL_CHAR_Z;
6119 case 0x162f: // (blue steel)
6120 element = EL_STEEL_CHAR_AUMLAUT;
6123 case 0x1630: // (blue steel)
6124 element = EL_STEEL_CHAR_OUMLAUT;
6127 case 0x1631: // (blue steel)
6128 element = EL_STEEL_CHAR_UUMLAUT;
6131 case 0x1632: // (blue steel)
6132 element = EL_STEEL_CHAR_0;
6135 case 0x1633: // (blue steel)
6136 element = EL_STEEL_CHAR_1;
6139 case 0x1634: // (blue steel)
6140 element = EL_STEEL_CHAR_2;
6143 case 0x1635: // (blue steel)
6144 element = EL_STEEL_CHAR_3;
6147 case 0x1636: // (blue steel)
6148 element = EL_STEEL_CHAR_4;
6151 case 0x1637: // (blue steel)
6152 element = EL_STEEL_CHAR_5;
6155 case 0x1638: // (blue steel)
6156 element = EL_STEEL_CHAR_6;
6159 case 0x1639: // (blue steel)
6160 element = EL_STEEL_CHAR_7;
6163 case 0x163a: // (blue steel)
6164 element = EL_STEEL_CHAR_8;
6167 case 0x163b: // (blue steel)
6168 element = EL_STEEL_CHAR_9;
6171 case 0x163c: // (blue steel)
6172 element = EL_STEEL_CHAR_PERIOD;
6175 case 0x163d: // (blue steel)
6176 element = EL_STEEL_CHAR_EXCLAM;
6179 case 0x163e: // (blue steel)
6180 element = EL_STEEL_CHAR_COLON;
6183 case 0x163f: // (blue steel)
6184 element = EL_STEEL_CHAR_LESS;
6187 case 0x1640: // (blue steel)
6188 element = EL_STEEL_CHAR_GREATER;
6191 case 0x1641: // (blue steel)
6192 element = EL_STEEL_CHAR_QUESTION;
6195 case 0x1642: // (blue steel)
6196 element = EL_STEEL_CHAR_COPYRIGHT;
6199 case 0x1643: // (blue steel)
6200 element = EL_STEEL_CHAR_UP;
6203 case 0x1644: // (blue steel)
6204 element = EL_STEEL_CHAR_DOWN;
6207 case 0x1645: // (blue steel)
6208 element = EL_STEEL_CHAR_BUTTON;
6211 case 0x1646: // (blue steel)
6212 element = EL_STEEL_CHAR_PLUS;
6215 case 0x1647: // (blue steel)
6216 element = EL_STEEL_CHAR_MINUS;
6219 case 0x1648: // (blue steel)
6220 element = EL_STEEL_CHAR_APOSTROPHE;
6223 case 0x1649: // (blue steel)
6224 element = EL_STEEL_CHAR_PARENLEFT;
6227 case 0x164a: // (blue steel)
6228 element = EL_STEEL_CHAR_PARENRIGHT;
6231 case 0x164b: // (green steel)
6232 element = EL_STEEL_CHAR_A;
6235 case 0x164c: // (green steel)
6236 element = EL_STEEL_CHAR_B;
6239 case 0x164d: // (green steel)
6240 element = EL_STEEL_CHAR_C;
6243 case 0x164e: // (green steel)
6244 element = EL_STEEL_CHAR_D;
6247 case 0x164f: // (green steel)
6248 element = EL_STEEL_CHAR_E;
6251 case 0x1650: // (green steel)
6252 element = EL_STEEL_CHAR_F;
6255 case 0x1651: // (green steel)
6256 element = EL_STEEL_CHAR_G;
6259 case 0x1652: // (green steel)
6260 element = EL_STEEL_CHAR_H;
6263 case 0x1653: // (green steel)
6264 element = EL_STEEL_CHAR_I;
6267 case 0x1654: // (green steel)
6268 element = EL_STEEL_CHAR_J;
6271 case 0x1655: // (green steel)
6272 element = EL_STEEL_CHAR_K;
6275 case 0x1656: // (green steel)
6276 element = EL_STEEL_CHAR_L;
6279 case 0x1657: // (green steel)
6280 element = EL_STEEL_CHAR_M;
6283 case 0x1658: // (green steel)
6284 element = EL_STEEL_CHAR_N;
6287 case 0x1659: // (green steel)
6288 element = EL_STEEL_CHAR_O;
6291 case 0x165a: // (green steel)
6292 element = EL_STEEL_CHAR_P;
6295 case 0x165b: // (green steel)
6296 element = EL_STEEL_CHAR_Q;
6299 case 0x165c: // (green steel)
6300 element = EL_STEEL_CHAR_R;
6303 case 0x165d: // (green steel)
6304 element = EL_STEEL_CHAR_S;
6307 case 0x165e: // (green steel)
6308 element = EL_STEEL_CHAR_T;
6311 case 0x165f: // (green steel)
6312 element = EL_STEEL_CHAR_U;
6315 case 0x1660: // (green steel)
6316 element = EL_STEEL_CHAR_V;
6319 case 0x1661: // (green steel)
6320 element = EL_STEEL_CHAR_W;
6323 case 0x1662: // (green steel)
6324 element = EL_STEEL_CHAR_X;
6327 case 0x1663: // (green steel)
6328 element = EL_STEEL_CHAR_Y;
6331 case 0x1664: // (green steel)
6332 element = EL_STEEL_CHAR_Z;
6335 case 0x1665: // (green steel)
6336 element = EL_STEEL_CHAR_AUMLAUT;
6339 case 0x1666: // (green steel)
6340 element = EL_STEEL_CHAR_OUMLAUT;
6343 case 0x1667: // (green steel)
6344 element = EL_STEEL_CHAR_UUMLAUT;
6347 case 0x1668: // (green steel)
6348 element = EL_STEEL_CHAR_0;
6351 case 0x1669: // (green steel)
6352 element = EL_STEEL_CHAR_1;
6355 case 0x166a: // (green steel)
6356 element = EL_STEEL_CHAR_2;
6359 case 0x166b: // (green steel)
6360 element = EL_STEEL_CHAR_3;
6363 case 0x166c: // (green steel)
6364 element = EL_STEEL_CHAR_4;
6367 case 0x166d: // (green steel)
6368 element = EL_STEEL_CHAR_5;
6371 case 0x166e: // (green steel)
6372 element = EL_STEEL_CHAR_6;
6375 case 0x166f: // (green steel)
6376 element = EL_STEEL_CHAR_7;
6379 case 0x1670: // (green steel)
6380 element = EL_STEEL_CHAR_8;
6383 case 0x1671: // (green steel)
6384 element = EL_STEEL_CHAR_9;
6387 case 0x1672: // (green steel)
6388 element = EL_STEEL_CHAR_PERIOD;
6391 case 0x1673: // (green steel)
6392 element = EL_STEEL_CHAR_EXCLAM;
6395 case 0x1674: // (green steel)
6396 element = EL_STEEL_CHAR_COLON;
6399 case 0x1675: // (green steel)
6400 element = EL_STEEL_CHAR_LESS;
6403 case 0x1676: // (green steel)
6404 element = EL_STEEL_CHAR_GREATER;
6407 case 0x1677: // (green steel)
6408 element = EL_STEEL_CHAR_QUESTION;
6411 case 0x1678: // (green steel)
6412 element = EL_STEEL_CHAR_COPYRIGHT;
6415 case 0x1679: // (green steel)
6416 element = EL_STEEL_CHAR_UP;
6419 case 0x167a: // (green steel)
6420 element = EL_STEEL_CHAR_DOWN;
6423 case 0x167b: // (green steel)
6424 element = EL_STEEL_CHAR_BUTTON;
6427 case 0x167c: // (green steel)
6428 element = EL_STEEL_CHAR_PLUS;
6431 case 0x167d: // (green steel)
6432 element = EL_STEEL_CHAR_MINUS;
6435 case 0x167e: // (green steel)
6436 element = EL_STEEL_CHAR_APOSTROPHE;
6439 case 0x167f: // (green steel)
6440 element = EL_STEEL_CHAR_PARENLEFT;
6443 case 0x1680: // (green steel)
6444 element = EL_STEEL_CHAR_PARENRIGHT;
6447 case 0x1681: // gate (red)
6448 element = EL_EM_GATE_1;
6451 case 0x1682: // secret gate (red)
6452 element = EL_EM_GATE_1_GRAY;
6455 case 0x1683: // gate (yellow)
6456 element = EL_EM_GATE_2;
6459 case 0x1684: // secret gate (yellow)
6460 element = EL_EM_GATE_2_GRAY;
6463 case 0x1685: // gate (blue)
6464 element = EL_EM_GATE_4;
6467 case 0x1686: // secret gate (blue)
6468 element = EL_EM_GATE_4_GRAY;
6471 case 0x1687: // gate (green)
6472 element = EL_EM_GATE_3;
6475 case 0x1688: // secret gate (green)
6476 element = EL_EM_GATE_3_GRAY;
6479 case 0x1689: // gate (white)
6480 element = EL_DC_GATE_WHITE;
6483 case 0x168a: // secret gate (white)
6484 element = EL_DC_GATE_WHITE_GRAY;
6487 case 0x168b: // secret gate (no key)
6488 element = EL_DC_GATE_FAKE_GRAY;
6492 element = EL_ROBOT_WHEEL;
6496 element = EL_DC_TIMEGATE_SWITCH;
6500 element = EL_ACID_POOL_BOTTOM;
6504 element = EL_ACID_POOL_TOPLEFT;
6508 element = EL_ACID_POOL_TOPRIGHT;
6512 element = EL_ACID_POOL_BOTTOMLEFT;
6516 element = EL_ACID_POOL_BOTTOMRIGHT;
6520 element = EL_STEELWALL;
6524 element = EL_STEELWALL_SLIPPERY;
6527 case 0x1695: // steel wall (not round)
6528 element = EL_STEELWALL;
6531 case 0x1696: // steel wall (left)
6532 element = EL_DC_STEELWALL_1_LEFT;
6535 case 0x1697: // steel wall (bottom)
6536 element = EL_DC_STEELWALL_1_BOTTOM;
6539 case 0x1698: // steel wall (right)
6540 element = EL_DC_STEELWALL_1_RIGHT;
6543 case 0x1699: // steel wall (top)
6544 element = EL_DC_STEELWALL_1_TOP;
6547 case 0x169a: // steel wall (left/bottom)
6548 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6551 case 0x169b: // steel wall (right/bottom)
6552 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6555 case 0x169c: // steel wall (right/top)
6556 element = EL_DC_STEELWALL_1_TOPRIGHT;
6559 case 0x169d: // steel wall (left/top)
6560 element = EL_DC_STEELWALL_1_TOPLEFT;
6563 case 0x169e: // steel wall (right/bottom small)
6564 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6567 case 0x169f: // steel wall (left/bottom small)
6568 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6571 case 0x16a0: // steel wall (right/top small)
6572 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6575 case 0x16a1: // steel wall (left/top small)
6576 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6579 case 0x16a2: // steel wall (left/right)
6580 element = EL_DC_STEELWALL_1_VERTICAL;
6583 case 0x16a3: // steel wall (top/bottom)
6584 element = EL_DC_STEELWALL_1_HORIZONTAL;
6587 case 0x16a4: // steel wall 2 (left end)
6588 element = EL_DC_STEELWALL_2_LEFT;
6591 case 0x16a5: // steel wall 2 (right end)
6592 element = EL_DC_STEELWALL_2_RIGHT;
6595 case 0x16a6: // steel wall 2 (top end)
6596 element = EL_DC_STEELWALL_2_TOP;
6599 case 0x16a7: // steel wall 2 (bottom end)
6600 element = EL_DC_STEELWALL_2_BOTTOM;
6603 case 0x16a8: // steel wall 2 (left/right)
6604 element = EL_DC_STEELWALL_2_HORIZONTAL;
6607 case 0x16a9: // steel wall 2 (up/down)
6608 element = EL_DC_STEELWALL_2_VERTICAL;
6611 case 0x16aa: // steel wall 2 (mid)
6612 element = EL_DC_STEELWALL_2_MIDDLE;
6616 element = EL_SIGN_EXCLAMATION;
6620 element = EL_SIGN_RADIOACTIVITY;
6624 element = EL_SIGN_STOP;
6628 element = EL_SIGN_WHEELCHAIR;
6632 element = EL_SIGN_PARKING;
6636 element = EL_SIGN_NO_ENTRY;
6640 element = EL_SIGN_HEART;
6644 element = EL_SIGN_GIVE_WAY;
6648 element = EL_SIGN_ENTRY_FORBIDDEN;
6652 element = EL_SIGN_EMERGENCY_EXIT;
6656 element = EL_SIGN_YIN_YANG;
6660 element = EL_WALL_EMERALD;
6664 element = EL_WALL_DIAMOND;
6668 element = EL_WALL_PEARL;
6672 element = EL_WALL_CRYSTAL;
6676 element = EL_INVISIBLE_WALL;
6680 element = EL_INVISIBLE_STEELWALL;
6684 // EL_INVISIBLE_SAND
6687 element = EL_LIGHT_SWITCH;
6691 element = EL_ENVELOPE_1;
6695 if (element >= 0x0117 && element <= 0x036e) // (?)
6696 element = EL_DIAMOND;
6697 else if (element >= 0x042d && element <= 0x0684) // (?)
6698 element = EL_EMERALD;
6699 else if (element >= 0x157c && element <= 0x158b)
6701 else if (element >= 0x1590 && element <= 0x159f)
6702 element = EL_DC_LANDMINE;
6703 else if (element >= 0x16bc && element <= 0x16cb)
6704 element = EL_INVISIBLE_SAND;
6707 Warn("unknown Diamond Caves element 0x%04x", element);
6709 element = EL_UNKNOWN;
6714 return getMappedElement(element);
6717 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6719 byte header[DC_LEVEL_HEADER_SIZE];
6721 int envelope_header_pos = 62;
6722 int envelope_content_pos = 94;
6723 int level_name_pos = 251;
6724 int level_author_pos = 292;
6725 int envelope_header_len;
6726 int envelope_content_len;
6728 int level_author_len;
6730 int num_yamyam_contents;
6733 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6735 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6737 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6739 header[i * 2 + 0] = header_word >> 8;
6740 header[i * 2 + 1] = header_word & 0xff;
6743 // read some values from level header to check level decoding integrity
6744 fieldx = header[6] | (header[7] << 8);
6745 fieldy = header[8] | (header[9] << 8);
6746 num_yamyam_contents = header[60] | (header[61] << 8);
6748 // do some simple sanity checks to ensure that level was correctly decoded
6749 if (fieldx < 1 || fieldx > 256 ||
6750 fieldy < 1 || fieldy > 256 ||
6751 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6753 level->no_valid_file = TRUE;
6755 Warn("cannot decode level from stream -- using empty level");
6760 // maximum envelope header size is 31 bytes
6761 envelope_header_len = header[envelope_header_pos];
6762 // maximum envelope content size is 110 (156?) bytes
6763 envelope_content_len = header[envelope_content_pos];
6765 // maximum level title size is 40 bytes
6766 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6767 // maximum level author size is 30 (51?) bytes
6768 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6772 for (i = 0; i < envelope_header_len; i++)
6773 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6774 level->envelope[0].text[envelope_size++] =
6775 header[envelope_header_pos + 1 + i];
6777 if (envelope_header_len > 0 && envelope_content_len > 0)
6779 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6780 level->envelope[0].text[envelope_size++] = '\n';
6781 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6782 level->envelope[0].text[envelope_size++] = '\n';
6785 for (i = 0; i < envelope_content_len; i++)
6786 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6787 level->envelope[0].text[envelope_size++] =
6788 header[envelope_content_pos + 1 + i];
6790 level->envelope[0].text[envelope_size] = '\0';
6792 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6793 level->envelope[0].ysize = 10;
6794 level->envelope[0].autowrap = TRUE;
6795 level->envelope[0].centered = TRUE;
6797 for (i = 0; i < level_name_len; i++)
6798 level->name[i] = header[level_name_pos + 1 + i];
6799 level->name[level_name_len] = '\0';
6801 for (i = 0; i < level_author_len; i++)
6802 level->author[i] = header[level_author_pos + 1 + i];
6803 level->author[level_author_len] = '\0';
6805 num_yamyam_contents = header[60] | (header[61] << 8);
6806 level->num_yamyam_contents =
6807 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6809 for (i = 0; i < num_yamyam_contents; i++)
6811 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6813 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6814 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6816 if (i < MAX_ELEMENT_CONTENTS)
6817 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6821 fieldx = header[6] | (header[7] << 8);
6822 fieldy = header[8] | (header[9] << 8);
6823 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6824 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6826 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6828 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6829 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6831 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6832 level->field[x][y] = getMappedElement_DC(element_dc);
6835 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6836 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6837 level->field[x][y] = EL_PLAYER_1;
6839 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6840 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6841 level->field[x][y] = EL_PLAYER_2;
6843 level->gems_needed = header[18] | (header[19] << 8);
6845 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6846 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6847 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6848 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6849 level->score[SC_NUT] = header[28] | (header[29] << 8);
6850 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6851 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6852 level->score[SC_BUG] = header[34] | (header[35] << 8);
6853 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6854 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6855 level->score[SC_KEY] = header[40] | (header[41] << 8);
6856 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6858 level->time = header[44] | (header[45] << 8);
6860 level->amoeba_speed = header[46] | (header[47] << 8);
6861 level->time_light = header[48] | (header[49] << 8);
6862 level->time_timegate = header[50] | (header[51] << 8);
6863 level->time_wheel = header[52] | (header[53] << 8);
6864 level->time_magic_wall = header[54] | (header[55] << 8);
6865 level->extra_time = header[56] | (header[57] << 8);
6866 level->shield_normal_time = header[58] | (header[59] << 8);
6868 // shield and extra time elements do not have a score
6869 level->score[SC_SHIELD] = 0;
6870 level->extra_time_score = 0;
6872 // set time for normal and deadly shields to the same value
6873 level->shield_deadly_time = level->shield_normal_time;
6875 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6876 // can slip down from flat walls, like normal walls and steel walls
6877 level->em_slippery_gems = TRUE;
6879 // time score is counted for each 10 seconds left in Diamond Caves levels
6880 level->time_score_base = 10;
6883 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6884 struct LevelFileInfo *level_file_info,
6885 boolean level_info_only)
6887 char *filename = level_file_info->filename;
6889 int num_magic_bytes = 8;
6890 char magic_bytes[num_magic_bytes + 1];
6891 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6893 if (!(file = openFile(filename, MODE_READ)))
6895 level->no_valid_file = TRUE;
6897 if (!level_info_only)
6898 Warn("cannot read level '%s' -- using empty level", filename);
6903 // fseek(file, 0x0000, SEEK_SET);
6905 if (level_file_info->packed)
6907 // read "magic bytes" from start of file
6908 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6909 magic_bytes[0] = '\0';
6911 // check "magic bytes" for correct file format
6912 if (!strPrefix(magic_bytes, "DC2"))
6914 level->no_valid_file = TRUE;
6916 Warn("unknown DC level file '%s' -- using empty level", filename);
6921 if (strPrefix(magic_bytes, "DC2Win95") ||
6922 strPrefix(magic_bytes, "DC2Win98"))
6924 int position_first_level = 0x00fa;
6925 int extra_bytes = 4;
6928 // advance file stream to first level inside the level package
6929 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6931 // each block of level data is followed by block of non-level data
6932 num_levels_to_skip *= 2;
6934 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6935 while (num_levels_to_skip >= 0)
6937 // advance file stream to next level inside the level package
6938 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6940 level->no_valid_file = TRUE;
6942 Warn("cannot fseek in file '%s' -- using empty level", filename);
6947 // skip apparently unused extra bytes following each level
6948 ReadUnusedBytesFromFile(file, extra_bytes);
6950 // read size of next level in level package
6951 skip_bytes = getFile32BitLE(file);
6953 num_levels_to_skip--;
6958 level->no_valid_file = TRUE;
6960 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6966 LoadLevelFromFileStream_DC(file, level);
6972 // ----------------------------------------------------------------------------
6973 // functions for loading SB level
6974 // ----------------------------------------------------------------------------
6976 int getMappedElement_SB(int element_ascii, boolean use_ces)
6984 sb_element_mapping[] =
6986 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6987 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6988 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6989 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6990 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6991 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6992 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6993 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
7000 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7001 if (element_ascii == sb_element_mapping[i].ascii)
7002 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7004 return EL_UNDEFINED;
7007 static void SetLevelSettings_SB(struct LevelInfo *level)
7011 level->use_step_counter = TRUE;
7014 level->score[SC_TIME_BONUS] = 0;
7015 level->time_score_base = 1;
7016 level->rate_time_over_score = TRUE;
7019 level->auto_exit_sokoban = TRUE;
7022 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7023 struct LevelFileInfo *level_file_info,
7024 boolean level_info_only)
7026 char *filename = level_file_info->filename;
7027 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7028 char last_comment[MAX_LINE_LEN];
7029 char level_name[MAX_LINE_LEN];
7032 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7033 boolean read_continued_line = FALSE;
7034 boolean reading_playfield = FALSE;
7035 boolean got_valid_playfield_line = FALSE;
7036 boolean invalid_playfield_char = FALSE;
7037 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7038 int file_level_nr = 0;
7039 int x = 0, y = 0; // initialized to make compilers happy
7041 last_comment[0] = '\0';
7042 level_name[0] = '\0';
7044 if (!(file = openFile(filename, MODE_READ)))
7046 level->no_valid_file = TRUE;
7048 if (!level_info_only)
7049 Warn("cannot read level '%s' -- using empty level", filename);
7054 while (!checkEndOfFile(file))
7056 // level successfully read, but next level may follow here
7057 if (!got_valid_playfield_line && reading_playfield)
7059 // read playfield from single level file -- skip remaining file
7060 if (!level_file_info->packed)
7063 if (file_level_nr >= num_levels_to_skip)
7068 last_comment[0] = '\0';
7069 level_name[0] = '\0';
7071 reading_playfield = FALSE;
7074 got_valid_playfield_line = FALSE;
7076 // read next line of input file
7077 if (!getStringFromFile(file, line, MAX_LINE_LEN))
7080 // cut trailing line break (this can be newline and/or carriage return)
7081 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7082 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7085 // copy raw input line for later use (mainly debugging output)
7086 strcpy(line_raw, line);
7088 if (read_continued_line)
7090 // append new line to existing line, if there is enough space
7091 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7092 strcat(previous_line, line_ptr);
7094 strcpy(line, previous_line); // copy storage buffer to line
7096 read_continued_line = FALSE;
7099 // if the last character is '\', continue at next line
7100 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7102 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
7103 strcpy(previous_line, line); // copy line to storage buffer
7105 read_continued_line = TRUE;
7111 if (line[0] == '\0')
7114 // extract comment text from comment line
7117 for (line_ptr = line; *line_ptr; line_ptr++)
7118 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7121 strcpy(last_comment, line_ptr);
7126 // extract level title text from line containing level title
7127 if (line[0] == '\'')
7129 strcpy(level_name, &line[1]);
7131 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7132 level_name[strlen(level_name) - 1] = '\0';
7137 // skip lines containing only spaces (or empty lines)
7138 for (line_ptr = line; *line_ptr; line_ptr++)
7139 if (*line_ptr != ' ')
7141 if (*line_ptr == '\0')
7144 // at this point, we have found a line containing part of a playfield
7146 got_valid_playfield_line = TRUE;
7148 if (!reading_playfield)
7150 reading_playfield = TRUE;
7151 invalid_playfield_char = FALSE;
7153 for (x = 0; x < MAX_LEV_FIELDX; x++)
7154 for (y = 0; y < MAX_LEV_FIELDY; y++)
7155 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7160 // start with topmost tile row
7164 // skip playfield line if larger row than allowed
7165 if (y >= MAX_LEV_FIELDY)
7168 // start with leftmost tile column
7171 // read playfield elements from line
7172 for (line_ptr = line; *line_ptr; line_ptr++)
7174 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7176 // stop parsing playfield line if larger column than allowed
7177 if (x >= MAX_LEV_FIELDX)
7180 if (mapped_sb_element == EL_UNDEFINED)
7182 invalid_playfield_char = TRUE;
7187 level->field[x][y] = mapped_sb_element;
7189 // continue with next tile column
7192 level->fieldx = MAX(x, level->fieldx);
7195 if (invalid_playfield_char)
7197 // if first playfield line, treat invalid lines as comment lines
7199 reading_playfield = FALSE;
7204 // continue with next tile row
7212 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7213 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7215 if (!reading_playfield)
7217 level->no_valid_file = TRUE;
7219 Warn("cannot read level '%s' -- using empty level", filename);
7224 if (*level_name != '\0')
7226 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7227 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7229 else if (*last_comment != '\0')
7231 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7232 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7236 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7239 // set all empty fields beyond the border walls to invisible steel wall
7240 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7242 if ((x == 0 || x == level->fieldx - 1 ||
7243 y == 0 || y == level->fieldy - 1) &&
7244 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7245 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7246 level->field, level->fieldx, level->fieldy);
7249 // set special level settings for Sokoban levels
7250 SetLevelSettings_SB(level);
7252 if (load_xsb_to_ces)
7254 // special global settings can now be set in level template
7255 level->use_custom_template = TRUE;
7260 // -------------------------------------------------------------------------
7261 // functions for handling native levels
7262 // -------------------------------------------------------------------------
7264 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7265 struct LevelFileInfo *level_file_info,
7266 boolean level_info_only)
7270 // determine position of requested level inside level package
7271 if (level_file_info->packed)
7272 pos = level_file_info->nr - leveldir_current->first_level;
7274 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7275 level->no_valid_file = TRUE;
7278 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7279 struct LevelFileInfo *level_file_info,
7280 boolean level_info_only)
7282 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7283 level->no_valid_file = TRUE;
7286 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7287 struct LevelFileInfo *level_file_info,
7288 boolean level_info_only)
7292 // determine position of requested level inside level package
7293 if (level_file_info->packed)
7294 pos = level_file_info->nr - leveldir_current->first_level;
7296 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7297 level->no_valid_file = TRUE;
7300 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7301 struct LevelFileInfo *level_file_info,
7302 boolean level_info_only)
7304 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7305 level->no_valid_file = TRUE;
7308 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7310 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7311 CopyNativeLevel_RND_to_BD(level);
7312 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7313 CopyNativeLevel_RND_to_EM(level);
7314 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7315 CopyNativeLevel_RND_to_SP(level);
7316 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7317 CopyNativeLevel_RND_to_MM(level);
7320 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7322 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7323 CopyNativeLevel_BD_to_RND(level);
7324 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7325 CopyNativeLevel_EM_to_RND(level);
7326 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7327 CopyNativeLevel_SP_to_RND(level);
7328 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7329 CopyNativeLevel_MM_to_RND(level);
7332 void SaveNativeLevel(struct LevelInfo *level)
7334 // saving native level files only supported for some game engines
7335 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7336 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7339 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7340 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7341 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7342 char *filename = getLevelFilenameFromBasename(basename);
7344 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7347 boolean success = FALSE;
7349 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7351 CopyNativeLevel_RND_to_BD(level);
7352 // CopyNativeTape_RND_to_BD(level);
7354 success = SaveNativeLevel_BD(filename);
7356 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7358 CopyNativeLevel_RND_to_SP(level);
7359 CopyNativeTape_RND_to_SP(level);
7361 success = SaveNativeLevel_SP(filename);
7365 Request("Native level file saved!", REQ_CONFIRM);
7367 Request("Failed to save native level file!", REQ_CONFIRM);
7371 // ----------------------------------------------------------------------------
7372 // functions for loading generic level
7373 // ----------------------------------------------------------------------------
7375 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7376 struct LevelFileInfo *level_file_info,
7377 boolean level_info_only)
7379 // always start with reliable default values
7380 setLevelInfoToDefaults(level, level_info_only, TRUE);
7382 switch (level_file_info->type)
7384 case LEVEL_FILE_TYPE_RND:
7385 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7388 case LEVEL_FILE_TYPE_BD:
7389 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7390 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7393 case LEVEL_FILE_TYPE_EM:
7394 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7395 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7398 case LEVEL_FILE_TYPE_SP:
7399 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7400 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7403 case LEVEL_FILE_TYPE_MM:
7404 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7405 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7408 case LEVEL_FILE_TYPE_DC:
7409 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7412 case LEVEL_FILE_TYPE_SB:
7413 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7417 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7421 // if level file is invalid, restore level structure to default values
7422 if (level->no_valid_file)
7423 setLevelInfoToDefaults(level, level_info_only, FALSE);
7425 if (check_special_flags("use_native_bd_game_engine"))
7426 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7428 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7429 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7431 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7432 CopyNativeLevel_Native_to_RND(level);
7435 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7437 static struct LevelFileInfo level_file_info;
7439 // always start with reliable default values
7440 setFileInfoToDefaults(&level_file_info);
7442 level_file_info.nr = 0; // unknown level number
7443 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7445 setString(&level_file_info.filename, filename);
7447 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7450 static void LoadLevel_InitVersion(struct LevelInfo *level)
7454 if (leveldir_current == NULL) // only when dumping level
7457 // all engine modifications also valid for levels which use latest engine
7458 if (level->game_version < VERSION_IDENT(3,2,0,5))
7460 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7461 level->time_score_base = 10;
7464 if (leveldir_current->latest_engine)
7466 // ---------- use latest game engine --------------------------------------
7468 /* For all levels which are forced to use the latest game engine version
7469 (normally all but user contributed, private and undefined levels), set
7470 the game engine version to the actual version; this allows for actual
7471 corrections in the game engine to take effect for existing, converted
7472 levels (from "classic" or other existing games) to make the emulation
7473 of the corresponding game more accurate, while (hopefully) not breaking
7474 existing levels created from other players. */
7476 level->game_version = GAME_VERSION_ACTUAL;
7478 /* Set special EM style gems behaviour: EM style gems slip down from
7479 normal, steel and growing wall. As this is a more fundamental change,
7480 it seems better to set the default behaviour to "off" (as it is more
7481 natural) and make it configurable in the level editor (as a property
7482 of gem style elements). Already existing converted levels (neither
7483 private nor contributed levels) are changed to the new behaviour. */
7485 if (level->file_version < FILE_VERSION_2_0)
7486 level->em_slippery_gems = TRUE;
7491 // ---------- use game engine the level was created with --------------------
7493 /* For all levels which are not forced to use the latest game engine
7494 version (normally user contributed, private and undefined levels),
7495 use the version of the game engine the levels were created for.
7497 Since 2.0.1, the game engine version is now directly stored
7498 in the level file (chunk "VERS"), so there is no need anymore
7499 to set the game version from the file version (except for old,
7500 pre-2.0 levels, where the game version is still taken from the
7501 file format version used to store the level -- see above). */
7503 // player was faster than enemies in 1.0.0 and before
7504 if (level->file_version == FILE_VERSION_1_0)
7505 for (i = 0; i < MAX_PLAYERS; i++)
7506 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7508 // default behaviour for EM style gems was "slippery" only in 2.0.1
7509 if (level->game_version == VERSION_IDENT(2,0,1,0))
7510 level->em_slippery_gems = TRUE;
7512 // springs could be pushed over pits before (pre-release version) 2.2.0
7513 if (level->game_version < VERSION_IDENT(2,2,0,0))
7514 level->use_spring_bug = TRUE;
7516 if (level->game_version < VERSION_IDENT(3,2,0,5))
7518 // time orb caused limited time in endless time levels before 3.2.0-5
7519 level->use_time_orb_bug = TRUE;
7521 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7522 level->block_snap_field = FALSE;
7524 // extra time score was same value as time left score before 3.2.0-5
7525 level->extra_time_score = level->score[SC_TIME_BONUS];
7528 if (level->game_version < VERSION_IDENT(3,2,0,7))
7530 // default behaviour for snapping was "not continuous" before 3.2.0-7
7531 level->continuous_snapping = FALSE;
7534 // only few elements were able to actively move into acid before 3.1.0
7535 // trigger settings did not exist before 3.1.0; set to default "any"
7536 if (level->game_version < VERSION_IDENT(3,1,0,0))
7538 // correct "can move into acid" settings (all zero in old levels)
7540 level->can_move_into_acid_bits = 0; // nothing can move into acid
7541 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7543 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7544 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7545 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7546 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7548 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7549 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7551 // correct trigger settings (stored as zero == "none" in old levels)
7553 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7555 int element = EL_CUSTOM_START + i;
7556 struct ElementInfo *ei = &element_info[element];
7558 for (j = 0; j < ei->num_change_pages; j++)
7560 struct ElementChangeInfo *change = &ei->change_page[j];
7562 change->trigger_player = CH_PLAYER_ANY;
7563 change->trigger_page = CH_PAGE_ANY;
7568 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7570 int element = EL_CUSTOM_256;
7571 struct ElementInfo *ei = &element_info[element];
7572 struct ElementChangeInfo *change = &ei->change_page[0];
7574 /* This is needed to fix a problem that was caused by a bugfix in function
7575 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7576 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7577 not replace walkable elements, but instead just placed the player on it,
7578 without placing the Sokoban field under the player). Unfortunately, this
7579 breaks "Snake Bite" style levels when the snake is halfway through a door
7580 that just closes (the snake head is still alive and can be moved in this
7581 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7582 player (without Sokoban element) which then gets killed as designed). */
7584 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7585 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7586 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7587 change->target_element = EL_PLAYER_1;
7590 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7591 if (level->game_version < VERSION_IDENT(3,2,5,0))
7593 /* This is needed to fix a problem that was caused by a bugfix in function
7594 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7595 corrects the behaviour when a custom element changes to another custom
7596 element with a higher element number that has change actions defined.
7597 Normally, only one change per frame is allowed for custom elements.
7598 Therefore, it is checked if a custom element already changed in the
7599 current frame; if it did, subsequent changes are suppressed.
7600 Unfortunately, this is only checked for element changes, but not for
7601 change actions, which are still executed. As the function above loops
7602 through all custom elements from lower to higher, an element change
7603 resulting in a lower CE number won't be checked again, while a target
7604 element with a higher number will also be checked, and potential change
7605 actions will get executed for this CE, too (which is wrong), while
7606 further changes are ignored (which is correct). As this bugfix breaks
7607 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7608 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7609 behaviour for existing levels and tapes that make use of this bug */
7611 level->use_action_after_change_bug = TRUE;
7614 // not centering level after relocating player was default only in 3.2.3
7615 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7616 level->shifted_relocation = TRUE;
7618 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7619 if (level->game_version < VERSION_IDENT(3,2,6,0))
7620 level->em_explodes_by_fire = TRUE;
7622 // levels were solved by the first player entering an exit up to 4.1.0.0
7623 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7624 level->solved_by_one_player = TRUE;
7626 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7627 if (level->game_version < VERSION_IDENT(4,1,1,1))
7628 level->use_life_bugs = TRUE;
7630 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7631 if (level->game_version < VERSION_IDENT(4,1,1,1))
7632 level->sb_objects_needed = FALSE;
7634 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7635 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7636 level->finish_dig_collect = FALSE;
7638 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7639 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7640 level->keep_walkable_ce = TRUE;
7643 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7645 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7648 // check if this level is (not) a Sokoban level
7649 for (y = 0; y < level->fieldy; y++)
7650 for (x = 0; x < level->fieldx; x++)
7651 if (!IS_SB_ELEMENT(Tile[x][y]))
7652 is_sokoban_level = FALSE;
7654 if (is_sokoban_level)
7656 // set special level settings for Sokoban levels
7657 SetLevelSettings_SB(level);
7661 static void LoadLevel_InitSettings(struct LevelInfo *level)
7663 // adjust level settings for (non-native) Sokoban-style levels
7664 LoadLevel_InitSettings_SB(level);
7666 // rename levels with title "nameless level" or if renaming is forced
7667 if (leveldir_current->empty_level_name != NULL &&
7668 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7669 leveldir_current->force_level_name))
7670 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7671 leveldir_current->empty_level_name, level_nr);
7674 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7678 // map elements that have changed in newer versions
7679 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7680 level->game_version);
7681 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7682 for (x = 0; x < 3; x++)
7683 for (y = 0; y < 3; y++)
7684 level->yamyam_content[i].e[x][y] =
7685 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7686 level->game_version);
7690 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7694 // map custom element change events that have changed in newer versions
7695 // (these following values were accidentally changed in version 3.0.1)
7696 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7697 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7699 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7701 int element = EL_CUSTOM_START + i;
7703 // order of checking and copying events to be mapped is important
7704 // (do not change the start and end value -- they are constant)
7705 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7707 if (HAS_CHANGE_EVENT(element, j - 2))
7709 SET_CHANGE_EVENT(element, j - 2, FALSE);
7710 SET_CHANGE_EVENT(element, j, TRUE);
7714 // order of checking and copying events to be mapped is important
7715 // (do not change the start and end value -- they are constant)
7716 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7718 if (HAS_CHANGE_EVENT(element, j - 1))
7720 SET_CHANGE_EVENT(element, j - 1, FALSE);
7721 SET_CHANGE_EVENT(element, j, TRUE);
7727 // initialize "can_change" field for old levels with only one change page
7728 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7730 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7732 int element = EL_CUSTOM_START + i;
7734 if (CAN_CHANGE(element))
7735 element_info[element].change->can_change = TRUE;
7739 // correct custom element values (for old levels without these options)
7740 if (level->game_version < VERSION_IDENT(3,1,1,0))
7742 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7744 int element = EL_CUSTOM_START + i;
7745 struct ElementInfo *ei = &element_info[element];
7747 if (ei->access_direction == MV_NO_DIRECTION)
7748 ei->access_direction = MV_ALL_DIRECTIONS;
7752 // correct custom element values (fix invalid values for all versions)
7755 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7757 int element = EL_CUSTOM_START + i;
7758 struct ElementInfo *ei = &element_info[element];
7760 for (j = 0; j < ei->num_change_pages; j++)
7762 struct ElementChangeInfo *change = &ei->change_page[j];
7764 if (change->trigger_player == CH_PLAYER_NONE)
7765 change->trigger_player = CH_PLAYER_ANY;
7767 if (change->trigger_side == CH_SIDE_NONE)
7768 change->trigger_side = CH_SIDE_ANY;
7773 // initialize "can_explode" field for old levels which did not store this
7774 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7775 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7777 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7779 int element = EL_CUSTOM_START + i;
7781 if (EXPLODES_1X1_OLD(element))
7782 element_info[element].explosion_type = EXPLODES_1X1;
7784 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7785 EXPLODES_SMASHED(element) ||
7786 EXPLODES_IMPACT(element)));
7790 // correct previously hard-coded move delay values for maze runner style
7791 if (level->game_version < VERSION_IDENT(3,1,1,0))
7793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7795 int element = EL_CUSTOM_START + i;
7797 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7799 // previously hard-coded and therefore ignored
7800 element_info[element].move_delay_fixed = 9;
7801 element_info[element].move_delay_random = 0;
7806 // set some other uninitialized values of custom elements in older levels
7807 if (level->game_version < VERSION_IDENT(3,1,0,0))
7809 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7811 int element = EL_CUSTOM_START + i;
7813 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7815 element_info[element].explosion_delay = 17;
7816 element_info[element].ignition_delay = 8;
7820 // set mouse click change events to work for left/middle/right mouse button
7821 if (level->game_version < VERSION_IDENT(4,2,3,0))
7823 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7825 int element = EL_CUSTOM_START + i;
7826 struct ElementInfo *ei = &element_info[element];
7828 for (j = 0; j < ei->num_change_pages; j++)
7830 struct ElementChangeInfo *change = &ei->change_page[j];
7832 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7833 change->has_event[CE_PRESSED_BY_MOUSE] ||
7834 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7835 change->has_event[CE_MOUSE_PRESSED_ON_X])
7836 change->trigger_side = CH_SIDE_ANY;
7842 static void LoadLevel_InitElements(struct LevelInfo *level)
7844 LoadLevel_InitStandardElements(level);
7846 if (level->file_has_custom_elements)
7847 LoadLevel_InitCustomElements(level);
7849 // initialize element properties for level editor etc.
7850 InitElementPropertiesEngine(level->game_version);
7851 InitElementPropertiesGfxElement();
7854 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7858 // map elements that have changed in newer versions
7859 for (y = 0; y < level->fieldy; y++)
7860 for (x = 0; x < level->fieldx; x++)
7861 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7862 level->game_version);
7864 // clear unused playfield data (nicer if level gets resized in editor)
7865 for (x = 0; x < MAX_LEV_FIELDX; x++)
7866 for (y = 0; y < MAX_LEV_FIELDY; y++)
7867 if (x >= level->fieldx || y >= level->fieldy)
7868 level->field[x][y] = EL_EMPTY;
7870 // copy elements to runtime playfield array
7871 for (x = 0; x < MAX_LEV_FIELDX; x++)
7872 for (y = 0; y < MAX_LEV_FIELDY; y++)
7873 Tile[x][y] = level->field[x][y];
7875 // initialize level size variables for faster access
7876 lev_fieldx = level->fieldx;
7877 lev_fieldy = level->fieldy;
7879 // determine border element for this level
7880 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7881 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7886 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7888 struct LevelFileInfo *level_file_info = &level->file_info;
7890 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7891 CopyNativeLevel_RND_to_Native(level);
7894 static void LoadLevelTemplate_LoadAndInit(void)
7896 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7898 LoadLevel_InitVersion(&level_template);
7899 LoadLevel_InitElements(&level_template);
7900 LoadLevel_InitSettings(&level_template);
7902 ActivateLevelTemplate();
7905 void LoadLevelTemplate(int nr)
7907 if (!fileExists(getGlobalLevelTemplateFilename()))
7909 Warn("no level template found for this level");
7914 setLevelFileInfo(&level_template.file_info, nr);
7916 LoadLevelTemplate_LoadAndInit();
7919 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7921 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7923 LoadLevelTemplate_LoadAndInit();
7926 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7928 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7930 if (level.use_custom_template)
7932 if (network_level != NULL)
7933 LoadNetworkLevelTemplate(network_level);
7935 LoadLevelTemplate(-1);
7938 LoadLevel_InitVersion(&level);
7939 LoadLevel_InitElements(&level);
7940 LoadLevel_InitPlayfield(&level);
7941 LoadLevel_InitSettings(&level);
7943 LoadLevel_InitNativeEngines(&level);
7946 void LoadLevel(int nr)
7948 SetLevelSetInfo(leveldir_current->identifier, nr);
7950 setLevelFileInfo(&level.file_info, nr);
7952 LoadLevel_LoadAndInit(NULL);
7955 void LoadLevelInfoOnly(int nr)
7957 setLevelFileInfo(&level.file_info, nr);
7959 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7962 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7964 SetLevelSetInfo(network_level->leveldir_identifier,
7965 network_level->file_info.nr);
7967 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7969 LoadLevel_LoadAndInit(network_level);
7972 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7976 chunk_size += putFileVersion(file, level->file_version);
7977 chunk_size += putFileVersion(file, level->game_version);
7982 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7986 chunk_size += putFile16BitBE(file, level->creation_date.year);
7987 chunk_size += putFile8Bit(file, level->creation_date.month);
7988 chunk_size += putFile8Bit(file, level->creation_date.day);
7993 #if ENABLE_HISTORIC_CHUNKS
7994 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7998 putFile8Bit(file, level->fieldx);
7999 putFile8Bit(file, level->fieldy);
8001 putFile16BitBE(file, level->time);
8002 putFile16BitBE(file, level->gems_needed);
8004 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8005 putFile8Bit(file, level->name[i]);
8007 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8008 putFile8Bit(file, level->score[i]);
8010 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8011 for (y = 0; y < 3; y++)
8012 for (x = 0; x < 3; x++)
8013 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8014 level->yamyam_content[i].e[x][y]));
8015 putFile8Bit(file, level->amoeba_speed);
8016 putFile8Bit(file, level->time_magic_wall);
8017 putFile8Bit(file, level->time_wheel);
8018 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8019 level->amoeba_content));
8020 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8021 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8022 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8023 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8025 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8027 putFile8Bit(file, (level->block_last_field ? 1 : 0));
8028 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8029 putFile32BitBE(file, level->can_move_into_acid_bits);
8030 putFile8Bit(file, level->dont_collide_with_bits);
8032 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8033 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8035 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8036 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8037 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8039 putFile8Bit(file, level->game_engine_type);
8041 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8045 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8050 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8051 chunk_size += putFile8Bit(file, level->name[i]);
8056 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8061 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8062 chunk_size += putFile8Bit(file, level->author[i]);
8067 #if ENABLE_HISTORIC_CHUNKS
8068 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8073 for (y = 0; y < level->fieldy; y++)
8074 for (x = 0; x < level->fieldx; x++)
8075 if (level->encoding_16bit_field)
8076 chunk_size += putFile16BitBE(file, level->field[x][y]);
8078 chunk_size += putFile8Bit(file, level->field[x][y]);
8084 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8089 for (y = 0; y < level->fieldy; y++)
8090 for (x = 0; x < level->fieldx; x++)
8091 chunk_size += putFile16BitBE(file, level->field[x][y]);
8096 #if ENABLE_HISTORIC_CHUNKS
8097 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8101 putFile8Bit(file, EL_YAMYAM);
8102 putFile8Bit(file, level->num_yamyam_contents);
8103 putFile8Bit(file, 0);
8104 putFile8Bit(file, 0);
8106 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8107 for (y = 0; y < 3; y++)
8108 for (x = 0; x < 3; x++)
8109 if (level->encoding_16bit_field)
8110 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8112 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8116 #if ENABLE_HISTORIC_CHUNKS
8117 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8120 int num_contents, content_xsize, content_ysize;
8121 int content_array[MAX_ELEMENT_CONTENTS][3][3];
8123 if (element == EL_YAMYAM)
8125 num_contents = level->num_yamyam_contents;
8129 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8130 for (y = 0; y < 3; y++)
8131 for (x = 0; x < 3; x++)
8132 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8134 else if (element == EL_BD_AMOEBA)
8140 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8141 for (y = 0; y < 3; y++)
8142 for (x = 0; x < 3; x++)
8143 content_array[i][x][y] = EL_EMPTY;
8144 content_array[0][0][0] = level->amoeba_content;
8148 // chunk header already written -- write empty chunk data
8149 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8151 Warn("cannot save content for element '%d'", element);
8156 putFile16BitBE(file, element);
8157 putFile8Bit(file, num_contents);
8158 putFile8Bit(file, content_xsize);
8159 putFile8Bit(file, content_ysize);
8161 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8163 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8164 for (y = 0; y < 3; y++)
8165 for (x = 0; x < 3; x++)
8166 putFile16BitBE(file, content_array[i][x][y]);
8170 #if ENABLE_HISTORIC_CHUNKS
8171 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8173 int envelope_nr = element - EL_ENVELOPE_1;
8174 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8178 chunk_size += putFile16BitBE(file, element);
8179 chunk_size += putFile16BitBE(file, envelope_len);
8180 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8181 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8183 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8184 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8186 for (i = 0; i < envelope_len; i++)
8187 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8193 #if ENABLE_HISTORIC_CHUNKS
8194 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8195 int num_changed_custom_elements)
8199 putFile16BitBE(file, num_changed_custom_elements);
8201 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8203 int element = EL_CUSTOM_START + i;
8205 struct ElementInfo *ei = &element_info[element];
8207 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8209 if (check < num_changed_custom_elements)
8211 putFile16BitBE(file, element);
8212 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8219 if (check != num_changed_custom_elements) // should not happen
8220 Warn("inconsistent number of custom element properties");
8224 #if ENABLE_HISTORIC_CHUNKS
8225 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8226 int num_changed_custom_elements)
8230 putFile16BitBE(file, num_changed_custom_elements);
8232 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8234 int element = EL_CUSTOM_START + i;
8236 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8238 if (check < num_changed_custom_elements)
8240 putFile16BitBE(file, element);
8241 putFile16BitBE(file, element_info[element].change->target_element);
8248 if (check != num_changed_custom_elements) // should not happen
8249 Warn("inconsistent number of custom target elements");
8253 #if ENABLE_HISTORIC_CHUNKS
8254 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8255 int num_changed_custom_elements)
8257 int i, j, x, y, check = 0;
8259 putFile16BitBE(file, num_changed_custom_elements);
8261 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8263 int element = EL_CUSTOM_START + i;
8264 struct ElementInfo *ei = &element_info[element];
8266 if (ei->modified_settings)
8268 if (check < num_changed_custom_elements)
8270 putFile16BitBE(file, element);
8272 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8273 putFile8Bit(file, ei->description[j]);
8275 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8277 // some free bytes for future properties and padding
8278 WriteUnusedBytesToFile(file, 7);
8280 putFile8Bit(file, ei->use_gfx_element);
8281 putFile16BitBE(file, ei->gfx_element_initial);
8283 putFile8Bit(file, ei->collect_score_initial);
8284 putFile8Bit(file, ei->collect_count_initial);
8286 putFile16BitBE(file, ei->push_delay_fixed);
8287 putFile16BitBE(file, ei->push_delay_random);
8288 putFile16BitBE(file, ei->move_delay_fixed);
8289 putFile16BitBE(file, ei->move_delay_random);
8291 putFile16BitBE(file, ei->move_pattern);
8292 putFile8Bit(file, ei->move_direction_initial);
8293 putFile8Bit(file, ei->move_stepsize);
8295 for (y = 0; y < 3; y++)
8296 for (x = 0; x < 3; x++)
8297 putFile16BitBE(file, ei->content.e[x][y]);
8299 putFile32BitBE(file, ei->change->events);
8301 putFile16BitBE(file, ei->change->target_element);
8303 putFile16BitBE(file, ei->change->delay_fixed);
8304 putFile16BitBE(file, ei->change->delay_random);
8305 putFile16BitBE(file, ei->change->delay_frames);
8307 putFile16BitBE(file, ei->change->initial_trigger_element);
8309 putFile8Bit(file, ei->change->explode);
8310 putFile8Bit(file, ei->change->use_target_content);
8311 putFile8Bit(file, ei->change->only_if_complete);
8312 putFile8Bit(file, ei->change->use_random_replace);
8314 putFile8Bit(file, ei->change->random_percentage);
8315 putFile8Bit(file, ei->change->replace_when);
8317 for (y = 0; y < 3; y++)
8318 for (x = 0; x < 3; x++)
8319 putFile16BitBE(file, ei->change->content.e[x][y]);
8321 putFile8Bit(file, ei->slippery_type);
8323 // some free bytes for future properties and padding
8324 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8331 if (check != num_changed_custom_elements) // should not happen
8332 Warn("inconsistent number of custom element properties");
8336 #if ENABLE_HISTORIC_CHUNKS
8337 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8339 struct ElementInfo *ei = &element_info[element];
8342 // ---------- custom element base property values (96 bytes) ----------------
8344 putFile16BitBE(file, element);
8346 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8347 putFile8Bit(file, ei->description[i]);
8349 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8351 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8353 putFile8Bit(file, ei->num_change_pages);
8355 putFile16BitBE(file, ei->ce_value_fixed_initial);
8356 putFile16BitBE(file, ei->ce_value_random_initial);
8357 putFile8Bit(file, ei->use_last_ce_value);
8359 putFile8Bit(file, ei->use_gfx_element);
8360 putFile16BitBE(file, ei->gfx_element_initial);
8362 putFile8Bit(file, ei->collect_score_initial);
8363 putFile8Bit(file, ei->collect_count_initial);
8365 putFile8Bit(file, ei->drop_delay_fixed);
8366 putFile8Bit(file, ei->push_delay_fixed);
8367 putFile8Bit(file, ei->drop_delay_random);
8368 putFile8Bit(file, ei->push_delay_random);
8369 putFile16BitBE(file, ei->move_delay_fixed);
8370 putFile16BitBE(file, ei->move_delay_random);
8372 // bits 0 - 15 of "move_pattern" ...
8373 putFile16BitBE(file, ei->move_pattern & 0xffff);
8374 putFile8Bit(file, ei->move_direction_initial);
8375 putFile8Bit(file, ei->move_stepsize);
8377 putFile8Bit(file, ei->slippery_type);
8379 for (y = 0; y < 3; y++)
8380 for (x = 0; x < 3; x++)
8381 putFile16BitBE(file, ei->content.e[x][y]);
8383 putFile16BitBE(file, ei->move_enter_element);
8384 putFile16BitBE(file, ei->move_leave_element);
8385 putFile8Bit(file, ei->move_leave_type);
8387 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8388 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8390 putFile8Bit(file, ei->access_direction);
8392 putFile8Bit(file, ei->explosion_delay);
8393 putFile8Bit(file, ei->ignition_delay);
8394 putFile8Bit(file, ei->explosion_type);
8396 // some free bytes for future custom property values and padding
8397 WriteUnusedBytesToFile(file, 1);
8399 // ---------- change page property values (48 bytes) ------------------------
8401 for (i = 0; i < ei->num_change_pages; i++)
8403 struct ElementChangeInfo *change = &ei->change_page[i];
8404 unsigned int event_bits;
8406 // bits 0 - 31 of "has_event[]" ...
8408 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8409 if (change->has_event[j])
8410 event_bits |= (1u << j);
8411 putFile32BitBE(file, event_bits);
8413 putFile16BitBE(file, change->target_element);
8415 putFile16BitBE(file, change->delay_fixed);
8416 putFile16BitBE(file, change->delay_random);
8417 putFile16BitBE(file, change->delay_frames);
8419 putFile16BitBE(file, change->initial_trigger_element);
8421 putFile8Bit(file, change->explode);
8422 putFile8Bit(file, change->use_target_content);
8423 putFile8Bit(file, change->only_if_complete);
8424 putFile8Bit(file, change->use_random_replace);
8426 putFile8Bit(file, change->random_percentage);
8427 putFile8Bit(file, change->replace_when);
8429 for (y = 0; y < 3; y++)
8430 for (x = 0; x < 3; x++)
8431 putFile16BitBE(file, change->target_content.e[x][y]);
8433 putFile8Bit(file, change->can_change);
8435 putFile8Bit(file, change->trigger_side);
8437 putFile8Bit(file, change->trigger_player);
8438 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8439 log_2(change->trigger_page)));
8441 putFile8Bit(file, change->has_action);
8442 putFile8Bit(file, change->action_type);
8443 putFile8Bit(file, change->action_mode);
8444 putFile16BitBE(file, change->action_arg);
8446 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8448 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8449 if (change->has_event[j])
8450 event_bits |= (1u << (j - 32));
8451 putFile8Bit(file, event_bits);
8456 #if ENABLE_HISTORIC_CHUNKS
8457 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8459 struct ElementInfo *ei = &element_info[element];
8460 struct ElementGroupInfo *group = ei->group;
8463 putFile16BitBE(file, element);
8465 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8466 putFile8Bit(file, ei->description[i]);
8468 putFile8Bit(file, group->num_elements);
8470 putFile8Bit(file, ei->use_gfx_element);
8471 putFile16BitBE(file, ei->gfx_element_initial);
8473 putFile8Bit(file, group->choice_mode);
8475 // some free bytes for future values and padding
8476 WriteUnusedBytesToFile(file, 3);
8478 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8479 putFile16BitBE(file, group->element[i]);
8483 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8484 boolean write_element)
8486 int save_type = entry->save_type;
8487 int data_type = entry->data_type;
8488 int conf_type = entry->conf_type;
8489 int byte_mask = conf_type & CONF_MASK_BYTES;
8490 int element = entry->element;
8491 int default_value = entry->default_value;
8493 boolean modified = FALSE;
8495 if (byte_mask != CONF_MASK_MULTI_BYTES)
8497 void *value_ptr = entry->value;
8498 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8501 // check if any settings have been modified before saving them
8502 if (value != default_value)
8505 // do not save if explicitly told or if unmodified default settings
8506 if ((save_type == SAVE_CONF_NEVER) ||
8507 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8511 num_bytes += putFile16BitBE(file, element);
8513 num_bytes += putFile8Bit(file, conf_type);
8514 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8515 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8516 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8519 else if (data_type == TYPE_STRING)
8521 char *default_string = entry->default_string;
8522 char *string = (char *)(entry->value);
8523 int string_length = strlen(string);
8526 // check if any settings have been modified before saving them
8527 if (!strEqual(string, default_string))
8530 // do not save if explicitly told or if unmodified default settings
8531 if ((save_type == SAVE_CONF_NEVER) ||
8532 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8536 num_bytes += putFile16BitBE(file, element);
8538 num_bytes += putFile8Bit(file, conf_type);
8539 num_bytes += putFile16BitBE(file, string_length);
8541 for (i = 0; i < string_length; i++)
8542 num_bytes += putFile8Bit(file, string[i]);
8544 else if (data_type == TYPE_ELEMENT_LIST)
8546 int *element_array = (int *)(entry->value);
8547 int num_elements = *(int *)(entry->num_entities);
8550 // check if any settings have been modified before saving them
8551 for (i = 0; i < num_elements; i++)
8552 if (element_array[i] != default_value)
8555 // do not save if explicitly told or if unmodified default settings
8556 if ((save_type == SAVE_CONF_NEVER) ||
8557 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8561 num_bytes += putFile16BitBE(file, element);
8563 num_bytes += putFile8Bit(file, conf_type);
8564 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8566 for (i = 0; i < num_elements; i++)
8567 num_bytes += putFile16BitBE(file, element_array[i]);
8569 else if (data_type == TYPE_CONTENT_LIST)
8571 struct Content *content = (struct Content *)(entry->value);
8572 int num_contents = *(int *)(entry->num_entities);
8575 // check if any settings have been modified before saving them
8576 for (i = 0; i < num_contents; i++)
8577 for (y = 0; y < 3; y++)
8578 for (x = 0; x < 3; x++)
8579 if (content[i].e[x][y] != default_value)
8582 // do not save if explicitly told or if unmodified default settings
8583 if ((save_type == SAVE_CONF_NEVER) ||
8584 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8588 num_bytes += putFile16BitBE(file, element);
8590 num_bytes += putFile8Bit(file, conf_type);
8591 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8593 for (i = 0; i < num_contents; i++)
8594 for (y = 0; y < 3; y++)
8595 for (x = 0; x < 3; x++)
8596 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8602 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8607 li = *level; // copy level data into temporary buffer
8609 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8610 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8615 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8620 li = *level; // copy level data into temporary buffer
8622 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8623 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8628 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8630 int envelope_nr = element - EL_ENVELOPE_1;
8634 chunk_size += putFile16BitBE(file, element);
8636 // copy envelope data into temporary buffer
8637 xx_envelope = level->envelope[envelope_nr];
8639 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8640 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8645 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8647 struct ElementInfo *ei = &element_info[element];
8651 chunk_size += putFile16BitBE(file, element);
8653 xx_ei = *ei; // copy element data into temporary buffer
8655 // set default description string for this specific element
8656 strcpy(xx_default_description, getDefaultElementDescription(ei));
8658 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8659 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8661 for (i = 0; i < ei->num_change_pages; i++)
8663 struct ElementChangeInfo *change = &ei->change_page[i];
8665 xx_current_change_page = i;
8667 xx_change = *change; // copy change data into temporary buffer
8670 setEventBitsFromEventFlags(change);
8672 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8673 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8680 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8682 struct ElementInfo *ei = &element_info[element];
8683 struct ElementGroupInfo *group = ei->group;
8687 chunk_size += putFile16BitBE(file, element);
8689 xx_ei = *ei; // copy element data into temporary buffer
8690 xx_group = *group; // copy group data into temporary buffer
8692 // set default description string for this specific element
8693 strcpy(xx_default_description, getDefaultElementDescription(ei));
8695 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8696 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8701 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8703 struct ElementInfo *ei = &element_info[element];
8707 chunk_size += putFile16BitBE(file, element);
8709 xx_ei = *ei; // copy element data into temporary buffer
8711 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8712 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8717 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8718 boolean save_as_template)
8724 if (!(file = fopen(filename, MODE_WRITE)))
8726 Warn("cannot save level file '%s'", filename);
8731 level->file_version = FILE_VERSION_ACTUAL;
8732 level->game_version = GAME_VERSION_ACTUAL;
8734 level->creation_date = getCurrentDate();
8736 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8737 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8739 chunk_size = SaveLevel_VERS(NULL, level);
8740 putFileChunkBE(file, "VERS", chunk_size);
8741 SaveLevel_VERS(file, level);
8743 chunk_size = SaveLevel_DATE(NULL, level);
8744 putFileChunkBE(file, "DATE", chunk_size);
8745 SaveLevel_DATE(file, level);
8747 chunk_size = SaveLevel_NAME(NULL, level);
8748 putFileChunkBE(file, "NAME", chunk_size);
8749 SaveLevel_NAME(file, level);
8751 chunk_size = SaveLevel_AUTH(NULL, level);
8752 putFileChunkBE(file, "AUTH", chunk_size);
8753 SaveLevel_AUTH(file, level);
8755 chunk_size = SaveLevel_INFO(NULL, level);
8756 putFileChunkBE(file, "INFO", chunk_size);
8757 SaveLevel_INFO(file, level);
8759 chunk_size = SaveLevel_BODY(NULL, level);
8760 putFileChunkBE(file, "BODY", chunk_size);
8761 SaveLevel_BODY(file, level);
8763 chunk_size = SaveLevel_ELEM(NULL, level);
8764 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8766 putFileChunkBE(file, "ELEM", chunk_size);
8767 SaveLevel_ELEM(file, level);
8770 for (i = 0; i < NUM_ENVELOPES; i++)
8772 int element = EL_ENVELOPE_1 + i;
8774 chunk_size = SaveLevel_NOTE(NULL, level, element);
8775 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8777 putFileChunkBE(file, "NOTE", chunk_size);
8778 SaveLevel_NOTE(file, level, element);
8782 // if not using template level, check for non-default custom/group elements
8783 if (!level->use_custom_template || save_as_template)
8785 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8787 int element = EL_CUSTOM_START + i;
8789 chunk_size = SaveLevel_CUSX(NULL, level, element);
8790 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8792 putFileChunkBE(file, "CUSX", chunk_size);
8793 SaveLevel_CUSX(file, level, element);
8797 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8799 int element = EL_GROUP_START + i;
8801 chunk_size = SaveLevel_GRPX(NULL, level, element);
8802 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8804 putFileChunkBE(file, "GRPX", chunk_size);
8805 SaveLevel_GRPX(file, level, element);
8809 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8811 int element = GET_EMPTY_ELEMENT(i);
8813 chunk_size = SaveLevel_EMPX(NULL, level, element);
8814 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8816 putFileChunkBE(file, "EMPX", chunk_size);
8817 SaveLevel_EMPX(file, level, element);
8824 SetFilePermissions(filename, PERMS_PRIVATE);
8827 void SaveLevel(int nr)
8829 char *filename = getDefaultLevelFilename(nr);
8831 SaveLevelFromFilename(&level, filename, FALSE);
8834 void SaveLevelTemplate(void)
8836 char *filename = getLocalLevelTemplateFilename();
8838 SaveLevelFromFilename(&level, filename, TRUE);
8841 boolean SaveLevelChecked(int nr)
8843 char *filename = getDefaultLevelFilename(nr);
8844 boolean new_level = !fileExists(filename);
8845 boolean level_saved = FALSE;
8847 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8852 Request("Level saved!", REQ_CONFIRM);
8860 void DumpLevel(struct LevelInfo *level)
8862 if (level->no_level_file || level->no_valid_file)
8864 Warn("cannot dump -- no valid level file found");
8870 Print("Level xxx (file version %08d, game version %08d)\n",
8871 level->file_version, level->game_version);
8874 Print("Level author: '%s'\n", level->author);
8875 Print("Level title: '%s'\n", level->name);
8877 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8879 Print("Level time: %d seconds\n", level->time);
8880 Print("Gems needed: %d\n", level->gems_needed);
8882 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8883 Print("Time for wheel: %d seconds\n", level->time_wheel);
8884 Print("Time for light: %d seconds\n", level->time_light);
8885 Print("Time for timegate: %d seconds\n", level->time_timegate);
8887 Print("Amoeba speed: %d\n", level->amoeba_speed);
8890 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8891 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8892 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8893 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8894 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8895 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8901 for (i = 0; i < NUM_ENVELOPES; i++)
8903 char *text = level->envelope[i].text;
8904 int text_len = strlen(text);
8905 boolean has_text = FALSE;
8907 for (j = 0; j < text_len; j++)
8908 if (text[j] != ' ' && text[j] != '\n')
8914 Print("Envelope %d:\n'%s'\n", i + 1, text);
8922 void DumpLevels(void)
8924 static LevelDirTree *dumplevel_leveldir = NULL;
8926 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8927 global.dumplevel_leveldir);
8929 if (dumplevel_leveldir == NULL)
8930 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8932 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8933 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8934 Fail("no such level number: %d", global.dumplevel_level_nr);
8936 leveldir_current = dumplevel_leveldir;
8938 LoadLevel(global.dumplevel_level_nr);
8945 // ============================================================================
8946 // tape file functions
8947 // ============================================================================
8949 static void setTapeInfoToDefaults(void)
8953 // always start with reliable default values (empty tape)
8956 // default values (also for pre-1.2 tapes) with only the first player
8957 tape.player_participates[0] = TRUE;
8958 for (i = 1; i < MAX_PLAYERS; i++)
8959 tape.player_participates[i] = FALSE;
8961 // at least one (default: the first) player participates in every tape
8962 tape.num_participating_players = 1;
8964 tape.property_bits = TAPE_PROPERTY_NONE;
8966 tape.level_nr = level_nr;
8968 tape.changed = FALSE;
8969 tape.solved = FALSE;
8971 tape.recording = FALSE;
8972 tape.playing = FALSE;
8973 tape.pausing = FALSE;
8975 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8976 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8978 tape.no_info_chunk = TRUE;
8979 tape.no_valid_file = FALSE;
8982 static int getTapePosSize(struct TapeInfo *tape)
8984 int tape_pos_size = 0;
8986 if (tape->use_key_actions)
8987 tape_pos_size += tape->num_participating_players;
8989 if (tape->use_mouse_actions)
8990 tape_pos_size += 3; // x and y position and mouse button mask
8992 tape_pos_size += 1; // tape action delay value
8994 return tape_pos_size;
8997 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8999 tape->use_key_actions = FALSE;
9000 tape->use_mouse_actions = FALSE;
9002 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9003 tape->use_key_actions = TRUE;
9005 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9006 tape->use_mouse_actions = TRUE;
9009 static int getTapeActionValue(struct TapeInfo *tape)
9011 return (tape->use_key_actions &&
9012 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9013 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
9014 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9015 TAPE_ACTIONS_DEFAULT);
9018 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9020 tape->file_version = getFileVersion(file);
9021 tape->game_version = getFileVersion(file);
9026 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9030 tape->random_seed = getFile32BitBE(file);
9031 tape->date = getFile32BitBE(file);
9032 tape->length = getFile32BitBE(file);
9034 // read header fields that are new since version 1.2
9035 if (tape->file_version >= FILE_VERSION_1_2)
9037 byte store_participating_players = getFile8Bit(file);
9040 // since version 1.2, tapes store which players participate in the tape
9041 tape->num_participating_players = 0;
9042 for (i = 0; i < MAX_PLAYERS; i++)
9044 tape->player_participates[i] = FALSE;
9046 if (store_participating_players & (1 << i))
9048 tape->player_participates[i] = TRUE;
9049 tape->num_participating_players++;
9053 setTapeActionFlags(tape, getFile8Bit(file));
9055 tape->property_bits = getFile8Bit(file);
9056 tape->solved = getFile8Bit(file);
9058 engine_version = getFileVersion(file);
9059 if (engine_version > 0)
9060 tape->engine_version = engine_version;
9062 tape->engine_version = tape->game_version;
9068 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9070 tape->scr_fieldx = getFile8Bit(file);
9071 tape->scr_fieldy = getFile8Bit(file);
9076 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9078 char *level_identifier = NULL;
9079 int level_identifier_size;
9082 tape->no_info_chunk = FALSE;
9084 level_identifier_size = getFile16BitBE(file);
9086 level_identifier = checked_malloc(level_identifier_size);
9088 for (i = 0; i < level_identifier_size; i++)
9089 level_identifier[i] = getFile8Bit(file);
9091 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9092 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9094 checked_free(level_identifier);
9096 tape->level_nr = getFile16BitBE(file);
9098 chunk_size = 2 + level_identifier_size + 2;
9103 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9106 int tape_pos_size = getTapePosSize(tape);
9107 int chunk_size_expected = tape_pos_size * tape->length;
9109 if (chunk_size_expected != chunk_size)
9111 ReadUnusedBytesFromFile(file, chunk_size);
9112 return chunk_size_expected;
9115 for (i = 0; i < tape->length; i++)
9117 if (i >= MAX_TAPE_LEN)
9119 Warn("tape truncated -- size exceeds maximum tape size %d",
9122 // tape too large; read and ignore remaining tape data from this chunk
9123 for (;i < tape->length; i++)
9124 ReadUnusedBytesFromFile(file, tape_pos_size);
9129 if (tape->use_key_actions)
9131 for (j = 0; j < MAX_PLAYERS; j++)
9133 tape->pos[i].action[j] = MV_NONE;
9135 if (tape->player_participates[j])
9136 tape->pos[i].action[j] = getFile8Bit(file);
9140 if (tape->use_mouse_actions)
9142 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9143 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9144 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9147 tape->pos[i].delay = getFile8Bit(file);
9149 if (tape->file_version == FILE_VERSION_1_0)
9151 // eliminate possible diagonal moves in old tapes
9152 // this is only for backward compatibility
9154 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9155 byte action = tape->pos[i].action[0];
9156 int k, num_moves = 0;
9158 for (k = 0; k < 4; k++)
9160 if (action & joy_dir[k])
9162 tape->pos[i + num_moves].action[0] = joy_dir[k];
9164 tape->pos[i + num_moves].delay = 0;
9173 tape->length += num_moves;
9176 else if (tape->file_version < FILE_VERSION_2_0)
9178 // convert pre-2.0 tapes to new tape format
9180 if (tape->pos[i].delay > 1)
9183 tape->pos[i + 1] = tape->pos[i];
9184 tape->pos[i + 1].delay = 1;
9187 for (j = 0; j < MAX_PLAYERS; j++)
9188 tape->pos[i].action[j] = MV_NONE;
9189 tape->pos[i].delay--;
9196 if (checkEndOfFile(file))
9200 if (i != tape->length)
9201 chunk_size = tape_pos_size * i;
9206 static void LoadTape_SokobanSolution(char *filename)
9209 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9211 if (!(file = openFile(filename, MODE_READ)))
9213 tape.no_valid_file = TRUE;
9218 while (!checkEndOfFile(file))
9220 unsigned char c = getByteFromFile(file);
9222 if (checkEndOfFile(file))
9229 tape.pos[tape.length].action[0] = MV_UP;
9230 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9236 tape.pos[tape.length].action[0] = MV_DOWN;
9237 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9243 tape.pos[tape.length].action[0] = MV_LEFT;
9244 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9250 tape.pos[tape.length].action[0] = MV_RIGHT;
9251 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9259 // ignore white-space characters
9263 tape.no_valid_file = TRUE;
9265 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9273 if (tape.no_valid_file)
9276 tape.length_frames = GetTapeLengthFrames();
9277 tape.length_seconds = GetTapeLengthSeconds();
9280 void LoadTapeFromFilename(char *filename)
9282 char cookie[MAX_LINE_LEN];
9283 char chunk_name[CHUNK_ID_LEN + 1];
9287 // always start with reliable default values
9288 setTapeInfoToDefaults();
9290 if (strSuffix(filename, ".sln"))
9292 LoadTape_SokobanSolution(filename);
9297 if (!(file = openFile(filename, MODE_READ)))
9299 tape.no_valid_file = TRUE;
9304 getFileChunkBE(file, chunk_name, NULL);
9305 if (strEqual(chunk_name, "RND1"))
9307 getFile32BitBE(file); // not used
9309 getFileChunkBE(file, chunk_name, NULL);
9310 if (!strEqual(chunk_name, "TAPE"))
9312 tape.no_valid_file = TRUE;
9314 Warn("unknown format of tape file '%s'", filename);
9321 else // check for pre-2.0 file format with cookie string
9323 strcpy(cookie, chunk_name);
9324 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9326 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9327 cookie[strlen(cookie) - 1] = '\0';
9329 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9331 tape.no_valid_file = TRUE;
9333 Warn("unknown format of tape file '%s'", filename);
9340 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9342 tape.no_valid_file = TRUE;
9344 Warn("unsupported version of tape file '%s'", filename);
9351 // pre-2.0 tape files have no game version, so use file version here
9352 tape.game_version = tape.file_version;
9355 if (tape.file_version < FILE_VERSION_1_2)
9357 // tape files from versions before 1.2.0 without chunk structure
9358 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9359 LoadTape_BODY(file, 2 * tape.length, &tape);
9367 int (*loader)(File *, int, struct TapeInfo *);
9371 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9372 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9373 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9374 { "INFO", -1, LoadTape_INFO },
9375 { "BODY", -1, LoadTape_BODY },
9379 while (getFileChunkBE(file, chunk_name, &chunk_size))
9383 while (chunk_info[i].name != NULL &&
9384 !strEqual(chunk_name, chunk_info[i].name))
9387 if (chunk_info[i].name == NULL)
9389 Warn("unknown chunk '%s' in tape file '%s'",
9390 chunk_name, filename);
9392 ReadUnusedBytesFromFile(file, chunk_size);
9394 else if (chunk_info[i].size != -1 &&
9395 chunk_info[i].size != chunk_size)
9397 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9398 chunk_size, chunk_name, filename);
9400 ReadUnusedBytesFromFile(file, chunk_size);
9404 // call function to load this tape chunk
9405 int chunk_size_expected =
9406 (chunk_info[i].loader)(file, chunk_size, &tape);
9408 // the size of some chunks cannot be checked before reading other
9409 // chunks first (like "HEAD" and "BODY") that contain some header
9410 // information, so check them here
9411 if (chunk_size_expected != chunk_size)
9413 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9414 chunk_size, chunk_name, filename);
9422 tape.length_frames = GetTapeLengthFrames();
9423 tape.length_seconds = GetTapeLengthSeconds();
9426 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9428 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9430 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9431 tape.engine_version);
9435 void LoadTape(int nr)
9437 char *filename = getTapeFilename(nr);
9439 LoadTapeFromFilename(filename);
9442 void LoadSolutionTape(int nr)
9444 char *filename = getSolutionTapeFilename(nr);
9446 LoadTapeFromFilename(filename);
9448 if (TAPE_IS_EMPTY(tape))
9450 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9451 level.native_bd_level->replay != NULL)
9452 CopyNativeTape_BD_to_RND(&level);
9453 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9454 level.native_sp_level->demo.is_available)
9455 CopyNativeTape_SP_to_RND(&level);
9459 void LoadScoreTape(char *score_tape_basename, int nr)
9461 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9463 LoadTapeFromFilename(filename);
9466 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9468 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9470 LoadTapeFromFilename(filename);
9473 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9475 // chunk required for team mode tapes with non-default screen size
9476 return (tape->num_participating_players > 1 &&
9477 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9478 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9481 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9483 putFileVersion(file, tape->file_version);
9484 putFileVersion(file, tape->game_version);
9487 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9490 byte store_participating_players = 0;
9492 // set bits for participating players for compact storage
9493 for (i = 0; i < MAX_PLAYERS; i++)
9494 if (tape->player_participates[i])
9495 store_participating_players |= (1 << i);
9497 putFile32BitBE(file, tape->random_seed);
9498 putFile32BitBE(file, tape->date);
9499 putFile32BitBE(file, tape->length);
9501 putFile8Bit(file, store_participating_players);
9503 putFile8Bit(file, getTapeActionValue(tape));
9505 putFile8Bit(file, tape->property_bits);
9506 putFile8Bit(file, tape->solved);
9508 putFileVersion(file, tape->engine_version);
9511 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9513 putFile8Bit(file, tape->scr_fieldx);
9514 putFile8Bit(file, tape->scr_fieldy);
9517 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9519 int level_identifier_size = strlen(tape->level_identifier) + 1;
9522 putFile16BitBE(file, level_identifier_size);
9524 for (i = 0; i < level_identifier_size; i++)
9525 putFile8Bit(file, tape->level_identifier[i]);
9527 putFile16BitBE(file, tape->level_nr);
9530 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9534 for (i = 0; i < tape->length; i++)
9536 if (tape->use_key_actions)
9538 for (j = 0; j < MAX_PLAYERS; j++)
9539 if (tape->player_participates[j])
9540 putFile8Bit(file, tape->pos[i].action[j]);
9543 if (tape->use_mouse_actions)
9545 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9546 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9547 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9550 putFile8Bit(file, tape->pos[i].delay);
9554 void SaveTapeToFilename(char *filename)
9558 int info_chunk_size;
9559 int body_chunk_size;
9561 if (!(file = fopen(filename, MODE_WRITE)))
9563 Warn("cannot save level recording file '%s'", filename);
9568 tape_pos_size = getTapePosSize(&tape);
9570 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9571 body_chunk_size = tape_pos_size * tape.length;
9573 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9574 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9576 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9577 SaveTape_VERS(file, &tape);
9579 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9580 SaveTape_HEAD(file, &tape);
9582 if (checkSaveTape_SCRN(&tape))
9584 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9585 SaveTape_SCRN(file, &tape);
9588 putFileChunkBE(file, "INFO", info_chunk_size);
9589 SaveTape_INFO(file, &tape);
9591 putFileChunkBE(file, "BODY", body_chunk_size);
9592 SaveTape_BODY(file, &tape);
9596 SetFilePermissions(filename, PERMS_PRIVATE);
9599 static void SaveTapeExt(char *filename)
9603 tape.file_version = FILE_VERSION_ACTUAL;
9604 tape.game_version = GAME_VERSION_ACTUAL;
9606 tape.num_participating_players = 0;
9608 // count number of participating players
9609 for (i = 0; i < MAX_PLAYERS; i++)
9610 if (tape.player_participates[i])
9611 tape.num_participating_players++;
9613 SaveTapeToFilename(filename);
9615 tape.changed = FALSE;
9618 void SaveTape(int nr)
9620 char *filename = getTapeFilename(nr);
9622 InitTapeDirectory(leveldir_current->subdir);
9624 SaveTapeExt(filename);
9627 void SaveScoreTape(int nr)
9629 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9631 // used instead of "leveldir_current->subdir" (for network games)
9632 InitScoreTapeDirectory(levelset.identifier, nr);
9634 SaveTapeExt(filename);
9637 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9638 unsigned int req_state_added)
9640 char *filename = getTapeFilename(nr);
9641 boolean new_tape = !fileExists(filename);
9642 boolean tape_saved = FALSE;
9644 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9649 Request(msg_saved, REQ_CONFIRM | req_state_added);
9657 boolean SaveTapeChecked(int nr)
9659 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9662 boolean SaveTapeChecked_LevelSolved(int nr)
9664 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9665 "Level solved! Tape saved!", REQ_STAY_OPEN);
9668 void DumpTape(struct TapeInfo *tape)
9670 int tape_frame_counter;
9673 if (tape->no_valid_file)
9675 Warn("cannot dump -- no valid tape file found");
9682 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9683 tape->level_nr, tape->file_version, tape->game_version);
9684 Print(" (effective engine version %08d)\n",
9685 tape->engine_version);
9686 Print("Level series identifier: '%s'\n", tape->level_identifier);
9688 Print("Solution tape: %s\n",
9689 tape->solved ? "yes" :
9690 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9692 Print("Special tape properties: ");
9693 if (tape->property_bits == TAPE_PROPERTY_NONE)
9695 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9696 Print("[em_random_bug]");
9697 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9698 Print("[game_speed]");
9699 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9701 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9702 Print("[single_step]");
9703 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9704 Print("[snapshot]");
9705 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9706 Print("[replayed]");
9707 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9708 Print("[tas_keys]");
9709 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9710 Print("[small_graphics]");
9713 int year2 = tape->date / 10000;
9714 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9715 int month_index_raw = (tape->date / 100) % 100;
9716 int month_index = month_index_raw % 12; // prevent invalid index
9717 int month = month_index + 1;
9718 int day = tape->date % 100;
9720 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9724 tape_frame_counter = 0;
9726 for (i = 0; i < tape->length; i++)
9728 if (i >= MAX_TAPE_LEN)
9733 for (j = 0; j < MAX_PLAYERS; j++)
9735 if (tape->player_participates[j])
9737 int action = tape->pos[i].action[j];
9739 Print("%d:%02x ", j, action);
9740 Print("[%c%c%c%c|%c%c] - ",
9741 (action & JOY_LEFT ? '<' : ' '),
9742 (action & JOY_RIGHT ? '>' : ' '),
9743 (action & JOY_UP ? '^' : ' '),
9744 (action & JOY_DOWN ? 'v' : ' '),
9745 (action & JOY_BUTTON_1 ? '1' : ' '),
9746 (action & JOY_BUTTON_2 ? '2' : ' '));
9750 Print("(%03d) ", tape->pos[i].delay);
9751 Print("[%05d]\n", tape_frame_counter);
9753 tape_frame_counter += tape->pos[i].delay;
9759 void DumpTapes(void)
9761 static LevelDirTree *dumptape_leveldir = NULL;
9763 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9764 global.dumptape_leveldir);
9766 if (dumptape_leveldir == NULL)
9767 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9769 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9770 global.dumptape_level_nr > dumptape_leveldir->last_level)
9771 Fail("no such level number: %d", global.dumptape_level_nr);
9773 leveldir_current = dumptape_leveldir;
9775 if (options.mytapes)
9776 LoadTape(global.dumptape_level_nr);
9778 LoadSolutionTape(global.dumptape_level_nr);
9786 // ============================================================================
9787 // score file functions
9788 // ============================================================================
9790 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9794 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9796 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9797 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9798 scores->entry[i].score = 0;
9799 scores->entry[i].time = 0;
9801 scores->entry[i].id = -1;
9802 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9803 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9804 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9805 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9806 strcpy(scores->entry[i].country_code, "??");
9809 scores->num_entries = 0;
9810 scores->last_added = -1;
9811 scores->last_added_local = -1;
9813 scores->updated = FALSE;
9814 scores->uploaded = FALSE;
9815 scores->tape_downloaded = FALSE;
9816 scores->force_last_added = FALSE;
9818 // The following values are intentionally not reset here:
9822 // - continue_playing
9823 // - continue_on_return
9826 static void setScoreInfoToDefaults(void)
9828 setScoreInfoToDefaultsExt(&scores);
9831 static void setServerScoreInfoToDefaults(void)
9833 setScoreInfoToDefaultsExt(&server_scores);
9836 static void LoadScore_OLD(int nr)
9839 char *filename = getScoreFilename(nr);
9840 char cookie[MAX_LINE_LEN];
9841 char line[MAX_LINE_LEN];
9845 if (!(file = fopen(filename, MODE_READ)))
9848 // check file identifier
9849 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9851 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9852 cookie[strlen(cookie) - 1] = '\0';
9854 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9856 Warn("unknown format of score file '%s'", filename);
9863 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9865 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9866 Warn("fscanf() failed; %s", strerror(errno));
9868 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9871 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9872 line[strlen(line) - 1] = '\0';
9874 for (line_ptr = line; *line_ptr; line_ptr++)
9876 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9878 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9879 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9888 static void ConvertScore_OLD(void)
9890 // only convert score to time for levels that rate playing time over score
9891 if (!level.rate_time_over_score)
9894 // convert old score to playing time for score-less levels (like Supaplex)
9895 int time_final_max = 999;
9898 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9900 int score = scores.entry[i].score;
9902 if (score > 0 && score < time_final_max)
9903 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9907 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9909 scores->file_version = getFileVersion(file);
9910 scores->game_version = getFileVersion(file);
9915 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9917 char *level_identifier = NULL;
9918 int level_identifier_size;
9921 level_identifier_size = getFile16BitBE(file);
9923 level_identifier = checked_malloc(level_identifier_size);
9925 for (i = 0; i < level_identifier_size; i++)
9926 level_identifier[i] = getFile8Bit(file);
9928 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9929 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9931 checked_free(level_identifier);
9933 scores->level_nr = getFile16BitBE(file);
9934 scores->num_entries = getFile16BitBE(file);
9936 chunk_size = 2 + level_identifier_size + 2 + 2;
9941 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9945 for (i = 0; i < scores->num_entries; i++)
9947 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9948 scores->entry[i].name[j] = getFile8Bit(file);
9950 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9953 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9958 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9962 for (i = 0; i < scores->num_entries; i++)
9963 scores->entry[i].score = getFile16BitBE(file);
9965 chunk_size = scores->num_entries * 2;
9970 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9974 for (i = 0; i < scores->num_entries; i++)
9975 scores->entry[i].score = getFile32BitBE(file);
9977 chunk_size = scores->num_entries * 4;
9982 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9986 for (i = 0; i < scores->num_entries; i++)
9987 scores->entry[i].time = getFile32BitBE(file);
9989 chunk_size = scores->num_entries * 4;
9994 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9998 for (i = 0; i < scores->num_entries; i++)
10000 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10001 scores->entry[i].tape_basename[j] = getFile8Bit(file);
10003 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10006 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10011 void LoadScore(int nr)
10013 char *filename = getScoreFilename(nr);
10014 char cookie[MAX_LINE_LEN];
10015 char chunk_name[CHUNK_ID_LEN + 1];
10017 boolean old_score_file_format = FALSE;
10020 // always start with reliable default values
10021 setScoreInfoToDefaults();
10023 if (!(file = openFile(filename, MODE_READ)))
10026 getFileChunkBE(file, chunk_name, NULL);
10027 if (strEqual(chunk_name, "RND1"))
10029 getFile32BitBE(file); // not used
10031 getFileChunkBE(file, chunk_name, NULL);
10032 if (!strEqual(chunk_name, "SCOR"))
10034 Warn("unknown format of score file '%s'", filename);
10041 else // check for old file format with cookie string
10043 strcpy(cookie, chunk_name);
10044 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10046 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10047 cookie[strlen(cookie) - 1] = '\0';
10049 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10051 Warn("unknown format of score file '%s'", filename);
10058 old_score_file_format = TRUE;
10061 if (old_score_file_format)
10063 // score files from versions before 4.2.4.0 without chunk structure
10066 // convert score to time, if possible (mainly for Supaplex levels)
10067 ConvertScore_OLD();
10075 int (*loader)(File *, int, struct ScoreInfo *);
10079 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
10080 { "INFO", -1, LoadScore_INFO },
10081 { "NAME", -1, LoadScore_NAME },
10082 { "SCOR", -1, LoadScore_SCOR },
10083 { "SC4R", -1, LoadScore_SC4R },
10084 { "TIME", -1, LoadScore_TIME },
10085 { "TAPE", -1, LoadScore_TAPE },
10090 while (getFileChunkBE(file, chunk_name, &chunk_size))
10094 while (chunk_info[i].name != NULL &&
10095 !strEqual(chunk_name, chunk_info[i].name))
10098 if (chunk_info[i].name == NULL)
10100 Warn("unknown chunk '%s' in score file '%s'",
10101 chunk_name, filename);
10103 ReadUnusedBytesFromFile(file, chunk_size);
10105 else if (chunk_info[i].size != -1 &&
10106 chunk_info[i].size != chunk_size)
10108 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10109 chunk_size, chunk_name, filename);
10111 ReadUnusedBytesFromFile(file, chunk_size);
10115 // call function to load this score chunk
10116 int chunk_size_expected =
10117 (chunk_info[i].loader)(file, chunk_size, &scores);
10119 // the size of some chunks cannot be checked before reading other
10120 // chunks first (like "HEAD" and "BODY") that contain some header
10121 // information, so check them here
10122 if (chunk_size_expected != chunk_size)
10124 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10125 chunk_size, chunk_name, filename);
10134 #if ENABLE_HISTORIC_CHUNKS
10135 void SaveScore_OLD(int nr)
10138 char *filename = getScoreFilename(nr);
10141 // used instead of "leveldir_current->subdir" (for network games)
10142 InitScoreDirectory(levelset.identifier);
10144 if (!(file = fopen(filename, MODE_WRITE)))
10146 Warn("cannot save score for level %d", nr);
10151 fprintf(file, "%s\n\n", SCORE_COOKIE);
10153 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10154 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10158 SetFilePermissions(filename, PERMS_PRIVATE);
10162 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10164 putFileVersion(file, scores->file_version);
10165 putFileVersion(file, scores->game_version);
10168 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10170 int level_identifier_size = strlen(scores->level_identifier) + 1;
10173 putFile16BitBE(file, level_identifier_size);
10175 for (i = 0; i < level_identifier_size; i++)
10176 putFile8Bit(file, scores->level_identifier[i]);
10178 putFile16BitBE(file, scores->level_nr);
10179 putFile16BitBE(file, scores->num_entries);
10182 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10186 for (i = 0; i < scores->num_entries; i++)
10188 int name_size = strlen(scores->entry[i].name);
10190 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10191 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10195 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10199 for (i = 0; i < scores->num_entries; i++)
10200 putFile16BitBE(file, scores->entry[i].score);
10203 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10207 for (i = 0; i < scores->num_entries; i++)
10208 putFile32BitBE(file, scores->entry[i].score);
10211 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10215 for (i = 0; i < scores->num_entries; i++)
10216 putFile32BitBE(file, scores->entry[i].time);
10219 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10223 for (i = 0; i < scores->num_entries; i++)
10225 int size = strlen(scores->entry[i].tape_basename);
10227 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10228 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10232 static void SaveScoreToFilename(char *filename)
10235 int info_chunk_size;
10236 int name_chunk_size;
10237 int scor_chunk_size;
10238 int sc4r_chunk_size;
10239 int time_chunk_size;
10240 int tape_chunk_size;
10241 boolean has_large_score_values;
10244 if (!(file = fopen(filename, MODE_WRITE)))
10246 Warn("cannot save score file '%s'", filename);
10251 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10252 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10253 scor_chunk_size = scores.num_entries * 2;
10254 sc4r_chunk_size = scores.num_entries * 4;
10255 time_chunk_size = scores.num_entries * 4;
10256 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10258 has_large_score_values = FALSE;
10259 for (i = 0; i < scores.num_entries; i++)
10260 if (scores.entry[i].score > 0xffff)
10261 has_large_score_values = TRUE;
10263 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10264 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10266 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10267 SaveScore_VERS(file, &scores);
10269 putFileChunkBE(file, "INFO", info_chunk_size);
10270 SaveScore_INFO(file, &scores);
10272 putFileChunkBE(file, "NAME", name_chunk_size);
10273 SaveScore_NAME(file, &scores);
10275 if (has_large_score_values)
10277 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10278 SaveScore_SC4R(file, &scores);
10282 putFileChunkBE(file, "SCOR", scor_chunk_size);
10283 SaveScore_SCOR(file, &scores);
10286 putFileChunkBE(file, "TIME", time_chunk_size);
10287 SaveScore_TIME(file, &scores);
10289 putFileChunkBE(file, "TAPE", tape_chunk_size);
10290 SaveScore_TAPE(file, &scores);
10294 SetFilePermissions(filename, PERMS_PRIVATE);
10297 void SaveScore(int nr)
10299 char *filename = getScoreFilename(nr);
10302 // used instead of "leveldir_current->subdir" (for network games)
10303 InitScoreDirectory(levelset.identifier);
10305 scores.file_version = FILE_VERSION_ACTUAL;
10306 scores.game_version = GAME_VERSION_ACTUAL;
10308 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10309 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10310 scores.level_nr = level_nr;
10312 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10313 if (scores.entry[i].score == 0 &&
10314 scores.entry[i].time == 0 &&
10315 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10318 scores.num_entries = i;
10320 if (scores.num_entries == 0)
10323 SaveScoreToFilename(filename);
10326 static void LoadServerScoreFromCache(int nr)
10328 struct ScoreEntry score_entry;
10337 { &score_entry.score, FALSE, 0 },
10338 { &score_entry.time, FALSE, 0 },
10339 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10340 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10341 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10342 { &score_entry.id, FALSE, 0 },
10343 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10344 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10345 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10346 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10350 char *filename = getScoreCacheFilename(nr);
10351 SetupFileHash *score_hash = loadSetupFileHash(filename);
10354 server_scores.num_entries = 0;
10356 if (score_hash == NULL)
10359 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10361 score_entry = server_scores.entry[i];
10363 for (j = 0; score_mapping[j].value != NULL; j++)
10367 sprintf(token, "%02d.%d", i, j);
10369 char *value = getHashEntry(score_hash, token);
10374 if (score_mapping[j].is_string)
10376 char *score_value = (char *)score_mapping[j].value;
10377 int value_size = score_mapping[j].string_size;
10379 strncpy(score_value, value, value_size);
10380 score_value[value_size] = '\0';
10384 int *score_value = (int *)score_mapping[j].value;
10386 *score_value = atoi(value);
10389 server_scores.num_entries = i + 1;
10392 server_scores.entry[i] = score_entry;
10395 freeSetupFileHash(score_hash);
10398 void LoadServerScore(int nr, boolean download_score)
10400 if (!setup.use_api_server)
10403 // always start with reliable default values
10404 setServerScoreInfoToDefaults();
10406 // 1st step: load server scores from cache file (which may not exist)
10407 // (this should prevent reading it while the thread is writing to it)
10408 LoadServerScoreFromCache(nr);
10410 if (download_score && runtime.use_api_server)
10412 // 2nd step: download server scores from score server to cache file
10413 // (as thread, as it might time out if the server is not reachable)
10414 ApiGetScoreAsThread(nr);
10418 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10420 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10422 // if score tape not uploaded, ask for uploading missing tapes later
10423 if (!setup.has_remaining_tapes)
10424 setup.ask_for_remaining_tapes = TRUE;
10426 setup.provide_uploading_tapes = TRUE;
10427 setup.has_remaining_tapes = TRUE;
10429 SaveSetup_ServerSetup();
10432 void SaveServerScore(int nr, boolean tape_saved)
10434 if (!runtime.use_api_server)
10436 PrepareScoreTapesForUpload(leveldir_current->subdir);
10441 ApiAddScoreAsThread(nr, tape_saved, NULL);
10444 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10445 char *score_tape_filename)
10447 if (!runtime.use_api_server)
10450 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10453 void LoadLocalAndServerScore(int nr, boolean download_score)
10455 int last_added_local = scores.last_added_local;
10456 boolean force_last_added = scores.force_last_added;
10458 // needed if only showing server scores
10459 setScoreInfoToDefaults();
10461 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10464 // restore last added local score entry (before merging server scores)
10465 scores.last_added = scores.last_added_local = last_added_local;
10467 if (setup.use_api_server &&
10468 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10470 // load server scores from cache file and trigger update from server
10471 LoadServerScore(nr, download_score);
10473 // merge local scores with scores from server
10474 MergeServerScore();
10477 if (force_last_added)
10478 scores.force_last_added = force_last_added;
10482 // ============================================================================
10483 // setup file functions
10484 // ============================================================================
10486 #define TOKEN_STR_PLAYER_PREFIX "player_"
10489 static struct TokenInfo global_setup_tokens[] =
10493 &setup.player_name, "player_name"
10497 &setup.multiple_users, "multiple_users"
10501 &setup.sound, "sound"
10505 &setup.sound_loops, "repeating_sound_loops"
10509 &setup.sound_music, "background_music"
10513 &setup.sound_simple, "simple_sound_effects"
10517 &setup.toons, "toons"
10521 &setup.global_animations, "global_animations"
10525 &setup.scroll_delay, "scroll_delay"
10529 &setup.forced_scroll_delay, "forced_scroll_delay"
10533 &setup.scroll_delay_value, "scroll_delay_value"
10537 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10541 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10545 &setup.fade_screens, "fade_screens"
10549 &setup.autorecord, "automatic_tape_recording"
10553 &setup.autorecord_after_replay, "autorecord_after_replay"
10557 &setup.auto_pause_on_start, "auto_pause_on_start"
10561 &setup.show_titlescreen, "show_titlescreen"
10565 &setup.quick_doors, "quick_doors"
10569 &setup.team_mode, "team_mode"
10573 &setup.handicap, "handicap"
10577 &setup.skip_levels, "skip_levels"
10581 &setup.increment_levels, "increment_levels"
10585 &setup.auto_play_next_level, "auto_play_next_level"
10589 &setup.count_score_after_game, "count_score_after_game"
10593 &setup.show_scores_after_game, "show_scores_after_game"
10597 &setup.time_limit, "time_limit"
10601 &setup.fullscreen, "fullscreen"
10605 &setup.window_scaling_percent, "window_scaling_percent"
10609 &setup.window_scaling_quality, "window_scaling_quality"
10613 &setup.screen_rendering_mode, "screen_rendering_mode"
10617 &setup.vsync_mode, "vsync_mode"
10621 &setup.ask_on_escape, "ask_on_escape"
10625 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10629 &setup.ask_on_game_over, "ask_on_game_over"
10633 &setup.ask_on_quit_game, "ask_on_quit_game"
10637 &setup.ask_on_quit_program, "ask_on_quit_program"
10641 &setup.quick_switch, "quick_player_switch"
10645 &setup.input_on_focus, "input_on_focus"
10649 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10653 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10657 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10661 &setup.game_speed_extended, "game_speed_extended"
10665 &setup.game_frame_delay, "game_frame_delay"
10669 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10673 &setup.bd_skip_hatching, "bd_skip_hatching"
10677 &setup.bd_scroll_delay, "bd_scroll_delay"
10681 &setup.bd_smooth_movements, "bd_smooth_movements"
10685 &setup.sp_show_border_elements, "sp_show_border_elements"
10689 &setup.small_game_graphics, "small_game_graphics"
10693 &setup.show_load_save_buttons, "show_load_save_buttons"
10697 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10701 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10705 &setup.graphics_set, "graphics_set"
10709 &setup.sounds_set, "sounds_set"
10713 &setup.music_set, "music_set"
10717 &setup.override_level_graphics, "override_level_graphics"
10721 &setup.override_level_sounds, "override_level_sounds"
10725 &setup.override_level_music, "override_level_music"
10729 &setup.volume_simple, "volume_simple"
10733 &setup.volume_loops, "volume_loops"
10737 &setup.volume_music, "volume_music"
10741 &setup.network_mode, "network_mode"
10745 &setup.network_player_nr, "network_player"
10749 &setup.network_server_hostname, "network_server_hostname"
10753 &setup.touch.control_type, "touch.control_type"
10757 &setup.touch.move_distance, "touch.move_distance"
10761 &setup.touch.drop_distance, "touch.drop_distance"
10765 &setup.touch.transparency, "touch.transparency"
10769 &setup.touch.draw_outlined, "touch.draw_outlined"
10773 &setup.touch.draw_pressed, "touch.draw_pressed"
10777 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10781 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10785 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10789 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10793 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10797 static struct TokenInfo auto_setup_tokens[] =
10801 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10805 static struct TokenInfo server_setup_tokens[] =
10809 &setup.player_uuid, "player_uuid"
10813 &setup.player_version, "player_version"
10817 &setup.use_api_server, TEST_PREFIX "use_api_server"
10821 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10825 &setup.api_server_password, TEST_PREFIX "api_server_password"
10829 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10833 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10837 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10841 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10845 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10849 static struct TokenInfo editor_setup_tokens[] =
10853 &setup.editor.el_classic, "editor.el_classic"
10857 &setup.editor.el_custom, "editor.el_custom"
10861 &setup.editor.el_user_defined, "editor.el_user_defined"
10865 &setup.editor.el_dynamic, "editor.el_dynamic"
10869 &setup.editor.el_headlines, "editor.el_headlines"
10873 &setup.editor.show_element_token, "editor.show_element_token"
10877 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10881 static struct TokenInfo editor_cascade_setup_tokens[] =
10885 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10889 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10893 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10897 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10901 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10905 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10909 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10913 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10917 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10921 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10925 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10929 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10933 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10937 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10941 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10945 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10949 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10953 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10957 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10961 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10965 static struct TokenInfo shortcut_setup_tokens[] =
10969 &setup.shortcut.save_game, "shortcut.save_game"
10973 &setup.shortcut.load_game, "shortcut.load_game"
10977 &setup.shortcut.restart_game, "shortcut.restart_game"
10981 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10985 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10989 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10993 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10997 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
11001 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
11005 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
11009 &setup.shortcut.tape_eject, "shortcut.tape_eject"
11013 &setup.shortcut.tape_extra, "shortcut.tape_extra"
11017 &setup.shortcut.tape_stop, "shortcut.tape_stop"
11021 &setup.shortcut.tape_pause, "shortcut.tape_pause"
11025 &setup.shortcut.tape_record, "shortcut.tape_record"
11029 &setup.shortcut.tape_play, "shortcut.tape_play"
11033 &setup.shortcut.sound_simple, "shortcut.sound_simple"
11037 &setup.shortcut.sound_loops, "shortcut.sound_loops"
11041 &setup.shortcut.sound_music, "shortcut.sound_music"
11045 &setup.shortcut.snap_left, "shortcut.snap_left"
11049 &setup.shortcut.snap_right, "shortcut.snap_right"
11053 &setup.shortcut.snap_up, "shortcut.snap_up"
11057 &setup.shortcut.snap_down, "shortcut.snap_down"
11061 static struct SetupInputInfo setup_input;
11062 static struct TokenInfo player_setup_tokens[] =
11066 &setup_input.use_joystick, ".use_joystick"
11070 &setup_input.joy.device_name, ".joy.device_name"
11074 &setup_input.joy.xleft, ".joy.xleft"
11078 &setup_input.joy.xmiddle, ".joy.xmiddle"
11082 &setup_input.joy.xright, ".joy.xright"
11086 &setup_input.joy.yupper, ".joy.yupper"
11090 &setup_input.joy.ymiddle, ".joy.ymiddle"
11094 &setup_input.joy.ylower, ".joy.ylower"
11098 &setup_input.joy.snap, ".joy.snap_field"
11102 &setup_input.joy.drop, ".joy.place_bomb"
11106 &setup_input.key.left, ".key.move_left"
11110 &setup_input.key.right, ".key.move_right"
11114 &setup_input.key.up, ".key.move_up"
11118 &setup_input.key.down, ".key.move_down"
11122 &setup_input.key.snap, ".key.snap_field"
11126 &setup_input.key.drop, ".key.place_bomb"
11130 static struct TokenInfo system_setup_tokens[] =
11134 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
11138 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11142 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11146 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11150 static struct TokenInfo internal_setup_tokens[] =
11154 &setup.internal.program_title, "program_title"
11158 &setup.internal.program_version, "program_version"
11162 &setup.internal.program_author, "program_author"
11166 &setup.internal.program_email, "program_email"
11170 &setup.internal.program_website, "program_website"
11174 &setup.internal.program_copyright, "program_copyright"
11178 &setup.internal.program_company, "program_company"
11182 &setup.internal.program_icon_file, "program_icon_file"
11186 &setup.internal.default_graphics_set, "default_graphics_set"
11190 &setup.internal.default_sounds_set, "default_sounds_set"
11194 &setup.internal.default_music_set, "default_music_set"
11198 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11202 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11206 &setup.internal.fallback_music_file, "fallback_music_file"
11210 &setup.internal.default_level_series, "default_level_series"
11214 &setup.internal.default_window_width, "default_window_width"
11218 &setup.internal.default_window_height, "default_window_height"
11222 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11226 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11230 &setup.internal.create_user_levelset, "create_user_levelset"
11234 &setup.internal.info_screens_from_main, "info_screens_from_main"
11238 &setup.internal.menu_game, "menu_game"
11242 &setup.internal.menu_engines, "menu_engines"
11246 &setup.internal.menu_editor, "menu_editor"
11250 &setup.internal.menu_graphics, "menu_graphics"
11254 &setup.internal.menu_sound, "menu_sound"
11258 &setup.internal.menu_artwork, "menu_artwork"
11262 &setup.internal.menu_input, "menu_input"
11266 &setup.internal.menu_touch, "menu_touch"
11270 &setup.internal.menu_shortcuts, "menu_shortcuts"
11274 &setup.internal.menu_exit, "menu_exit"
11278 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11282 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11286 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11290 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11294 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11298 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11302 &setup.internal.info_title, "info_title"
11306 &setup.internal.info_elements, "info_elements"
11310 &setup.internal.info_music, "info_music"
11314 &setup.internal.info_credits, "info_credits"
11318 &setup.internal.info_program, "info_program"
11322 &setup.internal.info_version, "info_version"
11326 &setup.internal.info_levelset, "info_levelset"
11330 &setup.internal.info_exit, "info_exit"
11334 static struct TokenInfo debug_setup_tokens[] =
11338 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11342 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11346 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11350 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11354 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11358 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11362 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11366 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11370 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11374 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11378 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11382 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11386 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11390 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11394 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11398 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11402 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11406 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11410 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11414 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11418 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11421 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11425 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11429 &setup.debug.xsn_mode, "debug.xsn_mode"
11433 &setup.debug.xsn_percent, "debug.xsn_percent"
11437 static struct TokenInfo options_setup_tokens[] =
11441 &setup.options.verbose, "options.verbose"
11445 &setup.options.debug, "options.debug"
11449 &setup.options.debug_mode, "options.debug_mode"
11453 static void setSetupInfoToDefaults(struct SetupInfo *si)
11457 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11459 si->multiple_users = TRUE;
11462 si->sound_loops = TRUE;
11463 si->sound_music = TRUE;
11464 si->sound_simple = TRUE;
11466 si->global_animations = TRUE;
11467 si->scroll_delay = TRUE;
11468 si->forced_scroll_delay = FALSE;
11469 si->scroll_delay_value = STD_SCROLL_DELAY;
11470 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11471 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11472 si->fade_screens = TRUE;
11473 si->autorecord = TRUE;
11474 si->autorecord_after_replay = TRUE;
11475 si->auto_pause_on_start = FALSE;
11476 si->show_titlescreen = TRUE;
11477 si->quick_doors = FALSE;
11478 si->team_mode = FALSE;
11479 si->handicap = TRUE;
11480 si->skip_levels = TRUE;
11481 si->increment_levels = TRUE;
11482 si->auto_play_next_level = TRUE;
11483 si->count_score_after_game = TRUE;
11484 si->show_scores_after_game = TRUE;
11485 si->time_limit = TRUE;
11486 si->fullscreen = FALSE;
11487 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11488 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11489 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11490 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11491 si->ask_on_escape = TRUE;
11492 si->ask_on_escape_editor = TRUE;
11493 si->ask_on_game_over = TRUE;
11494 si->ask_on_quit_game = TRUE;
11495 si->ask_on_quit_program = TRUE;
11496 si->quick_switch = FALSE;
11497 si->input_on_focus = FALSE;
11498 si->prefer_aga_graphics = TRUE;
11499 si->prefer_lowpass_sounds = FALSE;
11500 si->prefer_extra_panel_items = TRUE;
11501 si->game_speed_extended = FALSE;
11502 si->game_frame_delay = GAME_FRAME_DELAY;
11503 si->bd_skip_uncovering = FALSE;
11504 si->bd_skip_hatching = FALSE;
11505 si->bd_scroll_delay = TRUE;
11506 si->bd_smooth_movements = AUTO;
11507 si->sp_show_border_elements = FALSE;
11508 si->small_game_graphics = FALSE;
11509 si->show_load_save_buttons = FALSE;
11510 si->show_undo_redo_buttons = FALSE;
11511 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11513 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11514 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11515 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11517 si->override_level_graphics = FALSE;
11518 si->override_level_sounds = FALSE;
11519 si->override_level_music = FALSE;
11521 si->volume_simple = 100; // percent
11522 si->volume_loops = 100; // percent
11523 si->volume_music = 100; // percent
11525 si->network_mode = FALSE;
11526 si->network_player_nr = 0; // first player
11527 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11529 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11530 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11531 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11532 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11533 si->touch.draw_outlined = TRUE;
11534 si->touch.draw_pressed = TRUE;
11536 for (i = 0; i < 2; i++)
11538 char *default_grid_button[6][2] =
11544 { "111222", " vv " },
11545 { "111222", " vv " }
11547 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11548 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11549 int min_xsize = MIN(6, grid_xsize);
11550 int min_ysize = MIN(6, grid_ysize);
11551 int startx = grid_xsize - min_xsize;
11552 int starty = grid_ysize - min_ysize;
11555 // virtual buttons grid can only be set to defaults if video is initialized
11556 // (this will be repeated if virtual buttons are not loaded from setup file)
11557 if (video.initialized)
11559 si->touch.grid_xsize[i] = grid_xsize;
11560 si->touch.grid_ysize[i] = grid_ysize;
11564 si->touch.grid_xsize[i] = -1;
11565 si->touch.grid_ysize[i] = -1;
11568 for (x = 0; x < MAX_GRID_XSIZE; x++)
11569 for (y = 0; y < MAX_GRID_YSIZE; y++)
11570 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11572 for (x = 0; x < min_xsize; x++)
11573 for (y = 0; y < min_ysize; y++)
11574 si->touch.grid_button[i][x][starty + y] =
11575 default_grid_button[y][0][x];
11577 for (x = 0; x < min_xsize; x++)
11578 for (y = 0; y < min_ysize; y++)
11579 si->touch.grid_button[i][startx + x][starty + y] =
11580 default_grid_button[y][1][x];
11583 si->touch.grid_initialized = video.initialized;
11585 si->touch.overlay_buttons = FALSE;
11587 si->editor.el_boulderdash = TRUE;
11588 si->editor.el_boulderdash_native = TRUE;
11589 si->editor.el_boulderdash_effects = TRUE;
11590 si->editor.el_emerald_mine = TRUE;
11591 si->editor.el_emerald_mine_club = TRUE;
11592 si->editor.el_more = TRUE;
11593 si->editor.el_sokoban = TRUE;
11594 si->editor.el_supaplex = TRUE;
11595 si->editor.el_diamond_caves = TRUE;
11596 si->editor.el_dx_boulderdash = TRUE;
11598 si->editor.el_mirror_magic = TRUE;
11599 si->editor.el_deflektor = TRUE;
11601 si->editor.el_chars = TRUE;
11602 si->editor.el_steel_chars = TRUE;
11604 si->editor.el_classic = TRUE;
11605 si->editor.el_custom = TRUE;
11607 si->editor.el_user_defined = FALSE;
11608 si->editor.el_dynamic = TRUE;
11610 si->editor.el_headlines = TRUE;
11612 si->editor.show_element_token = FALSE;
11614 si->editor.show_read_only_warning = TRUE;
11616 si->editor.use_template_for_new_levels = TRUE;
11618 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11619 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11620 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11621 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11622 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11624 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11625 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11626 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11627 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11628 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11630 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11631 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11632 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11633 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11634 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11635 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11637 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11638 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11639 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11641 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11642 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11643 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11644 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11646 for (i = 0; i < MAX_PLAYERS; i++)
11648 si->input[i].use_joystick = FALSE;
11649 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11650 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11651 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11652 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11653 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11654 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11655 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11656 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11657 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11658 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11659 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11660 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11661 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11662 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11663 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11666 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11667 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11668 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11669 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11671 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11672 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11673 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11674 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11675 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11676 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11677 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11679 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11681 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11682 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11683 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11685 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11686 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11687 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11689 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11690 si->internal.choose_from_top_leveldir = FALSE;
11691 si->internal.show_scaling_in_title = TRUE;
11692 si->internal.create_user_levelset = TRUE;
11693 si->internal.info_screens_from_main = FALSE;
11695 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11696 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11698 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11699 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11700 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11701 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11702 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11703 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11704 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11705 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11706 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11707 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11709 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11710 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11711 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11712 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11713 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11714 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11715 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11716 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11717 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11718 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11720 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11721 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11723 si->debug.show_frames_per_second = FALSE;
11725 si->debug.xsn_mode = AUTO;
11726 si->debug.xsn_percent = 0;
11728 si->options.verbose = FALSE;
11729 si->options.debug = FALSE;
11730 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11732 #if defined(PLATFORM_ANDROID)
11733 si->fullscreen = TRUE;
11734 si->touch.overlay_buttons = TRUE;
11737 setHideSetupEntry(&setup.debug.xsn_mode);
11740 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11742 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11745 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11747 si->player_uuid = NULL; // (will be set later)
11748 si->player_version = 1; // (will be set later)
11750 si->use_api_server = TRUE;
11751 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11752 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11753 si->ask_for_uploading_tapes = TRUE;
11754 si->ask_for_remaining_tapes = FALSE;
11755 si->provide_uploading_tapes = TRUE;
11756 si->ask_for_using_api_server = TRUE;
11757 si->has_remaining_tapes = FALSE;
11760 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11762 si->editor_cascade.el_bd = TRUE;
11763 si->editor_cascade.el_bd_native = TRUE;
11764 si->editor_cascade.el_bd_effects = FALSE;
11765 si->editor_cascade.el_em = TRUE;
11766 si->editor_cascade.el_emc = TRUE;
11767 si->editor_cascade.el_rnd = TRUE;
11768 si->editor_cascade.el_sb = TRUE;
11769 si->editor_cascade.el_sp = TRUE;
11770 si->editor_cascade.el_dc = TRUE;
11771 si->editor_cascade.el_dx = TRUE;
11773 si->editor_cascade.el_mm = TRUE;
11774 si->editor_cascade.el_df = TRUE;
11776 si->editor_cascade.el_chars = FALSE;
11777 si->editor_cascade.el_steel_chars = FALSE;
11778 si->editor_cascade.el_ce = FALSE;
11779 si->editor_cascade.el_ge = FALSE;
11780 si->editor_cascade.el_es = FALSE;
11781 si->editor_cascade.el_ref = FALSE;
11782 si->editor_cascade.el_user = FALSE;
11783 si->editor_cascade.el_dynamic = FALSE;
11786 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11788 static char *getHideSetupToken(void *setup_value)
11790 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11792 if (setup_value != NULL)
11793 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11795 return hide_setup_token;
11798 void setHideSetupEntry(void *setup_value)
11800 char *hide_setup_token = getHideSetupToken(setup_value);
11802 if (hide_setup_hash == NULL)
11803 hide_setup_hash = newSetupFileHash();
11805 if (setup_value != NULL)
11806 setHashEntry(hide_setup_hash, hide_setup_token, "");
11809 void removeHideSetupEntry(void *setup_value)
11811 char *hide_setup_token = getHideSetupToken(setup_value);
11813 if (setup_value != NULL)
11814 removeHashEntry(hide_setup_hash, hide_setup_token);
11817 boolean hideSetupEntry(void *setup_value)
11819 char *hide_setup_token = getHideSetupToken(setup_value);
11821 return (setup_value != NULL &&
11822 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11825 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11826 struct TokenInfo *token_info,
11827 int token_nr, char *token_text)
11829 char *token_hide_text = getStringCat2(token_text, ".hide");
11830 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11832 // set the value of this setup option in the setup option structure
11833 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11835 // check if this setup option should be hidden in the setup menu
11836 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11837 setHideSetupEntry(token_info[token_nr].value);
11839 free(token_hide_text);
11842 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11843 struct TokenInfo *token_info,
11846 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11847 token_info[token_nr].text);
11850 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11854 if (!setup_file_hash)
11857 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11858 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11860 setup.touch.grid_initialized = TRUE;
11861 for (i = 0; i < 2; i++)
11863 int grid_xsize = setup.touch.grid_xsize[i];
11864 int grid_ysize = setup.touch.grid_ysize[i];
11867 // if virtual buttons are not loaded from setup file, repeat initializing
11868 // virtual buttons grid with default values later when video is initialized
11869 if (grid_xsize == -1 ||
11872 setup.touch.grid_initialized = FALSE;
11877 for (y = 0; y < grid_ysize; y++)
11879 char token_string[MAX_LINE_LEN];
11881 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11883 char *value_string = getHashEntry(setup_file_hash, token_string);
11885 if (value_string == NULL)
11888 for (x = 0; x < grid_xsize; x++)
11890 char c = value_string[x];
11892 setup.touch.grid_button[i][x][y] =
11893 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11898 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11899 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11901 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11902 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11904 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11908 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11910 setup_input = setup.input[pnr];
11911 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11913 char full_token[100];
11915 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11916 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11919 setup.input[pnr] = setup_input;
11922 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11923 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11925 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11926 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11928 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11929 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11931 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11932 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11934 setHideRelatedSetupEntries();
11937 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11941 if (!setup_file_hash)
11944 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11945 setSetupInfo(auto_setup_tokens, i,
11946 getHashEntry(setup_file_hash,
11947 auto_setup_tokens[i].text));
11950 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11954 if (!setup_file_hash)
11957 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11958 setSetupInfo(server_setup_tokens, i,
11959 getHashEntry(setup_file_hash,
11960 server_setup_tokens[i].text));
11963 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11967 if (!setup_file_hash)
11970 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11971 setSetupInfo(editor_cascade_setup_tokens, i,
11972 getHashEntry(setup_file_hash,
11973 editor_cascade_setup_tokens[i].text));
11976 void LoadUserNames(void)
11978 int last_user_nr = user.nr;
11981 if (global.user_names != NULL)
11983 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11984 checked_free(global.user_names[i]);
11986 checked_free(global.user_names);
11989 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11991 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11995 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11997 if (setup_file_hash)
11999 char *player_name = getHashEntry(setup_file_hash, "player_name");
12001 global.user_names[i] = getFixedUserName(player_name);
12003 freeSetupFileHash(setup_file_hash);
12006 if (global.user_names[i] == NULL)
12007 global.user_names[i] = getStringCopy(getDefaultUserName(i));
12010 user.nr = last_user_nr;
12013 void LoadSetupFromFilename(char *filename)
12015 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12017 if (setup_file_hash)
12019 decodeSetupFileHash_Default(setup_file_hash);
12021 freeSetupFileHash(setup_file_hash);
12025 Debug("setup", "using default setup values");
12029 static void LoadSetup_SpecialPostProcessing(void)
12031 char *player_name_new;
12033 // needed to work around problems with fixed length strings
12034 player_name_new = getFixedUserName(setup.player_name);
12035 free(setup.player_name);
12036 setup.player_name = player_name_new;
12038 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12039 if (setup.scroll_delay == FALSE)
12041 setup.scroll_delay_value = MIN_SCROLL_DELAY;
12042 setup.scroll_delay = TRUE; // now always "on"
12045 // make sure that scroll delay value stays inside valid range
12046 setup.scroll_delay_value =
12047 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12050 void LoadSetup_Default(void)
12054 // always start with reliable default values
12055 setSetupInfoToDefaults(&setup);
12057 // try to load setup values from default setup file
12058 filename = getDefaultSetupFilename();
12060 if (fileExists(filename))
12061 LoadSetupFromFilename(filename);
12063 // try to load setup values from platform setup file
12064 filename = getPlatformSetupFilename();
12066 if (fileExists(filename))
12067 LoadSetupFromFilename(filename);
12069 // try to load setup values from user setup file
12070 filename = getSetupFilename();
12072 LoadSetupFromFilename(filename);
12074 LoadSetup_SpecialPostProcessing();
12077 void LoadSetup_AutoSetup(void)
12079 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12080 SetupFileHash *setup_file_hash = NULL;
12082 // always start with reliable default values
12083 setSetupInfoToDefaults_AutoSetup(&setup);
12085 setup_file_hash = loadSetupFileHash(filename);
12087 if (setup_file_hash)
12089 decodeSetupFileHash_AutoSetup(setup_file_hash);
12091 freeSetupFileHash(setup_file_hash);
12097 void LoadSetup_ServerSetup(void)
12099 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12100 SetupFileHash *setup_file_hash = NULL;
12102 // always start with reliable default values
12103 setSetupInfoToDefaults_ServerSetup(&setup);
12105 setup_file_hash = loadSetupFileHash(filename);
12107 if (setup_file_hash)
12109 decodeSetupFileHash_ServerSetup(setup_file_hash);
12111 freeSetupFileHash(setup_file_hash);
12116 if (setup.player_uuid == NULL)
12118 // player UUID does not yet exist in setup file
12119 setup.player_uuid = getStringCopy(getUUID());
12120 setup.player_version = 2;
12122 SaveSetup_ServerSetup();
12126 void LoadSetup_EditorCascade(void)
12128 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12129 SetupFileHash *setup_file_hash = NULL;
12131 // always start with reliable default values
12132 setSetupInfoToDefaults_EditorCascade(&setup);
12134 setup_file_hash = loadSetupFileHash(filename);
12136 if (setup_file_hash)
12138 decodeSetupFileHash_EditorCascade(setup_file_hash);
12140 freeSetupFileHash(setup_file_hash);
12146 void LoadSetup(void)
12148 LoadSetup_Default();
12149 LoadSetup_AutoSetup();
12150 LoadSetup_ServerSetup();
12151 LoadSetup_EditorCascade();
12154 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12155 char *mapping_line)
12157 char mapping_guid[MAX_LINE_LEN];
12158 char *mapping_start, *mapping_end;
12160 // get GUID from game controller mapping line: copy complete line
12161 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12162 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12164 // get GUID from game controller mapping line: cut after GUID part
12165 mapping_start = strchr(mapping_guid, ',');
12166 if (mapping_start != NULL)
12167 *mapping_start = '\0';
12169 // cut newline from game controller mapping line
12170 mapping_end = strchr(mapping_line, '\n');
12171 if (mapping_end != NULL)
12172 *mapping_end = '\0';
12174 // add mapping entry to game controller mappings hash
12175 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12178 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12183 if (!(file = fopen(filename, MODE_READ)))
12185 Warn("cannot read game controller mappings file '%s'", filename);
12190 while (!feof(file))
12192 char line[MAX_LINE_LEN];
12194 if (!fgets(line, MAX_LINE_LEN, file))
12197 addGameControllerMappingToHash(mappings_hash, line);
12203 void SaveSetup_Default(void)
12205 char *filename = getSetupFilename();
12209 InitUserDataDirectory();
12211 if (!(file = fopen(filename, MODE_WRITE)))
12213 Warn("cannot write setup file '%s'", filename);
12218 fprintFileHeader(file, SETUP_FILENAME);
12220 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12222 // just to make things nicer :)
12223 if (global_setup_tokens[i].value == &setup.multiple_users ||
12224 global_setup_tokens[i].value == &setup.sound ||
12225 global_setup_tokens[i].value == &setup.graphics_set ||
12226 global_setup_tokens[i].value == &setup.volume_simple ||
12227 global_setup_tokens[i].value == &setup.network_mode ||
12228 global_setup_tokens[i].value == &setup.touch.control_type ||
12229 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12230 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12231 fprintf(file, "\n");
12233 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12236 for (i = 0; i < 2; i++)
12238 int grid_xsize = setup.touch.grid_xsize[i];
12239 int grid_ysize = setup.touch.grid_ysize[i];
12242 fprintf(file, "\n");
12244 for (y = 0; y < grid_ysize; y++)
12246 char token_string[MAX_LINE_LEN];
12247 char value_string[MAX_LINE_LEN];
12249 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12251 for (x = 0; x < grid_xsize; x++)
12253 char c = setup.touch.grid_button[i][x][y];
12255 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12258 value_string[grid_xsize] = '\0';
12260 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12264 fprintf(file, "\n");
12265 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12266 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12268 fprintf(file, "\n");
12269 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12270 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12272 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12276 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12277 fprintf(file, "\n");
12279 setup_input = setup.input[pnr];
12280 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12281 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12284 fprintf(file, "\n");
12285 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12286 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12288 // (internal setup values not saved to user setup file)
12290 fprintf(file, "\n");
12291 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12292 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12293 setup.debug.xsn_mode != AUTO)
12294 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12296 fprintf(file, "\n");
12297 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12298 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12302 SetFilePermissions(filename, PERMS_PRIVATE);
12305 void SaveSetup_AutoSetup(void)
12307 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12311 InitUserDataDirectory();
12313 if (!(file = fopen(filename, MODE_WRITE)))
12315 Warn("cannot write auto setup file '%s'", filename);
12322 fprintFileHeader(file, AUTOSETUP_FILENAME);
12324 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12325 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12329 SetFilePermissions(filename, PERMS_PRIVATE);
12334 void SaveSetup_ServerSetup(void)
12336 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12340 InitUserDataDirectory();
12342 if (!(file = fopen(filename, MODE_WRITE)))
12344 Warn("cannot write server setup file '%s'", filename);
12351 fprintFileHeader(file, SERVERSETUP_FILENAME);
12353 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12355 // just to make things nicer :)
12356 if (server_setup_tokens[i].value == &setup.use_api_server)
12357 fprintf(file, "\n");
12359 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12364 SetFilePermissions(filename, PERMS_PRIVATE);
12369 void SaveSetup_EditorCascade(void)
12371 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12375 InitUserDataDirectory();
12377 if (!(file = fopen(filename, MODE_WRITE)))
12379 Warn("cannot write editor cascade state file '%s'", filename);
12386 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12388 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12389 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12393 SetFilePermissions(filename, PERMS_PRIVATE);
12398 void SaveSetup(void)
12400 SaveSetup_Default();
12401 SaveSetup_AutoSetup();
12402 SaveSetup_ServerSetup();
12403 SaveSetup_EditorCascade();
12406 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12411 if (!(file = fopen(filename, MODE_WRITE)))
12413 Warn("cannot write game controller mappings file '%s'", filename);
12418 BEGIN_HASH_ITERATION(mappings_hash, itr)
12420 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12422 END_HASH_ITERATION(mappings_hash, itr)
12427 void SaveSetup_AddGameControllerMapping(char *mapping)
12429 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12430 SetupFileHash *mappings_hash = newSetupFileHash();
12432 InitUserDataDirectory();
12434 // load existing personal game controller mappings
12435 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12437 // add new mapping to personal game controller mappings
12438 addGameControllerMappingToHash(mappings_hash, mapping);
12440 // save updated personal game controller mappings
12441 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12443 freeSetupFileHash(mappings_hash);
12447 void LoadCustomElementDescriptions(void)
12449 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12450 SetupFileHash *setup_file_hash;
12453 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12455 if (element_info[i].custom_description != NULL)
12457 free(element_info[i].custom_description);
12458 element_info[i].custom_description = NULL;
12462 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12465 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12467 char *token = getStringCat2(element_info[i].token_name, ".name");
12468 char *value = getHashEntry(setup_file_hash, token);
12471 element_info[i].custom_description = getStringCopy(value);
12476 freeSetupFileHash(setup_file_hash);
12479 static int getElementFromToken(char *token)
12481 char *value = getHashEntry(element_token_hash, token);
12484 return atoi(value);
12486 Warn("unknown element token '%s'", token);
12488 return EL_UNDEFINED;
12491 void FreeGlobalAnimEventInfo(void)
12493 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12495 if (gaei->event_list == NULL)
12500 for (i = 0; i < gaei->num_event_lists; i++)
12502 checked_free(gaei->event_list[i]->event_value);
12503 checked_free(gaei->event_list[i]);
12506 checked_free(gaei->event_list);
12508 gaei->event_list = NULL;
12509 gaei->num_event_lists = 0;
12512 static int AddGlobalAnimEventList(void)
12514 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12515 int list_pos = gaei->num_event_lists++;
12517 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12518 sizeof(struct GlobalAnimEventListInfo *));
12520 gaei->event_list[list_pos] =
12521 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12523 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12525 gaeli->event_value = NULL;
12526 gaeli->num_event_values = 0;
12531 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12533 // do not add empty global animation events
12534 if (event_value == ANIM_EVENT_NONE)
12537 // if list position is undefined, create new list
12538 if (list_pos == ANIM_EVENT_UNDEFINED)
12539 list_pos = AddGlobalAnimEventList();
12541 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12542 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12543 int value_pos = gaeli->num_event_values++;
12545 gaeli->event_value = checked_realloc(gaeli->event_value,
12546 gaeli->num_event_values * sizeof(int *));
12548 gaeli->event_value[value_pos] = event_value;
12553 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12555 if (list_pos == ANIM_EVENT_UNDEFINED)
12556 return ANIM_EVENT_NONE;
12558 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12559 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12561 return gaeli->event_value[value_pos];
12564 int GetGlobalAnimEventValueCount(int list_pos)
12566 if (list_pos == ANIM_EVENT_UNDEFINED)
12569 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12570 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12572 return gaeli->num_event_values;
12575 // This function checks if a string <s> of the format "string1, string2, ..."
12576 // exactly contains a string <s_contained>.
12578 static boolean string_has_parameter(char *s, char *s_contained)
12582 if (s == NULL || s_contained == NULL)
12585 if (strlen(s_contained) > strlen(s))
12588 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12590 char next_char = s[strlen(s_contained)];
12592 // check if next character is delimiter or whitespace
12593 if (next_char == ',' || next_char == '\0' ||
12594 next_char == ' ' || next_char == '\t')
12598 // check if string contains another parameter string after a comma
12599 substring = strchr(s, ',');
12600 if (substring == NULL) // string does not contain a comma
12603 // advance string pointer to next character after the comma
12606 // skip potential whitespaces after the comma
12607 while (*substring == ' ' || *substring == '\t')
12610 return string_has_parameter(substring, s_contained);
12613 static int get_anim_parameter_value_ce(char *s)
12616 char *pattern_1 = "ce_change:custom_";
12617 char *pattern_2 = ".page_";
12618 int pattern_1_len = strlen(pattern_1);
12619 char *matching_char = strstr(s_ptr, pattern_1);
12620 int result = ANIM_EVENT_NONE;
12622 if (matching_char == NULL)
12623 return ANIM_EVENT_NONE;
12625 result = ANIM_EVENT_CE_CHANGE;
12627 s_ptr = matching_char + pattern_1_len;
12629 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12630 if (*s_ptr >= '0' && *s_ptr <= '9')
12632 int gic_ce_nr = (*s_ptr++ - '0');
12634 if (*s_ptr >= '0' && *s_ptr <= '9')
12636 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12638 if (*s_ptr >= '0' && *s_ptr <= '9')
12639 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12642 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12643 return ANIM_EVENT_NONE;
12645 // custom element stored as 0 to 255
12648 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12652 // invalid custom element number specified
12654 return ANIM_EVENT_NONE;
12657 // check for change page number ("page_X" or "page_XX") (optional)
12658 if (strPrefix(s_ptr, pattern_2))
12660 s_ptr += strlen(pattern_2);
12662 if (*s_ptr >= '0' && *s_ptr <= '9')
12664 int gic_page_nr = (*s_ptr++ - '0');
12666 if (*s_ptr >= '0' && *s_ptr <= '9')
12667 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12669 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12670 return ANIM_EVENT_NONE;
12672 // change page stored as 1 to 32 (0 means "all change pages")
12674 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12678 // invalid animation part number specified
12680 return ANIM_EVENT_NONE;
12684 // discard result if next character is neither delimiter nor whitespace
12685 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12686 *s_ptr == ' ' || *s_ptr == '\t'))
12687 return ANIM_EVENT_NONE;
12692 static int get_anim_parameter_value(char *s)
12694 int event_value[] =
12702 char *pattern_1[] =
12710 char *pattern_2 = ".part_";
12711 char *matching_char = NULL;
12713 int pattern_1_len = 0;
12714 int result = ANIM_EVENT_NONE;
12717 result = get_anim_parameter_value_ce(s);
12719 if (result != ANIM_EVENT_NONE)
12722 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12724 matching_char = strstr(s_ptr, pattern_1[i]);
12725 pattern_1_len = strlen(pattern_1[i]);
12726 result = event_value[i];
12728 if (matching_char != NULL)
12732 if (matching_char == NULL)
12733 return ANIM_EVENT_NONE;
12735 s_ptr = matching_char + pattern_1_len;
12737 // check for main animation number ("anim_X" or "anim_XX")
12738 if (*s_ptr >= '0' && *s_ptr <= '9')
12740 int gic_anim_nr = (*s_ptr++ - '0');
12742 if (*s_ptr >= '0' && *s_ptr <= '9')
12743 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12745 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12746 return ANIM_EVENT_NONE;
12748 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12752 // invalid main animation number specified
12754 return ANIM_EVENT_NONE;
12757 // check for animation part number ("part_X" or "part_XX") (optional)
12758 if (strPrefix(s_ptr, pattern_2))
12760 s_ptr += strlen(pattern_2);
12762 if (*s_ptr >= '0' && *s_ptr <= '9')
12764 int gic_part_nr = (*s_ptr++ - '0');
12766 if (*s_ptr >= '0' && *s_ptr <= '9')
12767 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12769 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12770 return ANIM_EVENT_NONE;
12772 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12776 // invalid animation part number specified
12778 return ANIM_EVENT_NONE;
12782 // discard result if next character is neither delimiter nor whitespace
12783 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12784 *s_ptr == ' ' || *s_ptr == '\t'))
12785 return ANIM_EVENT_NONE;
12790 static int get_anim_parameter_values(char *s)
12792 int list_pos = ANIM_EVENT_UNDEFINED;
12793 int event_value = ANIM_EVENT_DEFAULT;
12795 if (string_has_parameter(s, "any"))
12796 event_value |= ANIM_EVENT_ANY;
12798 if (string_has_parameter(s, "click:self") ||
12799 string_has_parameter(s, "click") ||
12800 string_has_parameter(s, "self"))
12801 event_value |= ANIM_EVENT_SELF;
12803 if (string_has_parameter(s, "unclick:any"))
12804 event_value |= ANIM_EVENT_UNCLICK_ANY;
12806 // if animation event found, add it to global animation event list
12807 if (event_value != ANIM_EVENT_NONE)
12808 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12812 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12813 event_value = get_anim_parameter_value(s);
12815 // if animation event found, add it to global animation event list
12816 if (event_value != ANIM_EVENT_NONE)
12817 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12819 // continue with next part of the string, starting with next comma
12820 s = strchr(s + 1, ',');
12826 static int get_anim_action_parameter_value(char *token)
12828 // check most common default case first to massively speed things up
12829 if (strEqual(token, ARG_UNDEFINED))
12830 return ANIM_EVENT_ACTION_NONE;
12832 int result = getImageIDFromToken(token);
12836 char *gfx_token = getStringCat2("gfx.", token);
12838 result = getImageIDFromToken(gfx_token);
12840 checked_free(gfx_token);
12845 Key key = getKeyFromX11KeyName(token);
12847 if (key != KSYM_UNDEFINED)
12848 result = -(int)key;
12855 result = get_hash_from_string(token); // unsigned int => int
12856 result = ABS(result); // may be negative now
12857 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12859 setHashEntry(anim_url_hash, int2str(result, 0), token);
12864 result = ANIM_EVENT_ACTION_NONE;
12869 int get_parameter_value(char *value_raw, char *suffix, int type)
12871 char *value = getStringToLower(value_raw);
12872 int result = 0; // probably a save default value
12874 if (strEqual(suffix, ".direction"))
12876 result = (strEqual(value, "left") ? MV_LEFT :
12877 strEqual(value, "right") ? MV_RIGHT :
12878 strEqual(value, "up") ? MV_UP :
12879 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12881 else if (strEqual(suffix, ".position"))
12883 result = (strEqual(value, "left") ? POS_LEFT :
12884 strEqual(value, "right") ? POS_RIGHT :
12885 strEqual(value, "top") ? POS_TOP :
12886 strEqual(value, "upper") ? POS_UPPER :
12887 strEqual(value, "middle") ? POS_MIDDLE :
12888 strEqual(value, "lower") ? POS_LOWER :
12889 strEqual(value, "bottom") ? POS_BOTTOM :
12890 strEqual(value, "any") ? POS_ANY :
12891 strEqual(value, "ce") ? POS_CE :
12892 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12893 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12895 else if (strEqual(suffix, ".align"))
12897 result = (strEqual(value, "left") ? ALIGN_LEFT :
12898 strEqual(value, "right") ? ALIGN_RIGHT :
12899 strEqual(value, "center") ? ALIGN_CENTER :
12900 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12902 else if (strEqual(suffix, ".valign"))
12904 result = (strEqual(value, "top") ? VALIGN_TOP :
12905 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12906 strEqual(value, "middle") ? VALIGN_MIDDLE :
12907 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12909 else if (strEqual(suffix, ".anim_mode"))
12911 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12912 string_has_parameter(value, "loop") ? ANIM_LOOP :
12913 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12914 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12915 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12916 string_has_parameter(value, "random") ? ANIM_RANDOM :
12917 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12918 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12919 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12920 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12921 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12922 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12923 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12924 string_has_parameter(value, "all") ? ANIM_ALL :
12925 string_has_parameter(value, "tiled") ? ANIM_TILED :
12926 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12929 if (string_has_parameter(value, "once"))
12930 result |= ANIM_ONCE;
12932 if (string_has_parameter(value, "reverse"))
12933 result |= ANIM_REVERSE;
12935 if (string_has_parameter(value, "opaque_player"))
12936 result |= ANIM_OPAQUE_PLAYER;
12938 if (string_has_parameter(value, "static_panel"))
12939 result |= ANIM_STATIC_PANEL;
12941 else if (strEqual(suffix, ".init_event") ||
12942 strEqual(suffix, ".anim_event"))
12944 result = get_anim_parameter_values(value);
12946 else if (strEqual(suffix, ".init_delay_action") ||
12947 strEqual(suffix, ".anim_delay_action") ||
12948 strEqual(suffix, ".post_delay_action") ||
12949 strEqual(suffix, ".init_event_action") ||
12950 strEqual(suffix, ".anim_event_action"))
12952 result = get_anim_action_parameter_value(value_raw);
12954 else if (strEqual(suffix, ".class"))
12956 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12957 get_hash_from_string(value));
12959 else if (strEqual(suffix, ".style"))
12961 result = STYLE_DEFAULT;
12963 if (string_has_parameter(value, "accurate_borders"))
12964 result |= STYLE_ACCURATE_BORDERS;
12966 if (string_has_parameter(value, "inner_corners"))
12967 result |= STYLE_INNER_CORNERS;
12969 if (string_has_parameter(value, "reverse"))
12970 result |= STYLE_REVERSE;
12972 if (string_has_parameter(value, "leftmost_position"))
12973 result |= STYLE_LEFTMOST_POSITION;
12975 if (string_has_parameter(value, "block_clicks"))
12976 result |= STYLE_BLOCK;
12978 if (string_has_parameter(value, "passthrough_clicks"))
12979 result |= STYLE_PASSTHROUGH;
12981 if (string_has_parameter(value, "multiple_actions"))
12982 result |= STYLE_MULTIPLE_ACTIONS;
12984 if (string_has_parameter(value, "consume_ce_event"))
12985 result |= STYLE_CONSUME_CE_EVENT;
12987 else if (strEqual(suffix, ".fade_mode"))
12989 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12990 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12991 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12992 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12993 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12994 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12995 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12996 FADE_MODE_DEFAULT);
12998 else if (strEqual(suffix, ".auto_delay_unit"))
13000 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
13001 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13002 AUTO_DELAY_UNIT_DEFAULT);
13004 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
13006 result = gfx.get_font_from_token_function(value);
13008 else // generic parameter of type integer or boolean
13010 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13011 type == TYPE_INTEGER ? get_integer_from_string(value) :
13012 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13013 ARG_UNDEFINED_VALUE);
13021 static int get_token_parameter_value(char *token, char *value_raw)
13025 if (token == NULL || value_raw == NULL)
13026 return ARG_UNDEFINED_VALUE;
13028 suffix = strrchr(token, '.');
13029 if (suffix == NULL)
13032 if (strEqual(suffix, ".element"))
13033 return getElementFromToken(value_raw);
13035 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13036 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13039 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13040 boolean ignore_defaults)
13044 for (i = 0; image_config_vars[i].token != NULL; i++)
13046 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13048 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13049 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13053 *image_config_vars[i].value =
13054 get_token_parameter_value(image_config_vars[i].token, value);
13058 void InitMenuDesignSettings_Static(void)
13060 // always start with reliable default values from static default config
13061 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13064 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13068 // the following initializes hierarchical values from static configuration
13070 // special case: initialize "ARG_DEFAULT" values in static default config
13071 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13072 titlescreen_initial_first_default.fade_mode =
13073 title_initial_first_default.fade_mode;
13074 titlescreen_initial_first_default.fade_delay =
13075 title_initial_first_default.fade_delay;
13076 titlescreen_initial_first_default.post_delay =
13077 title_initial_first_default.post_delay;
13078 titlescreen_initial_first_default.auto_delay =
13079 title_initial_first_default.auto_delay;
13080 titlescreen_initial_first_default.auto_delay_unit =
13081 title_initial_first_default.auto_delay_unit;
13082 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
13083 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13084 titlescreen_first_default.post_delay = title_first_default.post_delay;
13085 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13086 titlescreen_first_default.auto_delay_unit =
13087 title_first_default.auto_delay_unit;
13088 titlemessage_initial_first_default.fade_mode =
13089 title_initial_first_default.fade_mode;
13090 titlemessage_initial_first_default.fade_delay =
13091 title_initial_first_default.fade_delay;
13092 titlemessage_initial_first_default.post_delay =
13093 title_initial_first_default.post_delay;
13094 titlemessage_initial_first_default.auto_delay =
13095 title_initial_first_default.auto_delay;
13096 titlemessage_initial_first_default.auto_delay_unit =
13097 title_initial_first_default.auto_delay_unit;
13098 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
13099 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13100 titlemessage_first_default.post_delay = title_first_default.post_delay;
13101 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13102 titlemessage_first_default.auto_delay_unit =
13103 title_first_default.auto_delay_unit;
13105 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
13106 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13107 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13108 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13109 titlescreen_initial_default.auto_delay_unit =
13110 title_initial_default.auto_delay_unit;
13111 titlescreen_default.fade_mode = title_default.fade_mode;
13112 titlescreen_default.fade_delay = title_default.fade_delay;
13113 titlescreen_default.post_delay = title_default.post_delay;
13114 titlescreen_default.auto_delay = title_default.auto_delay;
13115 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13116 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
13117 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13118 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13119 titlemessage_initial_default.auto_delay_unit =
13120 title_initial_default.auto_delay_unit;
13121 titlemessage_default.fade_mode = title_default.fade_mode;
13122 titlemessage_default.fade_delay = title_default.fade_delay;
13123 titlemessage_default.post_delay = title_default.post_delay;
13124 titlemessage_default.auto_delay = title_default.auto_delay;
13125 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13127 // special case: initialize "ARG_DEFAULT" values in static default config
13128 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13129 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13131 titlescreen_initial_first[i] = titlescreen_initial_first_default;
13132 titlescreen_first[i] = titlescreen_first_default;
13133 titlemessage_initial_first[i] = titlemessage_initial_first_default;
13134 titlemessage_first[i] = titlemessage_first_default;
13136 titlescreen_initial[i] = titlescreen_initial_default;
13137 titlescreen[i] = titlescreen_default;
13138 titlemessage_initial[i] = titlemessage_initial_default;
13139 titlemessage[i] = titlemessage_default;
13142 // special case: initialize "ARG_DEFAULT" values in static default config
13143 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13144 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13146 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13149 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13150 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13151 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13154 // special case: initialize "ARG_DEFAULT" values in static default config
13155 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13156 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13158 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13159 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13160 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13162 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13165 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13169 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13173 struct XY *dst, *src;
13175 game_buttons_xy[] =
13177 { &game.button.save, &game.button.stop },
13178 { &game.button.pause2, &game.button.pause },
13179 { &game.button.load, &game.button.play },
13180 { &game.button.undo, &game.button.stop },
13181 { &game.button.redo, &game.button.play },
13187 // special case: initialize later added SETUP list size from LEVELS value
13188 if (menu.list_size[GAME_MODE_SETUP] == -1)
13189 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13191 // set default position for snapshot buttons to stop/pause/play buttons
13192 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13193 if ((*game_buttons_xy[i].dst).x == -1 &&
13194 (*game_buttons_xy[i].dst).y == -1)
13195 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13197 // --------------------------------------------------------------------------
13198 // dynamic viewports (including playfield margins, borders and alignments)
13199 // --------------------------------------------------------------------------
13201 // dynamic viewports currently only supported for landscape mode
13202 int display_width = MAX(video.display_width, video.display_height);
13203 int display_height = MIN(video.display_width, video.display_height);
13205 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13207 struct RectWithBorder *vp_window = &viewport.window[i];
13208 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13209 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13210 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13211 boolean dynamic_window_width = (vp_window->min_width != -1);
13212 boolean dynamic_window_height = (vp_window->min_height != -1);
13213 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13214 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13216 // adjust window size if min/max width/height is specified
13218 if (vp_window->min_width != -1)
13220 int window_width = display_width;
13222 // when using static window height, use aspect ratio of display
13223 if (vp_window->min_height == -1)
13224 window_width = vp_window->height * display_width / display_height;
13226 vp_window->width = MAX(vp_window->min_width, window_width);
13229 if (vp_window->min_height != -1)
13231 int window_height = display_height;
13233 // when using static window width, use aspect ratio of display
13234 if (vp_window->min_width == -1)
13235 window_height = vp_window->width * display_height / display_width;
13237 vp_window->height = MAX(vp_window->min_height, window_height);
13240 if (vp_window->max_width != -1)
13241 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13243 if (vp_window->max_height != -1)
13244 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13246 int playfield_width = vp_window->width;
13247 int playfield_height = vp_window->height;
13249 // adjust playfield size and position according to specified margins
13251 playfield_width -= vp_playfield->margin_left;
13252 playfield_width -= vp_playfield->margin_right;
13254 playfield_height -= vp_playfield->margin_top;
13255 playfield_height -= vp_playfield->margin_bottom;
13257 // adjust playfield size if min/max width/height is specified
13259 if (vp_playfield->min_width != -1)
13260 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13262 if (vp_playfield->min_height != -1)
13263 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13265 if (vp_playfield->max_width != -1)
13266 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13268 if (vp_playfield->max_height != -1)
13269 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13271 // adjust playfield position according to specified alignment
13273 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13274 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13275 else if (vp_playfield->align == ALIGN_CENTER)
13276 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13277 else if (vp_playfield->align == ALIGN_RIGHT)
13278 vp_playfield->x += playfield_width - vp_playfield->width;
13280 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13281 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13282 else if (vp_playfield->valign == VALIGN_MIDDLE)
13283 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13284 else if (vp_playfield->valign == VALIGN_BOTTOM)
13285 vp_playfield->y += playfield_height - vp_playfield->height;
13287 vp_playfield->x += vp_playfield->margin_left;
13288 vp_playfield->y += vp_playfield->margin_top;
13290 // adjust individual playfield borders if only default border is specified
13292 if (vp_playfield->border_left == -1)
13293 vp_playfield->border_left = vp_playfield->border_size;
13294 if (vp_playfield->border_right == -1)
13295 vp_playfield->border_right = vp_playfield->border_size;
13296 if (vp_playfield->border_top == -1)
13297 vp_playfield->border_top = vp_playfield->border_size;
13298 if (vp_playfield->border_bottom == -1)
13299 vp_playfield->border_bottom = vp_playfield->border_size;
13301 // set dynamic playfield borders if borders are specified as undefined
13302 // (but only if window size was dynamic and playfield size was static)
13304 if (dynamic_window_width && !dynamic_playfield_width)
13306 if (vp_playfield->border_left == -1)
13308 vp_playfield->border_left = (vp_playfield->x -
13309 vp_playfield->margin_left);
13310 vp_playfield->x -= vp_playfield->border_left;
13311 vp_playfield->width += vp_playfield->border_left;
13314 if (vp_playfield->border_right == -1)
13316 vp_playfield->border_right = (vp_window->width -
13318 vp_playfield->width -
13319 vp_playfield->margin_right);
13320 vp_playfield->width += vp_playfield->border_right;
13324 if (dynamic_window_height && !dynamic_playfield_height)
13326 if (vp_playfield->border_top == -1)
13328 vp_playfield->border_top = (vp_playfield->y -
13329 vp_playfield->margin_top);
13330 vp_playfield->y -= vp_playfield->border_top;
13331 vp_playfield->height += vp_playfield->border_top;
13334 if (vp_playfield->border_bottom == -1)
13336 vp_playfield->border_bottom = (vp_window->height -
13338 vp_playfield->height -
13339 vp_playfield->margin_bottom);
13340 vp_playfield->height += vp_playfield->border_bottom;
13344 // adjust playfield size to be a multiple of a defined alignment tile size
13346 int align_size = vp_playfield->align_size;
13347 int playfield_xtiles = vp_playfield->width / align_size;
13348 int playfield_ytiles = vp_playfield->height / align_size;
13349 int playfield_width_corrected = playfield_xtiles * align_size;
13350 int playfield_height_corrected = playfield_ytiles * align_size;
13351 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13352 i == GFX_SPECIAL_ARG_EDITOR);
13354 if (is_playfield_mode &&
13355 dynamic_playfield_width &&
13356 vp_playfield->width != playfield_width_corrected)
13358 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13360 vp_playfield->width = playfield_width_corrected;
13362 if (vp_playfield->align == ALIGN_LEFT)
13364 vp_playfield->border_left += playfield_xdiff;
13366 else if (vp_playfield->align == ALIGN_RIGHT)
13368 vp_playfield->border_right += playfield_xdiff;
13370 else if (vp_playfield->align == ALIGN_CENTER)
13372 int border_left_diff = playfield_xdiff / 2;
13373 int border_right_diff = playfield_xdiff - border_left_diff;
13375 vp_playfield->border_left += border_left_diff;
13376 vp_playfield->border_right += border_right_diff;
13380 if (is_playfield_mode &&
13381 dynamic_playfield_height &&
13382 vp_playfield->height != playfield_height_corrected)
13384 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13386 vp_playfield->height = playfield_height_corrected;
13388 if (vp_playfield->valign == VALIGN_TOP)
13390 vp_playfield->border_top += playfield_ydiff;
13392 else if (vp_playfield->align == VALIGN_BOTTOM)
13394 vp_playfield->border_right += playfield_ydiff;
13396 else if (vp_playfield->align == VALIGN_MIDDLE)
13398 int border_top_diff = playfield_ydiff / 2;
13399 int border_bottom_diff = playfield_ydiff - border_top_diff;
13401 vp_playfield->border_top += border_top_diff;
13402 vp_playfield->border_bottom += border_bottom_diff;
13406 // adjust door positions according to specified alignment
13408 for (j = 0; j < 2; j++)
13410 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13412 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13413 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13414 else if (vp_door->align == ALIGN_CENTER)
13415 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13416 else if (vp_door->align == ALIGN_RIGHT)
13417 vp_door->x += vp_window->width - vp_door->width;
13419 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13420 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13421 else if (vp_door->valign == VALIGN_MIDDLE)
13422 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13423 else if (vp_door->valign == VALIGN_BOTTOM)
13424 vp_door->y += vp_window->height - vp_door->height;
13429 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13433 struct XYTileSize *dst, *src;
13436 editor_buttons_xy[] =
13439 &editor.button.element_left, &editor.palette.element_left,
13440 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13443 &editor.button.element_middle, &editor.palette.element_middle,
13444 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13447 &editor.button.element_right, &editor.palette.element_right,
13448 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13455 // set default position for element buttons to element graphics
13456 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13458 if ((*editor_buttons_xy[i].dst).x == -1 &&
13459 (*editor_buttons_xy[i].dst).y == -1)
13461 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13463 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13465 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13469 // adjust editor palette rows and columns if specified to be dynamic
13471 if (editor.palette.cols == -1)
13473 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13474 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13475 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13477 editor.palette.cols = (vp_width - sc_width) / bt_width;
13479 if (editor.palette.x == -1)
13481 int palette_width = editor.palette.cols * bt_width + sc_width;
13483 editor.palette.x = (vp_width - palette_width) / 2;
13487 if (editor.palette.rows == -1)
13489 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13490 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13491 int tx_height = getFontHeight(FONT_TEXT_2);
13493 editor.palette.rows = (vp_height - tx_height) / bt_height;
13495 if (editor.palette.y == -1)
13497 int palette_height = editor.palette.rows * bt_height + tx_height;
13499 editor.palette.y = (vp_height - palette_height) / 2;
13504 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13505 boolean initialize)
13507 // special case: check if network and preview player positions are redefined,
13508 // to compare this later against the main menu level preview being redefined
13509 struct TokenIntPtrInfo menu_config_players[] =
13511 { "main.network_players.x", &menu.main.network_players.redefined },
13512 { "main.network_players.y", &menu.main.network_players.redefined },
13513 { "main.preview_players.x", &menu.main.preview_players.redefined },
13514 { "main.preview_players.y", &menu.main.preview_players.redefined },
13515 { "preview.x", &preview.redefined },
13516 { "preview.y", &preview.redefined }
13522 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13523 *menu_config_players[i].value = FALSE;
13527 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13528 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13529 *menu_config_players[i].value = TRUE;
13533 static void InitMenuDesignSettings_PreviewPlayers(void)
13535 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13538 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13540 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13543 static void LoadMenuDesignSettingsFromFilename(char *filename)
13545 static struct TitleFadingInfo tfi;
13546 static struct TitleMessageInfo tmi;
13547 static struct TokenInfo title_tokens[] =
13549 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13550 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13551 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13552 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13553 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13557 static struct TokenInfo titlemessage_tokens[] =
13559 { TYPE_INTEGER, &tmi.x, ".x" },
13560 { TYPE_INTEGER, &tmi.y, ".y" },
13561 { TYPE_INTEGER, &tmi.width, ".width" },
13562 { TYPE_INTEGER, &tmi.height, ".height" },
13563 { TYPE_INTEGER, &tmi.chars, ".chars" },
13564 { TYPE_INTEGER, &tmi.lines, ".lines" },
13565 { TYPE_INTEGER, &tmi.align, ".align" },
13566 { TYPE_INTEGER, &tmi.valign, ".valign" },
13567 { TYPE_INTEGER, &tmi.font, ".font" },
13568 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13569 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13570 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13571 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13572 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13573 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13574 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13575 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13576 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13582 struct TitleFadingInfo *info;
13587 // initialize first titles from "enter screen" definitions, if defined
13588 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13589 { &title_first_default, "menu.enter_screen.TITLE" },
13591 // initialize title screens from "next screen" definitions, if defined
13592 { &title_initial_default, "menu.next_screen.TITLE" },
13593 { &title_default, "menu.next_screen.TITLE" },
13599 struct TitleMessageInfo *array;
13602 titlemessage_arrays[] =
13604 // initialize first titles from "enter screen" definitions, if defined
13605 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13606 { titlescreen_first, "menu.enter_screen.TITLE" },
13607 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13608 { titlemessage_first, "menu.enter_screen.TITLE" },
13610 // initialize titles from "next screen" definitions, if defined
13611 { titlescreen_initial, "menu.next_screen.TITLE" },
13612 { titlescreen, "menu.next_screen.TITLE" },
13613 { titlemessage_initial, "menu.next_screen.TITLE" },
13614 { titlemessage, "menu.next_screen.TITLE" },
13616 // overwrite titles with title definitions, if defined
13617 { titlescreen_initial_first, "[title_initial]" },
13618 { titlescreen_first, "[title]" },
13619 { titlemessage_initial_first, "[title_initial]" },
13620 { titlemessage_first, "[title]" },
13622 { titlescreen_initial, "[title_initial]" },
13623 { titlescreen, "[title]" },
13624 { titlemessage_initial, "[title_initial]" },
13625 { titlemessage, "[title]" },
13627 // overwrite titles with title screen/message definitions, if defined
13628 { titlescreen_initial_first, "[titlescreen_initial]" },
13629 { titlescreen_first, "[titlescreen]" },
13630 { titlemessage_initial_first, "[titlemessage_initial]" },
13631 { titlemessage_first, "[titlemessage]" },
13633 { titlescreen_initial, "[titlescreen_initial]" },
13634 { titlescreen, "[titlescreen]" },
13635 { titlemessage_initial, "[titlemessage_initial]" },
13636 { titlemessage, "[titlemessage]" },
13640 SetupFileHash *setup_file_hash;
13643 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13646 // the following initializes hierarchical values from dynamic configuration
13648 // special case: initialize with default values that may be overwritten
13649 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13650 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13652 struct TokenIntPtrInfo menu_config[] =
13654 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13655 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13656 { "menu.list_size", &menu.list_size[i] }
13659 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13661 char *token = menu_config[j].token;
13662 char *value = getHashEntry(setup_file_hash, token);
13665 *menu_config[j].value = get_integer_from_string(value);
13669 // special case: initialize with default values that may be overwritten
13670 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13671 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13673 struct TokenIntPtrInfo menu_config[] =
13675 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13676 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13677 { "menu.list_size.INFO", &menu.list_size_info[i] },
13678 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13679 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13682 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13684 char *token = menu_config[j].token;
13685 char *value = getHashEntry(setup_file_hash, token);
13688 *menu_config[j].value = get_integer_from_string(value);
13692 // special case: initialize with default values that may be overwritten
13693 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13694 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13696 struct TokenIntPtrInfo menu_config[] =
13698 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13699 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13702 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13704 char *token = menu_config[j].token;
13705 char *value = getHashEntry(setup_file_hash, token);
13708 *menu_config[j].value = get_integer_from_string(value);
13712 // special case: initialize with default values that may be overwritten
13713 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13714 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13716 struct TokenIntPtrInfo menu_config[] =
13718 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13719 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13720 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13721 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13722 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13723 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13724 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13725 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13726 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13727 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13730 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13732 char *token = menu_config[j].token;
13733 char *value = getHashEntry(setup_file_hash, token);
13736 *menu_config[j].value = get_integer_from_string(value);
13740 // special case: initialize with default values that may be overwritten
13741 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13742 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13744 struct TokenIntPtrInfo menu_config[] =
13746 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13747 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13748 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13749 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13750 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13751 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13752 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13753 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13754 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13757 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13759 char *token = menu_config[j].token;
13760 char *value = getHashEntry(setup_file_hash, token);
13763 *menu_config[j].value = get_token_parameter_value(token, value);
13767 // special case: initialize with default values that may be overwritten
13768 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13769 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13773 char *token_prefix;
13774 struct RectWithBorder *struct_ptr;
13778 { "viewport.window", &viewport.window[i] },
13779 { "viewport.playfield", &viewport.playfield[i] },
13780 { "viewport.door_1", &viewport.door_1[i] },
13781 { "viewport.door_2", &viewport.door_2[i] }
13784 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13786 struct TokenIntPtrInfo vp_config[] =
13788 { ".x", &vp_struct[j].struct_ptr->x },
13789 { ".y", &vp_struct[j].struct_ptr->y },
13790 { ".width", &vp_struct[j].struct_ptr->width },
13791 { ".height", &vp_struct[j].struct_ptr->height },
13792 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13793 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13794 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13795 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13796 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13797 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13798 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13799 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13800 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13801 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13802 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13803 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13804 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13805 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13806 { ".align", &vp_struct[j].struct_ptr->align },
13807 { ".valign", &vp_struct[j].struct_ptr->valign }
13810 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13812 char *token = getStringCat2(vp_struct[j].token_prefix,
13813 vp_config[k].token);
13814 char *value = getHashEntry(setup_file_hash, token);
13817 *vp_config[k].value = get_token_parameter_value(token, value);
13824 // special case: initialize with default values that may be overwritten
13825 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13826 for (i = 0; title_info[i].info != NULL; i++)
13828 struct TitleFadingInfo *info = title_info[i].info;
13829 char *base_token = title_info[i].text;
13831 for (j = 0; title_tokens[j].type != -1; j++)
13833 char *token = getStringCat2(base_token, title_tokens[j].text);
13834 char *value = getHashEntry(setup_file_hash, token);
13838 int parameter_value = get_token_parameter_value(token, value);
13842 *(int *)title_tokens[j].value = (int)parameter_value;
13851 // special case: initialize with default values that may be overwritten
13852 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13853 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13855 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13856 char *base_token = titlemessage_arrays[i].text;
13858 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13860 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13861 char *value = getHashEntry(setup_file_hash, token);
13865 int parameter_value = get_token_parameter_value(token, value);
13867 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13871 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13872 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13874 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13884 // read (and overwrite with) values that may be specified in config file
13885 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13887 // special case: check if network and preview player positions are redefined
13888 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13890 freeSetupFileHash(setup_file_hash);
13893 void LoadMenuDesignSettings(void)
13895 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13897 InitMenuDesignSettings_Static();
13898 InitMenuDesignSettings_SpecialPreProcessing();
13899 InitMenuDesignSettings_PreviewPlayers();
13901 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13903 // first look for special settings configured in level series config
13904 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13906 if (fileExists(filename_base))
13907 LoadMenuDesignSettingsFromFilename(filename_base);
13910 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13912 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13913 LoadMenuDesignSettingsFromFilename(filename_local);
13915 InitMenuDesignSettings_SpecialPostProcessing();
13918 void LoadMenuDesignSettings_AfterGraphics(void)
13920 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13923 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13924 boolean ignore_defaults)
13928 for (i = 0; sound_config_vars[i].token != NULL; i++)
13930 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13932 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13933 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13937 *sound_config_vars[i].value =
13938 get_token_parameter_value(sound_config_vars[i].token, value);
13942 void InitSoundSettings_Static(void)
13944 // always start with reliable default values from static default config
13945 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13948 static void LoadSoundSettingsFromFilename(char *filename)
13950 SetupFileHash *setup_file_hash;
13952 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13955 // read (and overwrite with) values that may be specified in config file
13956 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13958 freeSetupFileHash(setup_file_hash);
13961 void LoadSoundSettings(void)
13963 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13965 InitSoundSettings_Static();
13967 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13969 // first look for special settings configured in level series config
13970 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13972 if (fileExists(filename_base))
13973 LoadSoundSettingsFromFilename(filename_base);
13976 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13978 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13979 LoadSoundSettingsFromFilename(filename_local);
13982 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13984 char *filename = getEditorSetupFilename();
13985 SetupFileList *setup_file_list, *list;
13986 SetupFileHash *element_hash;
13987 int num_unknown_tokens = 0;
13990 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13993 element_hash = newSetupFileHash();
13995 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13996 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13998 // determined size may be larger than needed (due to unknown elements)
14000 for (list = setup_file_list; list != NULL; list = list->next)
14003 // add space for up to 3 more elements for padding that may be needed
14004 *num_elements += 3;
14006 // free memory for old list of elements, if needed
14007 checked_free(*elements);
14009 // allocate memory for new list of elements
14010 *elements = checked_malloc(*num_elements * sizeof(int));
14013 for (list = setup_file_list; list != NULL; list = list->next)
14015 char *value = getHashEntry(element_hash, list->token);
14017 if (value == NULL) // try to find obsolete token mapping
14019 char *mapped_token = get_mapped_token(list->token);
14021 if (mapped_token != NULL)
14023 value = getHashEntry(element_hash, mapped_token);
14025 free(mapped_token);
14031 (*elements)[(*num_elements)++] = atoi(value);
14035 if (num_unknown_tokens == 0)
14038 Warn("unknown token(s) found in config file:");
14039 Warn("- config file: '%s'", filename);
14041 num_unknown_tokens++;
14044 Warn("- token: '%s'", list->token);
14048 if (num_unknown_tokens > 0)
14051 while (*num_elements % 4) // pad with empty elements, if needed
14052 (*elements)[(*num_elements)++] = EL_EMPTY;
14054 freeSetupFileList(setup_file_list);
14055 freeSetupFileHash(element_hash);
14058 for (i = 0; i < *num_elements; i++)
14059 Debug("editor", "element '%s' [%d]\n",
14060 element_info[(*elements)[i]].token_name, (*elements)[i]);
14064 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14067 SetupFileHash *setup_file_hash = NULL;
14068 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14069 char *filename_music, *filename_prefix, *filename_info;
14075 token_to_value_ptr[] =
14077 { "title_header", &tmp_music_file_info.title_header },
14078 { "artist_header", &tmp_music_file_info.artist_header },
14079 { "album_header", &tmp_music_file_info.album_header },
14080 { "year_header", &tmp_music_file_info.year_header },
14081 { "played_header", &tmp_music_file_info.played_header },
14083 { "title", &tmp_music_file_info.title },
14084 { "artist", &tmp_music_file_info.artist },
14085 { "album", &tmp_music_file_info.album },
14086 { "year", &tmp_music_file_info.year },
14087 { "played", &tmp_music_file_info.played },
14093 filename_music = (is_sound ? getCustomSoundFilename(basename) :
14094 getCustomMusicFilename(basename));
14096 if (filename_music == NULL)
14099 // ---------- try to replace file extension ----------
14101 filename_prefix = getStringCopy(filename_music);
14102 if (strrchr(filename_prefix, '.') != NULL)
14103 *strrchr(filename_prefix, '.') = '\0';
14104 filename_info = getStringCat2(filename_prefix, ".txt");
14106 if (fileExists(filename_info))
14107 setup_file_hash = loadSetupFileHash(filename_info);
14109 free(filename_prefix);
14110 free(filename_info);
14112 if (setup_file_hash == NULL)
14114 // ---------- try to add file extension ----------
14116 filename_prefix = getStringCopy(filename_music);
14117 filename_info = getStringCat2(filename_prefix, ".txt");
14119 if (fileExists(filename_info))
14120 setup_file_hash = loadSetupFileHash(filename_info);
14122 free(filename_prefix);
14123 free(filename_info);
14126 if (setup_file_hash == NULL)
14129 // ---------- music file info found ----------
14131 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14133 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14135 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14137 *token_to_value_ptr[i].value_ptr =
14138 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14141 tmp_music_file_info.basename = getStringCopy(basename);
14142 tmp_music_file_info.music = music;
14143 tmp_music_file_info.is_sound = is_sound;
14145 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14146 *new_music_file_info = tmp_music_file_info;
14148 return new_music_file_info;
14151 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14153 return get_music_file_info_ext(basename, music, FALSE);
14156 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14158 return get_music_file_info_ext(basename, sound, TRUE);
14161 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14162 char *basename, boolean is_sound)
14164 for (; list != NULL; list = list->next)
14165 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14171 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14173 return music_info_listed_ext(list, basename, FALSE);
14176 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14178 return music_info_listed_ext(list, basename, TRUE);
14181 void LoadMusicInfo(void)
14183 int num_music_noconf = getMusicListSize_NoConf();
14184 int num_music = getMusicListSize();
14185 int num_sounds = getSoundListSize();
14186 struct FileInfo *music, *sound;
14187 struct MusicFileInfo *next, **new;
14191 while (music_file_info != NULL)
14193 next = music_file_info->next;
14195 checked_free(music_file_info->basename);
14197 checked_free(music_file_info->title_header);
14198 checked_free(music_file_info->artist_header);
14199 checked_free(music_file_info->album_header);
14200 checked_free(music_file_info->year_header);
14201 checked_free(music_file_info->played_header);
14203 checked_free(music_file_info->title);
14204 checked_free(music_file_info->artist);
14205 checked_free(music_file_info->album);
14206 checked_free(music_file_info->year);
14207 checked_free(music_file_info->played);
14209 free(music_file_info);
14211 music_file_info = next;
14214 new = &music_file_info;
14216 // get (configured or unconfigured) music file info for all levels
14217 for (i = leveldir_current->first_level;
14218 i <= leveldir_current->last_level; i++)
14222 if (levelset.music[i] != MUS_UNDEFINED)
14224 // get music file info for configured level music
14225 music_nr = levelset.music[i];
14227 else if (num_music_noconf > 0)
14229 // get music file info for unconfigured level music
14230 int level_pos = i - leveldir_current->first_level;
14232 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14239 char *basename = getMusicInfoEntryFilename(music_nr);
14241 if (basename == NULL)
14244 if (!music_info_listed(music_file_info, basename))
14246 *new = get_music_file_info(basename, music_nr);
14249 new = &(*new)->next;
14253 // get music file info for all remaining configured music files
14254 for (i = 0; i < num_music; i++)
14256 music = getMusicListEntry(i);
14258 if (music->filename == NULL)
14261 if (strEqual(music->filename, UNDEFINED_FILENAME))
14264 // a configured file may be not recognized as music
14265 if (!FileIsMusic(music->filename))
14268 if (!music_info_listed(music_file_info, music->filename))
14270 *new = get_music_file_info(music->filename, i);
14273 new = &(*new)->next;
14277 // get sound file info for all configured sound files
14278 for (i = 0; i < num_sounds; i++)
14280 sound = getSoundListEntry(i);
14282 if (sound->filename == NULL)
14285 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14288 // a configured file may be not recognized as sound
14289 if (!FileIsSound(sound->filename))
14292 if (!sound_info_listed(music_file_info, sound->filename))
14294 *new = get_sound_file_info(sound->filename, i);
14296 new = &(*new)->next;
14300 // add pointers to previous list nodes
14302 struct MusicFileInfo *node = music_file_info;
14304 while (node != NULL)
14307 node->next->prev = node;
14313 static void add_helpanim_entry(int element, int action, int direction,
14314 int delay, int *num_list_entries)
14316 struct HelpAnimInfo *new_list_entry;
14317 (*num_list_entries)++;
14320 checked_realloc(helpanim_info,
14321 *num_list_entries * sizeof(struct HelpAnimInfo));
14322 new_list_entry = &helpanim_info[*num_list_entries - 1];
14324 new_list_entry->element = element;
14325 new_list_entry->action = action;
14326 new_list_entry->direction = direction;
14327 new_list_entry->delay = delay;
14330 static void print_unknown_token(char *filename, char *token, int token_nr)
14335 Warn("unknown token(s) found in config file:");
14336 Warn("- config file: '%s'", filename);
14339 Warn("- token: '%s'", token);
14342 static void print_unknown_token_end(int token_nr)
14348 void LoadHelpAnimInfo(void)
14350 char *filename = getHelpAnimFilename();
14351 SetupFileList *setup_file_list = NULL, *list;
14352 SetupFileHash *element_hash, *action_hash, *direction_hash;
14353 int num_list_entries = 0;
14354 int num_unknown_tokens = 0;
14357 if (fileExists(filename))
14358 setup_file_list = loadSetupFileList(filename);
14360 if (setup_file_list == NULL)
14362 // use reliable default values from static configuration
14363 SetupFileList *insert_ptr;
14365 insert_ptr = setup_file_list =
14366 newSetupFileList(helpanim_config[0].token,
14367 helpanim_config[0].value);
14369 for (i = 1; helpanim_config[i].token; i++)
14370 insert_ptr = addListEntry(insert_ptr,
14371 helpanim_config[i].token,
14372 helpanim_config[i].value);
14375 element_hash = newSetupFileHash();
14376 action_hash = newSetupFileHash();
14377 direction_hash = newSetupFileHash();
14379 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14380 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14382 for (i = 0; i < NUM_ACTIONS; i++)
14383 setHashEntry(action_hash, element_action_info[i].suffix,
14384 i_to_a(element_action_info[i].value));
14386 // do not store direction index (bit) here, but direction value!
14387 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14388 setHashEntry(direction_hash, element_direction_info[i].suffix,
14389 i_to_a(1 << element_direction_info[i].value));
14391 for (list = setup_file_list; list != NULL; list = list->next)
14393 char *element_token, *action_token, *direction_token;
14394 char *element_value, *action_value, *direction_value;
14395 int delay = atoi(list->value);
14397 if (strEqual(list->token, "end"))
14399 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14404 /* first try to break element into element/action/direction parts;
14405 if this does not work, also accept combined "element[.act][.dir]"
14406 elements (like "dynamite.active"), which are unique elements */
14408 if (strchr(list->token, '.') == NULL) // token contains no '.'
14410 element_value = getHashEntry(element_hash, list->token);
14411 if (element_value != NULL) // element found
14412 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14413 &num_list_entries);
14416 // no further suffixes found -- this is not an element
14417 print_unknown_token(filename, list->token, num_unknown_tokens++);
14423 // token has format "<prefix>.<something>"
14425 action_token = strchr(list->token, '.'); // suffix may be action ...
14426 direction_token = action_token; // ... or direction
14428 element_token = getStringCopy(list->token);
14429 *strchr(element_token, '.') = '\0';
14431 element_value = getHashEntry(element_hash, element_token);
14433 if (element_value == NULL) // this is no element
14435 element_value = getHashEntry(element_hash, list->token);
14436 if (element_value != NULL) // combined element found
14437 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14438 &num_list_entries);
14440 print_unknown_token(filename, list->token, num_unknown_tokens++);
14442 free(element_token);
14447 action_value = getHashEntry(action_hash, action_token);
14449 if (action_value != NULL) // action found
14451 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14452 &num_list_entries);
14454 free(element_token);
14459 direction_value = getHashEntry(direction_hash, direction_token);
14461 if (direction_value != NULL) // direction found
14463 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14464 &num_list_entries);
14466 free(element_token);
14471 if (strchr(action_token + 1, '.') == NULL)
14473 // no further suffixes found -- this is not an action nor direction
14475 element_value = getHashEntry(element_hash, list->token);
14476 if (element_value != NULL) // combined element found
14477 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14478 &num_list_entries);
14480 print_unknown_token(filename, list->token, num_unknown_tokens++);
14482 free(element_token);
14487 // token has format "<prefix>.<suffix>.<something>"
14489 direction_token = strchr(action_token + 1, '.');
14491 action_token = getStringCopy(action_token);
14492 *strchr(action_token + 1, '.') = '\0';
14494 action_value = getHashEntry(action_hash, action_token);
14496 if (action_value == NULL) // this is no action
14498 element_value = getHashEntry(element_hash, list->token);
14499 if (element_value != NULL) // combined element found
14500 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14501 &num_list_entries);
14503 print_unknown_token(filename, list->token, num_unknown_tokens++);
14505 free(element_token);
14506 free(action_token);
14511 direction_value = getHashEntry(direction_hash, direction_token);
14513 if (direction_value != NULL) // direction found
14515 add_helpanim_entry(atoi(element_value), atoi(action_value),
14516 atoi(direction_value), delay, &num_list_entries);
14518 free(element_token);
14519 free(action_token);
14524 // this is no direction
14526 element_value = getHashEntry(element_hash, list->token);
14527 if (element_value != NULL) // combined element found
14528 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14529 &num_list_entries);
14531 print_unknown_token(filename, list->token, num_unknown_tokens++);
14533 free(element_token);
14534 free(action_token);
14537 print_unknown_token_end(num_unknown_tokens);
14539 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14540 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14542 freeSetupFileList(setup_file_list);
14543 freeSetupFileHash(element_hash);
14544 freeSetupFileHash(action_hash);
14545 freeSetupFileHash(direction_hash);
14548 for (i = 0; i < num_list_entries; i++)
14549 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14550 EL_NAME(helpanim_info[i].element),
14551 helpanim_info[i].element,
14552 helpanim_info[i].action,
14553 helpanim_info[i].direction,
14554 helpanim_info[i].delay);
14558 void LoadHelpTextInfo(void)
14560 char *filename = getHelpTextFilename();
14563 if (helptext_info != NULL)
14565 freeSetupFileHash(helptext_info);
14566 helptext_info = NULL;
14569 if (fileExists(filename))
14570 helptext_info = loadSetupFileHash(filename);
14572 if (helptext_info == NULL)
14574 // use reliable default values from static configuration
14575 helptext_info = newSetupFileHash();
14577 for (i = 0; helptext_config[i].token; i++)
14578 setHashEntry(helptext_info,
14579 helptext_config[i].token,
14580 helptext_config[i].value);
14584 BEGIN_HASH_ITERATION(helptext_info, itr)
14586 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14587 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14589 END_HASH_ITERATION(hash, itr)
14594 // ----------------------------------------------------------------------------
14596 // ----------------------------------------------------------------------------
14598 #define MAX_NUM_CONVERT_LEVELS 1000
14600 void ConvertLevels(void)
14602 static LevelDirTree *convert_leveldir = NULL;
14603 static int convert_level_nr = -1;
14604 static int num_levels_handled = 0;
14605 static int num_levels_converted = 0;
14606 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14609 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14610 global.convert_leveldir);
14612 if (convert_leveldir == NULL)
14613 Fail("no such level identifier: '%s'", global.convert_leveldir);
14615 leveldir_current = convert_leveldir;
14617 if (global.convert_level_nr != -1)
14619 convert_leveldir->first_level = global.convert_level_nr;
14620 convert_leveldir->last_level = global.convert_level_nr;
14623 convert_level_nr = convert_leveldir->first_level;
14625 PrintLine("=", 79);
14626 Print("Converting levels\n");
14627 PrintLine("-", 79);
14628 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14629 Print("Level series name: '%s'\n", convert_leveldir->name);
14630 Print("Level series author: '%s'\n", convert_leveldir->author);
14631 Print("Number of levels: %d\n", convert_leveldir->levels);
14632 PrintLine("=", 79);
14635 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14636 levels_failed[i] = FALSE;
14638 while (convert_level_nr <= convert_leveldir->last_level)
14640 char *level_filename;
14643 level_nr = convert_level_nr++;
14645 Print("Level %03d: ", level_nr);
14647 LoadLevel(level_nr);
14648 if (level.no_level_file || level.no_valid_file)
14650 Print("(no level)\n");
14654 Print("converting level ... ");
14657 // special case: conversion of some EMC levels as requested by ACME
14658 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14661 level_filename = getDefaultLevelFilename(level_nr);
14662 new_level = !fileExists(level_filename);
14666 SaveLevel(level_nr);
14668 num_levels_converted++;
14670 Print("converted.\n");
14674 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14675 levels_failed[level_nr] = TRUE;
14677 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14680 num_levels_handled++;
14684 PrintLine("=", 79);
14685 Print("Number of levels handled: %d\n", num_levels_handled);
14686 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14687 (num_levels_handled ?
14688 num_levels_converted * 100 / num_levels_handled : 0));
14689 PrintLine("-", 79);
14690 Print("Summary (for automatic parsing by scripts):\n");
14691 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14692 convert_leveldir->identifier, num_levels_converted,
14693 num_levels_handled,
14694 (num_levels_handled ?
14695 num_levels_converted * 100 / num_levels_handled : 0));
14697 if (num_levels_handled != num_levels_converted)
14699 Print(", FAILED:");
14700 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14701 if (levels_failed[i])
14706 PrintLine("=", 79);
14708 CloseAllAndExit(0);
14712 // ----------------------------------------------------------------------------
14713 // create and save images for use in level sketches (raw BMP format)
14714 // ----------------------------------------------------------------------------
14716 void CreateLevelSketchImages(void)
14722 InitElementPropertiesGfxElement();
14724 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14725 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14727 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14729 int element = getMappedElement(i);
14730 char basename1[16];
14731 char basename2[16];
14735 sprintf(basename1, "%04d.bmp", i);
14736 sprintf(basename2, "%04ds.bmp", i);
14738 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14739 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14741 DrawSizedElement(0, 0, element, TILESIZE);
14742 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14744 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14745 Fail("cannot save level sketch image file '%s'", filename1);
14747 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14748 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14750 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14751 Fail("cannot save level sketch image file '%s'", filename2);
14756 // create corresponding SQL statements (for normal and small images)
14759 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14760 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14763 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14764 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14766 // optional: create content for forum level sketch demonstration post
14768 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14771 FreeBitmap(bitmap1);
14772 FreeBitmap(bitmap2);
14775 fprintf(stderr, "\n");
14777 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14779 CloseAllAndExit(0);
14783 // ----------------------------------------------------------------------------
14784 // create and save images for element collecting animations (raw BMP format)
14785 // ----------------------------------------------------------------------------
14787 static boolean createCollectImage(int element)
14789 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14792 void CreateCollectElementImages(void)
14796 int anim_frames = num_steps - 1;
14797 int tile_size = TILESIZE;
14798 int anim_width = tile_size * anim_frames;
14799 int anim_height = tile_size;
14800 int num_collect_images = 0;
14801 int pos_collect_images = 0;
14803 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14804 if (createCollectImage(i))
14805 num_collect_images++;
14807 Info("Creating %d element collecting animation images ...",
14808 num_collect_images);
14810 int dst_width = anim_width * 2;
14811 int dst_height = anim_height * num_collect_images / 2;
14812 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14813 char *basename_bmp = "RocksCollect.bmp";
14814 char *basename_png = "RocksCollect.png";
14815 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14816 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14817 int len_filename_bmp = strlen(filename_bmp);
14818 int len_filename_png = strlen(filename_png);
14819 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14820 char cmd_convert[max_command_len];
14822 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14826 // force using RGBA surface for destination bitmap
14827 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14828 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14830 dst_bitmap->surface =
14831 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14833 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14835 if (!createCollectImage(i))
14838 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14839 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14840 int graphic = el2img(i);
14841 char *token_name = element_info[i].token_name;
14842 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14843 Bitmap *src_bitmap;
14846 Info("- creating collecting image for '%s' ...", token_name);
14848 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14850 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14851 tile_size, tile_size, 0, 0);
14853 // force using RGBA surface for temporary bitmap (using transparent black)
14854 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14855 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14857 tmp_bitmap->surface =
14858 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14860 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14862 for (j = 0; j < anim_frames; j++)
14864 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14865 int frame_size = frame_size_final * num_steps;
14866 int offset = (tile_size - frame_size_final) / 2;
14867 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14869 while (frame_size > frame_size_final)
14873 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14875 FreeBitmap(frame_bitmap);
14877 frame_bitmap = half_bitmap;
14880 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14881 frame_size_final, frame_size_final,
14882 dst_x + j * tile_size + offset, dst_y + offset);
14884 FreeBitmap(frame_bitmap);
14887 tmp_bitmap->surface_masked = NULL;
14889 FreeBitmap(tmp_bitmap);
14891 pos_collect_images++;
14894 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14895 Fail("cannot save element collecting image file '%s'", filename_bmp);
14897 FreeBitmap(dst_bitmap);
14899 Info("Converting image file from BMP to PNG ...");
14901 if (system(cmd_convert) != 0)
14902 Fail("converting image file failed");
14904 unlink(filename_bmp);
14908 CloseAllAndExit(0);
14912 // ----------------------------------------------------------------------------
14913 // create and save images for custom and group elements (raw BMP format)
14914 // ----------------------------------------------------------------------------
14916 void CreateCustomElementImages(char *directory)
14918 char *src_basename = "RocksCE-template.ilbm";
14919 char *dst_basename = "RocksCE.bmp";
14920 char *src_filename = getPath2(directory, src_basename);
14921 char *dst_filename = getPath2(directory, dst_basename);
14922 Bitmap *src_bitmap;
14924 int yoffset_ce = 0;
14925 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14928 InitVideoDefaults();
14930 ReCreateBitmap(&backbuffer, video.width, video.height);
14932 src_bitmap = LoadImage(src_filename);
14934 bitmap = CreateBitmap(TILEX * 16 * 2,
14935 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14938 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14945 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14946 TILEX * x, TILEY * y + yoffset_ce);
14948 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14950 TILEX * x + TILEX * 16,
14951 TILEY * y + yoffset_ce);
14953 for (j = 2; j >= 0; j--)
14957 BlitBitmap(src_bitmap, bitmap,
14958 TILEX + c * 7, 0, 6, 10,
14959 TILEX * x + 6 + j * 7,
14960 TILEY * y + 11 + yoffset_ce);
14962 BlitBitmap(src_bitmap, bitmap,
14963 TILEX + c * 8, TILEY, 6, 10,
14964 TILEX * 16 + TILEX * x + 6 + j * 8,
14965 TILEY * y + 10 + yoffset_ce);
14971 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14978 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14979 TILEX * x, TILEY * y + yoffset_ge);
14981 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14983 TILEX * x + TILEX * 16,
14984 TILEY * y + yoffset_ge);
14986 for (j = 1; j >= 0; j--)
14990 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14991 TILEX * x + 6 + j * 10,
14992 TILEY * y + 11 + yoffset_ge);
14994 BlitBitmap(src_bitmap, bitmap,
14995 TILEX + c * 8, TILEY + 12, 6, 10,
14996 TILEX * 16 + TILEX * x + 10 + j * 8,
14997 TILEY * y + 10 + yoffset_ge);
15003 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15004 Fail("cannot save CE graphics file '%s'", dst_filename);
15006 FreeBitmap(bitmap);
15008 CloseAllAndExit(0);