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_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(25),
321 &li.bd_creatures_start_backwards, FALSE
325 TYPE_BOOLEAN, CONF_VALUE_8_BIT(26),
326 &li.bd_creatures_turn_on_hatching, FALSE
330 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
331 &li.bd_creatures_auto_turn_delay, 0
341 static struct LevelFileConfigInfo chunk_config_ELEM[] =
343 // (these values are the same for each player)
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
347 &li.block_last_field, FALSE // default case for EM levels
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
352 &li.sp_block_last_field, TRUE // default case for SP levels
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
357 &li.instant_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
362 &li.can_pass_to_walkable, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
367 &li.block_snap_field, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
372 &li.continuous_snapping, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
377 &li.shifted_relocation, FALSE
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
382 &li.lazy_relocation, FALSE
386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
387 &li.finish_dig_collect, TRUE
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
392 &li.keep_walkable_ce, FALSE
395 // (these values are different for each player)
398 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
399 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
404 &li.initial_player_gravity[0], FALSE
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
409 &li.use_start_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
414 &li.start_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
419 &li.use_artwork_element[0], FALSE
423 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
424 &li.artwork_element[0], EL_PLAYER_1
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
429 &li.use_explosion_element[0], FALSE
433 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
434 &li.explosion_element[0], EL_PLAYER_1
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
439 &li.use_initial_inventory[0], FALSE
443 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
444 &li.initial_inventory_size[0], 1
448 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
449 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
450 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
455 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
456 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
461 &li.initial_player_gravity[1], FALSE
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
466 &li.use_start_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
471 &li.start_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
476 &li.use_artwork_element[1], FALSE
480 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
481 &li.artwork_element[1], EL_PLAYER_2
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
486 &li.use_explosion_element[1], FALSE
490 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
491 &li.explosion_element[1], EL_PLAYER_2
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
496 &li.use_initial_inventory[1], FALSE
500 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
501 &li.initial_inventory_size[1], 1
505 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
506 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
507 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
512 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
513 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
518 &li.initial_player_gravity[2], FALSE
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
523 &li.use_start_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
528 &li.start_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
533 &li.use_artwork_element[2], FALSE
537 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
538 &li.artwork_element[2], EL_PLAYER_3
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
543 &li.use_explosion_element[2], FALSE
547 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
548 &li.explosion_element[2], EL_PLAYER_3
552 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
553 &li.use_initial_inventory[2], FALSE
557 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
558 &li.initial_inventory_size[2], 1
562 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
563 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
564 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
569 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
570 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
575 &li.initial_player_gravity[3], FALSE
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
580 &li.use_start_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
585 &li.start_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
590 &li.use_artwork_element[3], FALSE
594 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
595 &li.artwork_element[3], EL_PLAYER_4
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
600 &li.use_explosion_element[3], FALSE
604 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
605 &li.explosion_element[3], EL_PLAYER_4
609 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
610 &li.use_initial_inventory[3], FALSE
614 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
615 &li.initial_inventory_size[3], 1
619 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
620 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
621 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
624 // (these values are only valid for BD style levels)
625 // (some values for BD style amoeba following below)
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
629 &li.bd_diagonal_movements, FALSE
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
634 &li.bd_topmost_player_active, TRUE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
639 &li.bd_pushing_prob, 25
643 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
644 &li.bd_pushing_prob_with_sweet, 100
648 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
649 &li.bd_push_mega_rock_with_sweet, FALSE
653 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
654 &li.bd_snap_element, EL_EMPTY
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.score[SC_DIAMOND_EXTRA], 20
664 EL_BD_MAGIC_WALL, -1,
665 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
666 &li.bd_magic_wall_wait_hatching, FALSE
669 EL_BD_MAGIC_WALL, -1,
670 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
671 &li.bd_magic_wall_stops_amoeba, TRUE
674 EL_BD_MAGIC_WALL, -1,
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
676 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
679 EL_BD_MAGIC_WALL, -1,
680 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
681 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
684 EL_BD_MAGIC_WALL, -1,
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
686 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
689 EL_BD_MAGIC_WALL, -1,
690 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
691 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
694 EL_BD_MAGIC_WALL, -1,
695 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
696 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
699 EL_BD_MAGIC_WALL, -1,
700 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
701 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
704 EL_BD_MAGIC_WALL, -1,
705 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
706 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
711 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
712 &li.bd_clock_extra_time, 30
716 EL_BD_VOODOO_DOLL, -1,
717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
718 &li.bd_voodoo_collects_diamonds, FALSE
721 EL_BD_VOODOO_DOLL, -1,
722 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
723 &li.bd_voodoo_hurt_kills_player, FALSE
726 EL_BD_VOODOO_DOLL, -1,
727 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
728 &li.bd_voodoo_dies_by_rock, FALSE
731 EL_BD_VOODOO_DOLL, -1,
732 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
733 &li.bd_voodoo_vanish_by_explosion, TRUE
736 EL_BD_VOODOO_DOLL, -1,
737 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
738 &li.bd_voodoo_penalty_time, 30
743 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
744 &li.bd_slime_is_predictable, TRUE
748 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
749 &li.bd_slime_permeability_rate, 100
753 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
754 &li.bd_slime_permeability_bits_c64, 0
758 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
759 &li.bd_slime_random_seed_c64, -1
763 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
764 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
768 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
769 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
773 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
774 &li.bd_slime_eats_element_2, EL_BD_ROCK
778 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
779 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
783 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
784 &li.bd_slime_eats_element_3, EL_BD_NUT
788 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
789 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
794 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
795 &li.bd_acid_eats_element, EL_BD_SAND
799 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
800 &li.bd_acid_spread_rate, 3
804 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
805 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
810 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
811 &li.bd_biter_move_delay, 0
815 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
816 &li.bd_biter_eats_element, EL_BD_DIAMOND
821 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
822 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
826 EL_BD_EXPANDABLE_WALL_ANY, -1,
827 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
828 &li.bd_change_expanding_wall, FALSE
831 EL_BD_EXPANDABLE_WALL_ANY, -1,
832 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
833 &li.bd_expanding_wall_looks_like, EL_BD_WALL
837 EL_BD_REPLICATOR, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.bd_replicators_active, TRUE
842 EL_BD_REPLICATOR, -1,
843 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
844 &li.bd_replicator_create_delay, 4
848 EL_BD_CONVEYOR_LEFT, -1,
849 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
850 &li.bd_conveyor_belts_active, TRUE
853 EL_BD_CONVEYOR_LEFT, -1,
854 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
855 &li.bd_conveyor_belts_changed, FALSE
860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
861 &li.bd_water_cannot_flow_down, FALSE
866 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
867 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
871 EL_BD_PNEUMATIC_HAMMER, -1,
872 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
873 &li.bd_hammer_walls_break_delay, 5
876 EL_BD_PNEUMATIC_HAMMER, -1,
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
878 &li.bd_hammer_walls_reappear, FALSE
881 EL_BD_PNEUMATIC_HAMMER, -1,
882 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
883 &li.bd_hammer_walls_reappear_delay, 100
888 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
889 &li.bd_num_skeletons_needed_for_pot, 5
893 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
894 &li.bd_skeleton_worth_num_diamonds, 0
899 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
900 &li.bd_sand_looks_like, EL_BD_SAND
903 // (the following values are related to various game elements)
907 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
908 &li.score[SC_EMERALD], 10
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 &li.score[SC_DIAMOND], 10
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
920 &li.score[SC_BUG], 10
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
926 &li.score[SC_SPACESHIP], 10
931 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
932 &li.score[SC_PACMAN], 10
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.score[SC_NUT], 10
943 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
944 &li.score[SC_DYNAMITE], 10
949 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
950 &li.score[SC_KEY], 10
955 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
956 &li.score[SC_PEARL], 10
961 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
962 &li.score[SC_CRYSTAL], 10
965 // (amoeba values used by R'n'D game engine only)
968 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
969 &li.amoeba_content, EL_DIAMOND
973 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
978 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
979 &li.grow_into_diggable, TRUE
981 // (amoeba values used by BD game engine only)
984 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
985 &li.bd_amoeba_wait_for_hatching, FALSE
989 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
990 &li.bd_amoeba_start_immediately, TRUE
994 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
995 &li.bd_amoeba_2_explode_by_amoeba, TRUE
999 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1000 &li.bd_amoeba_threshold_too_big, 200
1004 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1005 &li.bd_amoeba_slow_growth_time, 200
1009 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1010 &li.bd_amoeba_slow_growth_rate, 3
1014 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1015 &li.bd_amoeba_fast_growth_rate, 25
1019 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1020 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1024 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1025 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1030 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1031 &li.bd_amoeba_2_threshold_too_big, 200
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1036 &li.bd_amoeba_2_slow_growth_time, 200
1040 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1041 &li.bd_amoeba_2_slow_growth_rate, 3
1045 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1046 &li.bd_amoeba_2_fast_growth_rate, 25
1050 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1051 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1055 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1056 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1060 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1061 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1065 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1066 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1071 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1072 &li.yamyam_content, EL_ROCK, NULL,
1073 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1078 &li.score[SC_YAMYAM], 10
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1084 &li.score[SC_ROBOT], 10
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1101 &li.time_magic_wall, 10
1105 EL_GAME_OF_LIFE, -1,
1106 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1107 &li.game_of_life[0], 2
1110 EL_GAME_OF_LIFE, -1,
1111 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1112 &li.game_of_life[1], 3
1115 EL_GAME_OF_LIFE, -1,
1116 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1117 &li.game_of_life[2], 3
1120 EL_GAME_OF_LIFE, -1,
1121 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1122 &li.game_of_life[3], 3
1125 EL_GAME_OF_LIFE, -1,
1126 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1127 &li.use_life_bugs, FALSE
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1137 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1142 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1147 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1152 EL_TIMEGATE_SWITCH, -1,
1153 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1154 &li.time_timegate, 10
1158 EL_LIGHT_SWITCH_ACTIVE, -1,
1159 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1164 EL_SHIELD_NORMAL, -1,
1165 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1166 &li.shield_normal_time, 10
1169 EL_SHIELD_NORMAL, -1,
1170 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1171 &li.score[SC_SHIELD], 10
1175 EL_SHIELD_DEADLY, -1,
1176 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1177 &li.shield_deadly_time, 10
1180 EL_SHIELD_DEADLY, -1,
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1182 &li.score[SC_SHIELD], 10
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1192 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1193 &li.extra_time_score, 10
1197 EL_TIME_ORB_FULL, -1,
1198 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1199 &li.time_orb_time, 10
1202 EL_TIME_ORB_FULL, -1,
1203 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1204 &li.use_time_orb_bug, FALSE
1209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1210 &li.use_spring_bug, FALSE
1215 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1216 &li.android_move_time, 10
1220 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1221 &li.android_clone_time, 10
1224 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1225 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1226 &li.android_clone_element[0], EL_EMPTY, NULL,
1227 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1231 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1232 &li.android_clone_element[0], EL_EMPTY, NULL,
1233 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1238 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1239 &li.lenses_score, 10
1243 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 EL_EMC_MAGNIFIER, -1,
1249 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1250 &li.magnify_score, 10
1253 EL_EMC_MAGNIFIER, -1,
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1255 &li.magnify_time, 10
1259 EL_EMC_MAGIC_BALL, -1,
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1264 EL_EMC_MAGIC_BALL, -1,
1265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1266 &li.ball_random, FALSE
1269 EL_EMC_MAGIC_BALL, -1,
1270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1271 &li.ball_active_initial, FALSE
1274 EL_EMC_MAGIC_BALL, -1,
1275 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1276 &li.ball_content, EL_EMPTY, NULL,
1277 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1281 EL_SOKOBAN_FIELD_EMPTY, -1,
1282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1283 &li.sb_fields_needed, TRUE
1287 EL_SOKOBAN_OBJECT, -1,
1288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1289 &li.sb_objects_needed, TRUE
1294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1295 &li.mm_laser_red, FALSE
1299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1300 &li.mm_laser_green, FALSE
1304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1305 &li.mm_laser_blue, TRUE
1310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1311 &li.df_laser_red, TRUE
1315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1316 &li.df_laser_green, TRUE
1320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1321 &li.df_laser_blue, FALSE
1325 EL_MM_FUSE_ACTIVE, -1,
1326 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1327 &li.mm_time_fuse, 25
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1332 &li.mm_time_bomb, 75
1336 EL_MM_GRAY_BALL, -1,
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1338 &li.mm_time_ball, 75
1341 EL_MM_GRAY_BALL, -1,
1342 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1343 &li.mm_ball_choice_mode, ANIM_RANDOM
1346 EL_MM_GRAY_BALL, -1,
1347 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1348 &li.mm_ball_content, EL_EMPTY, NULL,
1349 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1352 EL_MM_GRAY_BALL, -1,
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1354 &li.rotate_mm_ball_content, TRUE
1357 EL_MM_GRAY_BALL, -1,
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1359 &li.explode_mm_ball, FALSE
1363 EL_MM_STEEL_BLOCK, -1,
1364 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1365 &li.mm_time_block, 75
1368 EL_MM_LIGHTBALL, -1,
1369 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1370 &li.score[SC_ELEM_BONUS], 10
1380 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1384 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1385 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1389 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1390 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1396 &xx_envelope.autowrap, FALSE
1400 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1401 &xx_envelope.centered, FALSE
1406 TYPE_STRING, CONF_VALUE_BYTES(1),
1407 &xx_envelope.text, -1, NULL,
1408 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1409 &xx_default_string_empty[0]
1419 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1423 TYPE_STRING, CONF_VALUE_BYTES(1),
1424 &xx_ei.description[0], -1,
1425 &yy_ei.description[0],
1426 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1427 &xx_default_description[0]
1432 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1433 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1434 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1436 #if ENABLE_RESERVED_CODE
1437 // (reserved for later use)
1440 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1441 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1442 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1449 &xx_ei.use_gfx_element, FALSE,
1450 &yy_ei.use_gfx_element
1454 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1455 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1456 &yy_ei.gfx_element_initial
1461 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1462 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1463 &yy_ei.access_direction
1468 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1469 &xx_ei.collect_score_initial, 10,
1470 &yy_ei.collect_score_initial
1474 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1475 &xx_ei.collect_count_initial, 1,
1476 &yy_ei.collect_count_initial
1481 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1482 &xx_ei.ce_value_fixed_initial, 0,
1483 &yy_ei.ce_value_fixed_initial
1487 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1488 &xx_ei.ce_value_random_initial, 0,
1489 &yy_ei.ce_value_random_initial
1493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1494 &xx_ei.use_last_ce_value, FALSE,
1495 &yy_ei.use_last_ce_value
1500 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1501 &xx_ei.push_delay_fixed, 8,
1502 &yy_ei.push_delay_fixed
1506 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1507 &xx_ei.push_delay_random, 8,
1508 &yy_ei.push_delay_random
1512 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1513 &xx_ei.drop_delay_fixed, 0,
1514 &yy_ei.drop_delay_fixed
1518 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1519 &xx_ei.drop_delay_random, 0,
1520 &yy_ei.drop_delay_random
1524 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1525 &xx_ei.move_delay_fixed, 0,
1526 &yy_ei.move_delay_fixed
1530 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1531 &xx_ei.move_delay_random, 0,
1532 &yy_ei.move_delay_random
1536 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1537 &xx_ei.step_delay_fixed, 0,
1538 &yy_ei.step_delay_fixed
1542 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1543 &xx_ei.step_delay_random, 0,
1544 &yy_ei.step_delay_random
1549 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1550 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1555 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1556 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1557 &yy_ei.move_direction_initial
1561 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1562 &xx_ei.move_stepsize, TILEX / 8,
1563 &yy_ei.move_stepsize
1568 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1569 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1570 &yy_ei.move_enter_element
1574 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1575 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1576 &yy_ei.move_leave_element
1580 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1581 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1582 &yy_ei.move_leave_type
1587 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1588 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1589 &yy_ei.slippery_type
1594 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1595 &xx_ei.explosion_type, EXPLODES_3X3,
1596 &yy_ei.explosion_type
1600 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1601 &xx_ei.explosion_delay, 16,
1602 &yy_ei.explosion_delay
1606 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1607 &xx_ei.ignition_delay, 8,
1608 &yy_ei.ignition_delay
1613 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1614 &xx_ei.content, EL_EMPTY_SPACE,
1616 &xx_num_contents, 1, 1
1619 // ---------- "num_change_pages" must be the last entry ---------------------
1622 -1, SAVE_CONF_ALWAYS,
1623 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1624 &xx_ei.num_change_pages, 1,
1625 &yy_ei.num_change_pages
1636 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1638 // ---------- "current_change_page" must be the first entry -----------------
1641 -1, SAVE_CONF_ALWAYS,
1642 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1643 &xx_current_change_page, -1
1646 // ---------- (the remaining entries can be in any order) -------------------
1650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1651 &xx_change.can_change, FALSE
1656 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1657 &xx_event_bits[0], 0
1661 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1662 &xx_event_bits[1], 0
1667 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1668 &xx_change.trigger_player, CH_PLAYER_ANY
1672 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1673 &xx_change.trigger_side, CH_SIDE_ANY
1677 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1678 &xx_change.trigger_page, CH_PAGE_ANY
1683 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1684 &xx_change.target_element, EL_EMPTY_SPACE
1689 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1690 &xx_change.delay_fixed, 0
1694 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1695 &xx_change.delay_random, 0
1699 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1700 &xx_change.delay_frames, FRAMES_PER_SECOND
1705 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1706 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1711 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1712 &xx_change.explode, FALSE
1716 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1717 &xx_change.use_target_content, FALSE
1721 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1722 &xx_change.only_if_complete, FALSE
1726 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1727 &xx_change.use_random_replace, FALSE
1731 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1732 &xx_change.random_percentage, 100
1736 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1737 &xx_change.replace_when, CP_WHEN_EMPTY
1742 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1743 &xx_change.has_action, FALSE
1747 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1748 &xx_change.action_type, CA_NO_ACTION
1752 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1753 &xx_change.action_mode, CA_MODE_UNDEFINED
1757 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1758 &xx_change.action_arg, CA_ARG_UNDEFINED
1763 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1764 &xx_change.action_element, EL_EMPTY_SPACE
1769 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1770 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1771 &xx_num_contents, 1, 1
1781 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1785 TYPE_STRING, CONF_VALUE_BYTES(1),
1786 &xx_ei.description[0], -1, NULL,
1787 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1788 &xx_default_description[0]
1793 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1794 &xx_ei.use_gfx_element, FALSE
1798 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1799 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1804 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1805 &xx_group.choice_mode, ANIM_RANDOM
1810 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1811 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1812 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1822 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1826 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1827 &xx_ei.use_gfx_element, FALSE
1831 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1832 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1842 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1846 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1847 &li.block_snap_field, TRUE
1851 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1852 &li.continuous_snapping, TRUE
1856 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1857 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1862 &li.use_start_element[0], FALSE
1866 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1867 &li.start_element[0], EL_PLAYER_1
1871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1872 &li.use_artwork_element[0], FALSE
1876 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1877 &li.artwork_element[0], EL_PLAYER_1
1881 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1882 &li.use_explosion_element[0], FALSE
1886 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1887 &li.explosion_element[0], EL_PLAYER_1
1902 filetype_id_list[] =
1904 { LEVEL_FILE_TYPE_RND, "RND" },
1905 { LEVEL_FILE_TYPE_BD, "BD" },
1906 { LEVEL_FILE_TYPE_EM, "EM" },
1907 { LEVEL_FILE_TYPE_SP, "SP" },
1908 { LEVEL_FILE_TYPE_DX, "DX" },
1909 { LEVEL_FILE_TYPE_SB, "SB" },
1910 { LEVEL_FILE_TYPE_DC, "DC" },
1911 { LEVEL_FILE_TYPE_MM, "MM" },
1912 { LEVEL_FILE_TYPE_MM, "DF" },
1917 // ============================================================================
1918 // level file functions
1919 // ============================================================================
1921 static boolean check_special_flags(char *flag)
1923 if (strEqual(options.special_flags, flag) ||
1924 strEqual(leveldir_current->special_flags, flag))
1930 static struct DateInfo getCurrentDate(void)
1932 time_t epoch_seconds = time(NULL);
1933 struct tm *now = localtime(&epoch_seconds);
1934 struct DateInfo date;
1936 date.year = now->tm_year + 1900;
1937 date.month = now->tm_mon + 1;
1938 date.day = now->tm_mday;
1940 date.src = DATE_SRC_CLOCK;
1945 static void resetEventFlags(struct ElementChangeInfo *change)
1949 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1950 change->has_event[i] = FALSE;
1953 static void resetEventBits(void)
1957 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1958 xx_event_bits[i] = 0;
1961 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1965 /* important: only change event flag if corresponding event bit is set
1966 (this is because all xx_event_bits[] values are loaded separately,
1967 and all xx_event_bits[] values are set back to zero before loading
1968 another value xx_event_bits[x] (each value representing 32 flags)) */
1970 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1971 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1972 change->has_event[i] = TRUE;
1975 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1979 /* in contrast to the above function setEventFlagsFromEventBits(), it
1980 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1981 depending on the corresponding change->has_event[i] values here, as
1982 all xx_event_bits[] values are reset in resetEventBits() before */
1984 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1985 if (change->has_event[i])
1986 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1989 static char *getDefaultElementDescription(struct ElementInfo *ei)
1991 static char description[MAX_ELEMENT_NAME_LEN + 1];
1992 char *default_description = (ei->custom_description != NULL ?
1993 ei->custom_description :
1994 ei->editor_description);
1997 // always start with reliable default values
1998 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1999 description[i] = '\0';
2001 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2002 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2004 return &description[0];
2007 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2009 char *default_description = getDefaultElementDescription(ei);
2012 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2013 ei->description[i] = default_description[i];
2016 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2020 for (i = 0; conf[i].data_type != -1; i++)
2022 int default_value = conf[i].default_value;
2023 int data_type = conf[i].data_type;
2024 int conf_type = conf[i].conf_type;
2025 int byte_mask = conf_type & CONF_MASK_BYTES;
2027 if (byte_mask == CONF_MASK_MULTI_BYTES)
2029 int default_num_entities = conf[i].default_num_entities;
2030 int max_num_entities = conf[i].max_num_entities;
2032 *(int *)(conf[i].num_entities) = default_num_entities;
2034 if (data_type == TYPE_STRING)
2036 char *default_string = conf[i].default_string;
2037 char *string = (char *)(conf[i].value);
2039 strncpy(string, default_string, max_num_entities);
2041 else if (data_type == TYPE_ELEMENT_LIST)
2043 int *element_array = (int *)(conf[i].value);
2046 for (j = 0; j < max_num_entities; j++)
2047 element_array[j] = default_value;
2049 else if (data_type == TYPE_CONTENT_LIST)
2051 struct Content *content = (struct Content *)(conf[i].value);
2054 for (c = 0; c < max_num_entities; c++)
2055 for (y = 0; y < 3; y++)
2056 for (x = 0; x < 3; x++)
2057 content[c].e[x][y] = default_value;
2060 else // constant size configuration data (1, 2 or 4 bytes)
2062 if (data_type == TYPE_BOOLEAN)
2063 *(boolean *)(conf[i].value) = default_value;
2065 *(int *) (conf[i].value) = default_value;
2070 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2074 for (i = 0; conf[i].data_type != -1; i++)
2076 int data_type = conf[i].data_type;
2077 int conf_type = conf[i].conf_type;
2078 int byte_mask = conf_type & CONF_MASK_BYTES;
2080 if (byte_mask == CONF_MASK_MULTI_BYTES)
2082 int max_num_entities = conf[i].max_num_entities;
2084 if (data_type == TYPE_STRING)
2086 char *string = (char *)(conf[i].value);
2087 char *string_copy = (char *)(conf[i].value_copy);
2089 strncpy(string_copy, string, max_num_entities);
2091 else if (data_type == TYPE_ELEMENT_LIST)
2093 int *element_array = (int *)(conf[i].value);
2094 int *element_array_copy = (int *)(conf[i].value_copy);
2097 for (j = 0; j < max_num_entities; j++)
2098 element_array_copy[j] = element_array[j];
2100 else if (data_type == TYPE_CONTENT_LIST)
2102 struct Content *content = (struct Content *)(conf[i].value);
2103 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2106 for (c = 0; c < max_num_entities; c++)
2107 for (y = 0; y < 3; y++)
2108 for (x = 0; x < 3; x++)
2109 content_copy[c].e[x][y] = content[c].e[x][y];
2112 else // constant size configuration data (1, 2 or 4 bytes)
2114 if (data_type == TYPE_BOOLEAN)
2115 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2117 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2122 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2126 xx_ei = *ei_from; // copy element data into temporary buffer
2127 yy_ei = *ei_to; // copy element data into temporary buffer
2129 copyConfigFromConfigList(chunk_config_CUSX_base);
2134 // ---------- reinitialize and copy change pages ----------
2136 ei_to->num_change_pages = ei_from->num_change_pages;
2137 ei_to->current_change_page = ei_from->current_change_page;
2139 setElementChangePages(ei_to, ei_to->num_change_pages);
2141 for (i = 0; i < ei_to->num_change_pages; i++)
2142 ei_to->change_page[i] = ei_from->change_page[i];
2144 // ---------- copy group element info ----------
2145 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2146 *ei_to->group = *ei_from->group;
2148 // mark this custom element as modified
2149 ei_to->modified_settings = TRUE;
2152 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2154 int change_page_size = sizeof(struct ElementChangeInfo);
2156 ei->num_change_pages = MAX(1, change_pages);
2159 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2161 if (ei->current_change_page >= ei->num_change_pages)
2162 ei->current_change_page = ei->num_change_pages - 1;
2164 ei->change = &ei->change_page[ei->current_change_page];
2167 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2169 xx_change = *change; // copy change data into temporary buffer
2171 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2173 *change = xx_change;
2175 resetEventFlags(change);
2177 change->direct_action = 0;
2178 change->other_action = 0;
2180 change->pre_change_function = NULL;
2181 change->change_function = NULL;
2182 change->post_change_function = NULL;
2185 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2189 li = *level; // copy level data into temporary buffer
2190 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2191 *level = li; // copy temporary buffer back to level data
2193 setLevelInfoToDefaults_BD();
2194 setLevelInfoToDefaults_EM();
2195 setLevelInfoToDefaults_SP();
2196 setLevelInfoToDefaults_MM();
2198 level->native_bd_level = &native_bd_level;
2199 level->native_em_level = &native_em_level;
2200 level->native_sp_level = &native_sp_level;
2201 level->native_mm_level = &native_mm_level;
2203 level->file_version = FILE_VERSION_ACTUAL;
2204 level->game_version = GAME_VERSION_ACTUAL;
2206 level->creation_date = getCurrentDate();
2208 level->encoding_16bit_field = TRUE;
2209 level->encoding_16bit_yamyam = TRUE;
2210 level->encoding_16bit_amoeba = TRUE;
2212 // clear level name and level author string buffers
2213 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2214 level->name[i] = '\0';
2215 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2216 level->author[i] = '\0';
2218 // set level name and level author to default values
2219 strcpy(level->name, NAMELESS_LEVEL_NAME);
2220 strcpy(level->author, ANONYMOUS_NAME);
2222 // set level playfield to playable default level with player and exit
2223 for (x = 0; x < MAX_LEV_FIELDX; x++)
2224 for (y = 0; y < MAX_LEV_FIELDY; y++)
2225 level->field[x][y] = EL_SAND;
2227 level->field[0][0] = EL_PLAYER_1;
2228 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2230 BorderElement = EL_STEELWALL;
2232 // detect custom elements when loading them
2233 level->file_has_custom_elements = FALSE;
2235 // set all bug compatibility flags to "false" => do not emulate this bug
2236 level->use_action_after_change_bug = FALSE;
2238 if (leveldir_current)
2240 // try to determine better author name than 'anonymous'
2241 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2243 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2244 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2248 switch (LEVELCLASS(leveldir_current))
2250 case LEVELCLASS_TUTORIAL:
2251 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2254 case LEVELCLASS_CONTRIB:
2255 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2256 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2259 case LEVELCLASS_PRIVATE:
2260 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2261 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2265 // keep default value
2272 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2274 static boolean clipboard_elements_initialized = FALSE;
2277 InitElementPropertiesStatic();
2279 li = *level; // copy level data into temporary buffer
2280 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2281 *level = li; // copy temporary buffer back to level data
2283 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2286 struct ElementInfo *ei = &element_info[element];
2288 if (element == EL_MM_GRAY_BALL)
2290 struct LevelInfo_MM *level_mm = level->native_mm_level;
2293 for (j = 0; j < level->num_mm_ball_contents; j++)
2294 level->mm_ball_content[j] =
2295 map_element_MM_to_RND(level_mm->ball_content[j]);
2298 // never initialize clipboard elements after the very first time
2299 // (to be able to use clipboard elements between several levels)
2300 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2303 if (IS_ENVELOPE(element))
2305 int envelope_nr = element - EL_ENVELOPE_1;
2307 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2309 level->envelope[envelope_nr] = xx_envelope;
2312 if (IS_CUSTOM_ELEMENT(element) ||
2313 IS_GROUP_ELEMENT(element) ||
2314 IS_INTERNAL_ELEMENT(element))
2316 xx_ei = *ei; // copy element data into temporary buffer
2318 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2323 setElementChangePages(ei, 1);
2324 setElementChangeInfoToDefaults(ei->change);
2326 if (IS_CUSTOM_ELEMENT(element) ||
2327 IS_GROUP_ELEMENT(element))
2329 setElementDescriptionToDefault(ei);
2331 ei->modified_settings = FALSE;
2334 if (IS_CUSTOM_ELEMENT(element) ||
2335 IS_INTERNAL_ELEMENT(element))
2337 // internal values used in level editor
2339 ei->access_type = 0;
2340 ei->access_layer = 0;
2341 ei->access_protected = 0;
2342 ei->walk_to_action = 0;
2343 ei->smash_targets = 0;
2346 ei->can_explode_by_fire = FALSE;
2347 ei->can_explode_smashed = FALSE;
2348 ei->can_explode_impact = FALSE;
2350 ei->current_change_page = 0;
2353 if (IS_GROUP_ELEMENT(element) ||
2354 IS_INTERNAL_ELEMENT(element))
2356 struct ElementGroupInfo *group;
2358 // initialize memory for list of elements in group
2359 if (ei->group == NULL)
2360 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2364 xx_group = *group; // copy group data into temporary buffer
2366 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2371 if (IS_EMPTY_ELEMENT(element) ||
2372 IS_INTERNAL_ELEMENT(element))
2374 xx_ei = *ei; // copy element data into temporary buffer
2376 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2382 clipboard_elements_initialized = TRUE;
2385 static void setLevelInfoToDefaults(struct LevelInfo *level,
2386 boolean level_info_only,
2387 boolean reset_file_status)
2389 setLevelInfoToDefaults_Level(level);
2391 if (!level_info_only)
2392 setLevelInfoToDefaults_Elements(level);
2394 if (reset_file_status)
2396 level->no_valid_file = FALSE;
2397 level->no_level_file = FALSE;
2400 level->changed = FALSE;
2403 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2405 level_file_info->nr = 0;
2406 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2407 level_file_info->packed = FALSE;
2409 setString(&level_file_info->basename, NULL);
2410 setString(&level_file_info->filename, NULL);
2413 int getMappedElement_SB(int, boolean);
2415 static void ActivateLevelTemplate(void)
2419 if (check_special_flags("load_xsb_to_ces"))
2421 // fill smaller playfields with padding "beyond border wall" elements
2422 if (level.fieldx < level_template.fieldx ||
2423 level.fieldy < level_template.fieldy)
2425 short field[level.fieldx][level.fieldy];
2426 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2427 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2428 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2429 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2431 // copy old playfield (which is smaller than the visible area)
2432 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2433 field[x][y] = level.field[x][y];
2435 // fill new, larger playfield with "beyond border wall" elements
2436 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2437 level.field[x][y] = getMappedElement_SB('_', TRUE);
2439 // copy the old playfield to the middle of the new playfield
2440 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2441 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2443 level.fieldx = new_fieldx;
2444 level.fieldy = new_fieldy;
2448 // Currently there is no special action needed to activate the template
2449 // data, because 'element_info' property settings overwrite the original
2450 // level data, while all other variables do not change.
2452 // Exception: 'from_level_template' elements in the original level playfield
2453 // are overwritten with the corresponding elements at the same position in
2454 // playfield from the level template.
2456 for (x = 0; x < level.fieldx; x++)
2457 for (y = 0; y < level.fieldy; y++)
2458 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2459 level.field[x][y] = level_template.field[x][y];
2461 if (check_special_flags("load_xsb_to_ces"))
2463 struct LevelInfo level_backup = level;
2465 // overwrite all individual level settings from template level settings
2466 level = level_template;
2468 // restore level file info
2469 level.file_info = level_backup.file_info;
2471 // restore playfield size
2472 level.fieldx = level_backup.fieldx;
2473 level.fieldy = level_backup.fieldy;
2475 // restore playfield content
2476 for (x = 0; x < level.fieldx; x++)
2477 for (y = 0; y < level.fieldy; y++)
2478 level.field[x][y] = level_backup.field[x][y];
2480 // restore name and author from individual level
2481 strcpy(level.name, level_backup.name);
2482 strcpy(level.author, level_backup.author);
2484 // restore flag "use_custom_template"
2485 level.use_custom_template = level_backup.use_custom_template;
2489 static boolean checkForPackageFromBasename_BD(char *basename)
2491 // check for native BD level file extensions
2492 if (!strSuffixLower(basename, ".bd") &&
2493 !strSuffixLower(basename, ".bdr") &&
2494 !strSuffixLower(basename, ".brc") &&
2495 !strSuffixLower(basename, ".gds"))
2498 // check for standard single-level BD files (like "001.bd")
2499 if (strSuffixLower(basename, ".bd") &&
2500 strlen(basename) == 6 &&
2501 basename[0] >= '0' && basename[0] <= '9' &&
2502 basename[1] >= '0' && basename[1] <= '9' &&
2503 basename[2] >= '0' && basename[2] <= '9')
2506 // this is a level package in native BD file format
2510 static char *getLevelFilenameFromBasename(char *basename)
2512 static char *filename = NULL;
2514 checked_free(filename);
2516 filename = getPath2(getCurrentLevelDir(), basename);
2521 static int getFileTypeFromBasename(char *basename)
2523 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2525 static char *filename = NULL;
2526 struct stat file_status;
2528 // ---------- try to determine file type from filename ----------
2530 // check for typical filename of a Supaplex level package file
2531 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2532 return LEVEL_FILE_TYPE_SP;
2534 // check for typical filename of a Diamond Caves II level package file
2535 if (strSuffixLower(basename, ".dc") ||
2536 strSuffixLower(basename, ".dc2"))
2537 return LEVEL_FILE_TYPE_DC;
2539 // check for typical filename of a Sokoban level package file
2540 if (strSuffixLower(basename, ".xsb") &&
2541 strchr(basename, '%') == NULL)
2542 return LEVEL_FILE_TYPE_SB;
2544 // check for typical filename of a Boulder Dash (GDash) level package file
2545 if (checkForPackageFromBasename_BD(basename))
2546 return LEVEL_FILE_TYPE_BD;
2548 // ---------- try to determine file type from filesize ----------
2550 checked_free(filename);
2551 filename = getPath2(getCurrentLevelDir(), basename);
2553 if (stat(filename, &file_status) == 0)
2555 // check for typical filesize of a Supaplex level package file
2556 if (file_status.st_size == 170496)
2557 return LEVEL_FILE_TYPE_SP;
2560 return LEVEL_FILE_TYPE_UNKNOWN;
2563 static int getFileTypeFromMagicBytes(char *filename, int type)
2567 if ((file = openFile(filename, MODE_READ)))
2569 char chunk_name[CHUNK_ID_LEN + 1];
2571 getFileChunkBE(file, chunk_name, NULL);
2573 if (strEqual(chunk_name, "MMII") ||
2574 strEqual(chunk_name, "MIRR"))
2575 type = LEVEL_FILE_TYPE_MM;
2583 static boolean checkForPackageFromBasename(char *basename)
2585 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2586 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2588 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2591 static char *getSingleLevelBasenameExt(int nr, char *extension)
2593 static char basename[MAX_FILENAME_LEN];
2596 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2598 sprintf(basename, "%03d.%s", nr, extension);
2603 static char *getSingleLevelBasename(int nr)
2605 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2608 static char *getPackedLevelBasename(int type)
2610 static char basename[MAX_FILENAME_LEN];
2611 char *directory = getCurrentLevelDir();
2613 DirectoryEntry *dir_entry;
2615 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2617 if ((dir = openDirectory(directory)) == NULL)
2619 Warn("cannot read current level directory '%s'", directory);
2624 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2626 char *entry_basename = dir_entry->basename;
2627 int entry_type = getFileTypeFromBasename(entry_basename);
2629 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2631 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2634 strcpy(basename, entry_basename);
2641 closeDirectory(dir);
2646 static char *getSingleLevelFilename(int nr)
2648 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2651 #if ENABLE_UNUSED_CODE
2652 static char *getPackedLevelFilename(int type)
2654 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2658 char *getDefaultLevelFilename(int nr)
2660 return getSingleLevelFilename(nr);
2663 #if ENABLE_UNUSED_CODE
2664 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2668 lfi->packed = FALSE;
2670 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2671 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2675 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2676 int type, char *format, ...)
2678 static char basename[MAX_FILENAME_LEN];
2681 va_start(ap, format);
2682 vsprintf(basename, format, ap);
2686 lfi->packed = FALSE;
2688 setString(&lfi->basename, basename);
2689 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2692 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2698 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2699 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2702 static int getFiletypeFromID(char *filetype_id)
2704 char *filetype_id_lower;
2705 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2708 if (filetype_id == NULL)
2709 return LEVEL_FILE_TYPE_UNKNOWN;
2711 filetype_id_lower = getStringToLower(filetype_id);
2713 for (i = 0; filetype_id_list[i].id != NULL; i++)
2715 char *id_lower = getStringToLower(filetype_id_list[i].id);
2717 if (strEqual(filetype_id_lower, id_lower))
2718 filetype = filetype_id_list[i].filetype;
2722 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2726 free(filetype_id_lower);
2731 char *getLocalLevelTemplateFilename(void)
2733 return getDefaultLevelFilename(-1);
2736 char *getGlobalLevelTemplateFilename(void)
2738 // global variable "leveldir_current" must be modified in the loop below
2739 LevelDirTree *leveldir_current_last = leveldir_current;
2740 char *filename = NULL;
2742 // check for template level in path from current to topmost tree node
2744 while (leveldir_current != NULL)
2746 filename = getDefaultLevelFilename(-1);
2748 if (fileExists(filename))
2751 leveldir_current = leveldir_current->node_parent;
2754 // restore global variable "leveldir_current" modified in above loop
2755 leveldir_current = leveldir_current_last;
2760 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2764 // special case: level number is negative => check for level template file
2767 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2768 getSingleLevelBasename(-1));
2770 // replace local level template filename with global template filename
2771 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2773 // no fallback if template file not existing
2777 // special case: check for file name/pattern specified in "levelinfo.conf"
2778 if (leveldir_current->level_filename != NULL)
2780 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2782 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2783 leveldir_current->level_filename, nr);
2785 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2787 if (fileExists(lfi->filename))
2790 else if (leveldir_current->level_filetype != NULL)
2792 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2794 // check for specified native level file with standard file name
2795 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2796 "%03d.%s", nr, LEVELFILE_EXTENSION);
2797 if (fileExists(lfi->filename))
2801 // check for native Rocks'n'Diamonds level file
2802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2803 "%03d.%s", nr, LEVELFILE_EXTENSION);
2804 if (fileExists(lfi->filename))
2807 // check for native Boulder Dash level file
2808 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2809 if (fileExists(lfi->filename))
2812 // check for Emerald Mine level file (V1)
2813 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2814 'a' + (nr / 10) % 26, '0' + nr % 10);
2815 if (fileExists(lfi->filename))
2817 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2818 'A' + (nr / 10) % 26, '0' + nr % 10);
2819 if (fileExists(lfi->filename))
2822 // check for Emerald Mine level file (V2 to V5)
2823 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2824 if (fileExists(lfi->filename))
2827 // check for Emerald Mine level file (V6 / single mode)
2828 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2829 if (fileExists(lfi->filename))
2831 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2832 if (fileExists(lfi->filename))
2835 // check for Emerald Mine level file (V6 / teamwork mode)
2836 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2837 if (fileExists(lfi->filename))
2839 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2840 if (fileExists(lfi->filename))
2843 // check for various packed level file formats
2844 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2845 if (fileExists(lfi->filename))
2848 // no known level file found -- use default values (and fail later)
2849 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2850 "%03d.%s", nr, LEVELFILE_EXTENSION);
2853 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2855 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2856 lfi->type = getFileTypeFromBasename(lfi->basename);
2858 if (lfi->type == LEVEL_FILE_TYPE_RND)
2859 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2862 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2864 // always start with reliable default values
2865 setFileInfoToDefaults(level_file_info);
2867 level_file_info->nr = nr; // set requested level number
2869 determineLevelFileInfo_Filename(level_file_info);
2870 determineLevelFileInfo_Filetype(level_file_info);
2873 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2874 struct LevelFileInfo *lfi_to)
2876 lfi_to->nr = lfi_from->nr;
2877 lfi_to->type = lfi_from->type;
2878 lfi_to->packed = lfi_from->packed;
2880 setString(&lfi_to->basename, lfi_from->basename);
2881 setString(&lfi_to->filename, lfi_from->filename);
2884 // ----------------------------------------------------------------------------
2885 // functions for loading R'n'D level
2886 // ----------------------------------------------------------------------------
2888 int getMappedElement(int element)
2890 // remap some (historic, now obsolete) elements
2894 case EL_PLAYER_OBSOLETE:
2895 element = EL_PLAYER_1;
2898 case EL_KEY_OBSOLETE:
2902 case EL_EM_KEY_1_FILE_OBSOLETE:
2903 element = EL_EM_KEY_1;
2906 case EL_EM_KEY_2_FILE_OBSOLETE:
2907 element = EL_EM_KEY_2;
2910 case EL_EM_KEY_3_FILE_OBSOLETE:
2911 element = EL_EM_KEY_3;
2914 case EL_EM_KEY_4_FILE_OBSOLETE:
2915 element = EL_EM_KEY_4;
2918 case EL_ENVELOPE_OBSOLETE:
2919 element = EL_ENVELOPE_1;
2927 if (element >= NUM_FILE_ELEMENTS)
2929 Warn("invalid level element %d", element);
2931 element = EL_UNKNOWN;
2939 static int getMappedElementByVersion(int element, int game_version)
2941 // remap some elements due to certain game version
2943 if (game_version <= VERSION_IDENT(2,2,0,0))
2945 // map game font elements
2946 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2947 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2948 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2949 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2952 if (game_version < VERSION_IDENT(3,0,0,0))
2954 // map Supaplex gravity tube elements
2955 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2956 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2957 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2958 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2965 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2967 level->file_version = getFileVersion(file);
2968 level->game_version = getFileVersion(file);
2973 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2975 level->creation_date.year = getFile16BitBE(file);
2976 level->creation_date.month = getFile8Bit(file);
2977 level->creation_date.day = getFile8Bit(file);
2979 level->creation_date.src = DATE_SRC_LEVELFILE;
2984 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2986 int initial_player_stepsize;
2987 int initial_player_gravity;
2990 level->fieldx = getFile8Bit(file);
2991 level->fieldy = getFile8Bit(file);
2993 level->time = getFile16BitBE(file);
2994 level->gems_needed = getFile16BitBE(file);
2996 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2997 level->name[i] = getFile8Bit(file);
2998 level->name[MAX_LEVEL_NAME_LEN] = 0;
3000 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3001 level->score[i] = getFile8Bit(file);
3003 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3004 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3005 for (y = 0; y < 3; y++)
3006 for (x = 0; x < 3; x++)
3007 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3009 level->amoeba_speed = getFile8Bit(file);
3010 level->time_magic_wall = getFile8Bit(file);
3011 level->time_wheel = getFile8Bit(file);
3012 level->amoeba_content = getMappedElement(getFile8Bit(file));
3014 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3017 for (i = 0; i < MAX_PLAYERS; i++)
3018 level->initial_player_stepsize[i] = initial_player_stepsize;
3020 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3022 for (i = 0; i < MAX_PLAYERS; i++)
3023 level->initial_player_gravity[i] = initial_player_gravity;
3025 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3026 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3028 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3030 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3031 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3032 level->can_move_into_acid_bits = getFile32BitBE(file);
3033 level->dont_collide_with_bits = getFile8Bit(file);
3035 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3036 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3038 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3039 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3040 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3042 level->game_engine_type = getFile8Bit(file);
3044 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3049 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3053 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3054 level->name[i] = getFile8Bit(file);
3055 level->name[MAX_LEVEL_NAME_LEN] = 0;
3060 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3064 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3065 level->author[i] = getFile8Bit(file);
3066 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3071 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3074 int chunk_size_expected = level->fieldx * level->fieldy;
3076 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3077 stored with 16-bit encoding (and should be twice as big then).
3078 Even worse, playfield data was stored 16-bit when only yamyam content
3079 contained 16-bit elements and vice versa. */
3081 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3082 chunk_size_expected *= 2;
3084 if (chunk_size_expected != chunk_size)
3086 ReadUnusedBytesFromFile(file, chunk_size);
3087 return chunk_size_expected;
3090 for (y = 0; y < level->fieldy; y++)
3091 for (x = 0; x < level->fieldx; x++)
3092 level->field[x][y] =
3093 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3098 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3101 int header_size = 4;
3102 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3103 int chunk_size_expected = header_size + content_size;
3105 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3106 stored with 16-bit encoding (and should be twice as big then).
3107 Even worse, playfield data was stored 16-bit when only yamyam content
3108 contained 16-bit elements and vice versa. */
3110 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3111 chunk_size_expected += content_size;
3113 if (chunk_size_expected != chunk_size)
3115 ReadUnusedBytesFromFile(file, chunk_size);
3116 return chunk_size_expected;
3120 level->num_yamyam_contents = getFile8Bit(file);
3124 // correct invalid number of content fields -- should never happen
3125 if (level->num_yamyam_contents < 1 ||
3126 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3127 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3129 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3130 for (y = 0; y < 3; y++)
3131 for (x = 0; x < 3; x++)
3132 level->yamyam_content[i].e[x][y] =
3133 getMappedElement(level->encoding_16bit_field ?
3134 getFile16BitBE(file) : getFile8Bit(file));
3138 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3143 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3145 element = getMappedElement(getFile16BitBE(file));
3146 num_contents = getFile8Bit(file);
3148 getFile8Bit(file); // content x size (unused)
3149 getFile8Bit(file); // content y size (unused)
3151 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3153 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3154 for (y = 0; y < 3; y++)
3155 for (x = 0; x < 3; x++)
3156 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3158 // correct invalid number of content fields -- should never happen
3159 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3160 num_contents = STD_ELEMENT_CONTENTS;
3162 if (element == EL_YAMYAM)
3164 level->num_yamyam_contents = num_contents;
3166 for (i = 0; i < num_contents; i++)
3167 for (y = 0; y < 3; y++)
3168 for (x = 0; x < 3; x++)
3169 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3171 else if (element == EL_BD_AMOEBA)
3173 level->amoeba_content = content_array[0][0][0];
3177 Warn("cannot load content for element '%d'", element);
3183 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3189 int chunk_size_expected;
3191 element = getMappedElement(getFile16BitBE(file));
3192 if (!IS_ENVELOPE(element))
3193 element = EL_ENVELOPE_1;
3195 envelope_nr = element - EL_ENVELOPE_1;
3197 envelope_len = getFile16BitBE(file);
3199 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3200 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3202 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3204 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3205 if (chunk_size_expected != chunk_size)
3207 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3208 return chunk_size_expected;
3211 for (i = 0; i < envelope_len; i++)
3212 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3217 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3219 int num_changed_custom_elements = getFile16BitBE(file);
3220 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3223 if (chunk_size_expected != chunk_size)
3225 ReadUnusedBytesFromFile(file, chunk_size - 2);
3226 return chunk_size_expected;
3229 for (i = 0; i < num_changed_custom_elements; i++)
3231 int element = getMappedElement(getFile16BitBE(file));
3232 int properties = getFile32BitBE(file);
3234 if (IS_CUSTOM_ELEMENT(element))
3235 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3237 Warn("invalid custom element number %d", element);
3239 // older game versions that wrote level files with CUS1 chunks used
3240 // different default push delay values (not yet stored in level file)
3241 element_info[element].push_delay_fixed = 2;
3242 element_info[element].push_delay_random = 8;
3245 level->file_has_custom_elements = TRUE;
3250 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3252 int num_changed_custom_elements = getFile16BitBE(file);
3253 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3256 if (chunk_size_expected != chunk_size)
3258 ReadUnusedBytesFromFile(file, chunk_size - 2);
3259 return chunk_size_expected;
3262 for (i = 0; i < num_changed_custom_elements; i++)
3264 int element = getMappedElement(getFile16BitBE(file));
3265 int custom_target_element = getMappedElement(getFile16BitBE(file));
3267 if (IS_CUSTOM_ELEMENT(element))
3268 element_info[element].change->target_element = custom_target_element;
3270 Warn("invalid custom element number %d", element);
3273 level->file_has_custom_elements = TRUE;
3278 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3280 int num_changed_custom_elements = getFile16BitBE(file);
3281 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3284 if (chunk_size_expected != chunk_size)
3286 ReadUnusedBytesFromFile(file, chunk_size - 2);
3287 return chunk_size_expected;
3290 for (i = 0; i < num_changed_custom_elements; i++)
3292 int element = getMappedElement(getFile16BitBE(file));
3293 struct ElementInfo *ei = &element_info[element];
3294 unsigned int event_bits;
3296 if (!IS_CUSTOM_ELEMENT(element))
3298 Warn("invalid custom element number %d", element);
3300 element = EL_INTERNAL_DUMMY;
3303 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3304 ei->description[j] = getFile8Bit(file);
3305 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3307 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3309 // some free bytes for future properties and padding
3310 ReadUnusedBytesFromFile(file, 7);
3312 ei->use_gfx_element = getFile8Bit(file);
3313 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3315 ei->collect_score_initial = getFile8Bit(file);
3316 ei->collect_count_initial = getFile8Bit(file);
3318 ei->push_delay_fixed = getFile16BitBE(file);
3319 ei->push_delay_random = getFile16BitBE(file);
3320 ei->move_delay_fixed = getFile16BitBE(file);
3321 ei->move_delay_random = getFile16BitBE(file);
3323 ei->move_pattern = getFile16BitBE(file);
3324 ei->move_direction_initial = getFile8Bit(file);
3325 ei->move_stepsize = getFile8Bit(file);
3327 for (y = 0; y < 3; y++)
3328 for (x = 0; x < 3; x++)
3329 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3331 // bits 0 - 31 of "has_event[]"
3332 event_bits = getFile32BitBE(file);
3333 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3334 if (event_bits & (1u << j))
3335 ei->change->has_event[j] = TRUE;
3337 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3339 ei->change->delay_fixed = getFile16BitBE(file);
3340 ei->change->delay_random = getFile16BitBE(file);
3341 ei->change->delay_frames = getFile16BitBE(file);
3343 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3345 ei->change->explode = getFile8Bit(file);
3346 ei->change->use_target_content = getFile8Bit(file);
3347 ei->change->only_if_complete = getFile8Bit(file);
3348 ei->change->use_random_replace = getFile8Bit(file);
3350 ei->change->random_percentage = getFile8Bit(file);
3351 ei->change->replace_when = getFile8Bit(file);
3353 for (y = 0; y < 3; y++)
3354 for (x = 0; x < 3; x++)
3355 ei->change->target_content.e[x][y] =
3356 getMappedElement(getFile16BitBE(file));
3358 ei->slippery_type = getFile8Bit(file);
3360 // some free bytes for future properties and padding
3361 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3363 // mark that this custom element has been modified
3364 ei->modified_settings = TRUE;
3367 level->file_has_custom_elements = TRUE;
3372 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3374 struct ElementInfo *ei;
3375 int chunk_size_expected;
3379 // ---------- custom element base property values (96 bytes) ----------------
3381 element = getMappedElement(getFile16BitBE(file));
3383 if (!IS_CUSTOM_ELEMENT(element))
3385 Warn("invalid custom element number %d", element);
3387 ReadUnusedBytesFromFile(file, chunk_size - 2);
3392 ei = &element_info[element];
3394 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3395 ei->description[i] = getFile8Bit(file);
3396 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3398 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3400 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3402 ei->num_change_pages = getFile8Bit(file);
3404 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3405 if (chunk_size_expected != chunk_size)
3407 ReadUnusedBytesFromFile(file, chunk_size - 43);
3408 return chunk_size_expected;
3411 ei->ce_value_fixed_initial = getFile16BitBE(file);
3412 ei->ce_value_random_initial = getFile16BitBE(file);
3413 ei->use_last_ce_value = getFile8Bit(file);
3415 ei->use_gfx_element = getFile8Bit(file);
3416 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3418 ei->collect_score_initial = getFile8Bit(file);
3419 ei->collect_count_initial = getFile8Bit(file);
3421 ei->drop_delay_fixed = getFile8Bit(file);
3422 ei->push_delay_fixed = getFile8Bit(file);
3423 ei->drop_delay_random = getFile8Bit(file);
3424 ei->push_delay_random = getFile8Bit(file);
3425 ei->move_delay_fixed = getFile16BitBE(file);
3426 ei->move_delay_random = getFile16BitBE(file);
3428 // bits 0 - 15 of "move_pattern" ...
3429 ei->move_pattern = getFile16BitBE(file);
3430 ei->move_direction_initial = getFile8Bit(file);
3431 ei->move_stepsize = getFile8Bit(file);
3433 ei->slippery_type = getFile8Bit(file);
3435 for (y = 0; y < 3; y++)
3436 for (x = 0; x < 3; x++)
3437 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3439 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3440 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3441 ei->move_leave_type = getFile8Bit(file);
3443 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3444 ei->move_pattern |= (getFile16BitBE(file) << 16);
3446 ei->access_direction = getFile8Bit(file);
3448 ei->explosion_delay = getFile8Bit(file);
3449 ei->ignition_delay = getFile8Bit(file);
3450 ei->explosion_type = getFile8Bit(file);
3452 // some free bytes for future custom property values and padding
3453 ReadUnusedBytesFromFile(file, 1);
3455 // ---------- change page property values (48 bytes) ------------------------
3457 setElementChangePages(ei, ei->num_change_pages);
3459 for (i = 0; i < ei->num_change_pages; i++)
3461 struct ElementChangeInfo *change = &ei->change_page[i];
3462 unsigned int event_bits;
3464 // always start with reliable default values
3465 setElementChangeInfoToDefaults(change);
3467 // bits 0 - 31 of "has_event[]" ...
3468 event_bits = getFile32BitBE(file);
3469 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3470 if (event_bits & (1u << j))
3471 change->has_event[j] = TRUE;
3473 change->target_element = getMappedElement(getFile16BitBE(file));
3475 change->delay_fixed = getFile16BitBE(file);
3476 change->delay_random = getFile16BitBE(file);
3477 change->delay_frames = getFile16BitBE(file);
3479 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3481 change->explode = getFile8Bit(file);
3482 change->use_target_content = getFile8Bit(file);
3483 change->only_if_complete = getFile8Bit(file);
3484 change->use_random_replace = getFile8Bit(file);
3486 change->random_percentage = getFile8Bit(file);
3487 change->replace_when = getFile8Bit(file);
3489 for (y = 0; y < 3; y++)
3490 for (x = 0; x < 3; x++)
3491 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3493 change->can_change = getFile8Bit(file);
3495 change->trigger_side = getFile8Bit(file);
3497 change->trigger_player = getFile8Bit(file);
3498 change->trigger_page = getFile8Bit(file);
3500 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3501 CH_PAGE_ANY : (1 << change->trigger_page));
3503 change->has_action = getFile8Bit(file);
3504 change->action_type = getFile8Bit(file);
3505 change->action_mode = getFile8Bit(file);
3506 change->action_arg = getFile16BitBE(file);
3508 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3509 event_bits = getFile8Bit(file);
3510 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3511 if (event_bits & (1u << (j - 32)))
3512 change->has_event[j] = TRUE;
3515 // mark this custom element as modified
3516 ei->modified_settings = TRUE;
3518 level->file_has_custom_elements = TRUE;
3523 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3525 struct ElementInfo *ei;
3526 struct ElementGroupInfo *group;
3530 element = getMappedElement(getFile16BitBE(file));
3532 if (!IS_GROUP_ELEMENT(element))
3534 Warn("invalid group element number %d", element);
3536 ReadUnusedBytesFromFile(file, chunk_size - 2);
3541 ei = &element_info[element];
3543 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3544 ei->description[i] = getFile8Bit(file);
3545 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3547 group = element_info[element].group;
3549 group->num_elements = getFile8Bit(file);
3551 ei->use_gfx_element = getFile8Bit(file);
3552 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3554 group->choice_mode = getFile8Bit(file);
3556 // some free bytes for future values and padding
3557 ReadUnusedBytesFromFile(file, 3);
3559 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3560 group->element[i] = getMappedElement(getFile16BitBE(file));
3562 // mark this group element as modified
3563 element_info[element].modified_settings = TRUE;
3565 level->file_has_custom_elements = TRUE;
3570 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3571 int element, int real_element)
3573 int micro_chunk_size = 0;
3574 int conf_type = getFile8Bit(file);
3575 int byte_mask = conf_type & CONF_MASK_BYTES;
3576 boolean element_found = FALSE;
3579 micro_chunk_size += 1;
3581 if (byte_mask == CONF_MASK_MULTI_BYTES)
3583 int num_bytes = getFile16BitBE(file);
3584 byte *buffer = checked_malloc(num_bytes);
3586 ReadBytesFromFile(file, buffer, num_bytes);
3588 for (i = 0; conf[i].data_type != -1; i++)
3590 if (conf[i].element == element &&
3591 conf[i].conf_type == conf_type)
3593 int data_type = conf[i].data_type;
3594 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3595 int max_num_entities = conf[i].max_num_entities;
3597 if (num_entities > max_num_entities)
3599 Warn("truncating number of entities for element %d from %d to %d",
3600 element, num_entities, max_num_entities);
3602 num_entities = max_num_entities;
3605 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3606 data_type == TYPE_CONTENT_LIST))
3608 // for element and content lists, zero entities are not allowed
3609 Warn("found empty list of entities for element %d", element);
3611 // do not set "num_entities" here to prevent reading behind buffer
3613 *(int *)(conf[i].num_entities) = 1; // at least one is required
3617 *(int *)(conf[i].num_entities) = num_entities;
3620 element_found = TRUE;
3622 if (data_type == TYPE_STRING)
3624 char *string = (char *)(conf[i].value);
3627 for (j = 0; j < max_num_entities; j++)
3628 string[j] = (j < num_entities ? buffer[j] : '\0');
3630 else if (data_type == TYPE_ELEMENT_LIST)
3632 int *element_array = (int *)(conf[i].value);
3635 for (j = 0; j < num_entities; j++)
3637 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3639 else if (data_type == TYPE_CONTENT_LIST)
3641 struct Content *content= (struct Content *)(conf[i].value);
3644 for (c = 0; c < num_entities; c++)
3645 for (y = 0; y < 3; y++)
3646 for (x = 0; x < 3; x++)
3647 content[c].e[x][y] =
3648 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3651 element_found = FALSE;
3657 checked_free(buffer);
3659 micro_chunk_size += 2 + num_bytes;
3661 else // constant size configuration data (1, 2 or 4 bytes)
3663 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3664 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3665 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3667 for (i = 0; conf[i].data_type != -1; i++)
3669 if (conf[i].element == element &&
3670 conf[i].conf_type == conf_type)
3672 int data_type = conf[i].data_type;
3674 if (data_type == TYPE_ELEMENT)
3675 value = getMappedElement(value);
3677 if (data_type == TYPE_BOOLEAN)
3678 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3680 *(int *) (conf[i].value) = value;
3682 element_found = TRUE;
3688 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3693 char *error_conf_chunk_bytes =
3694 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3695 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3696 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3697 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3698 int error_element = real_element;
3700 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3701 error_conf_chunk_bytes, error_conf_chunk_token,
3702 error_element, EL_NAME(error_element));
3705 return micro_chunk_size;
3708 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3710 int real_chunk_size = 0;
3712 li = *level; // copy level data into temporary buffer
3714 while (!checkEndOfFile(file))
3716 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3718 if (real_chunk_size >= chunk_size)
3722 *level = li; // copy temporary buffer back to level data
3724 return real_chunk_size;
3727 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3729 int real_chunk_size = 0;
3731 li = *level; // copy level data into temporary buffer
3733 while (!checkEndOfFile(file))
3735 int element = getMappedElement(getFile16BitBE(file));
3737 real_chunk_size += 2;
3738 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3740 if (real_chunk_size >= chunk_size)
3744 *level = li; // copy temporary buffer back to level data
3746 return real_chunk_size;
3749 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3751 int real_chunk_size = 0;
3753 li = *level; // copy level data into temporary buffer
3755 while (!checkEndOfFile(file))
3757 int element = getMappedElement(getFile16BitBE(file));
3759 real_chunk_size += 2;
3760 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3762 if (real_chunk_size >= chunk_size)
3766 *level = li; // copy temporary buffer back to level data
3768 return real_chunk_size;
3771 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3773 int element = getMappedElement(getFile16BitBE(file));
3774 int envelope_nr = element - EL_ENVELOPE_1;
3775 int real_chunk_size = 2;
3777 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3779 while (!checkEndOfFile(file))
3781 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3784 if (real_chunk_size >= chunk_size)
3788 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3790 return real_chunk_size;
3793 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3795 int element = getMappedElement(getFile16BitBE(file));
3796 int real_chunk_size = 2;
3797 struct ElementInfo *ei = &element_info[element];
3800 xx_ei = *ei; // copy element data into temporary buffer
3802 xx_ei.num_change_pages = -1;
3804 while (!checkEndOfFile(file))
3806 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3808 if (xx_ei.num_change_pages != -1)
3811 if (real_chunk_size >= chunk_size)
3817 if (ei->num_change_pages == -1)
3819 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3822 ei->num_change_pages = 1;
3824 setElementChangePages(ei, 1);
3825 setElementChangeInfoToDefaults(ei->change);
3827 return real_chunk_size;
3830 // initialize number of change pages stored for this custom element
3831 setElementChangePages(ei, ei->num_change_pages);
3832 for (i = 0; i < ei->num_change_pages; i++)
3833 setElementChangeInfoToDefaults(&ei->change_page[i]);
3835 // start with reading properties for the first change page
3836 xx_current_change_page = 0;
3838 while (!checkEndOfFile(file))
3840 // level file might contain invalid change page number
3841 if (xx_current_change_page >= ei->num_change_pages)
3844 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3846 xx_change = *change; // copy change data into temporary buffer
3848 resetEventBits(); // reset bits; change page might have changed
3850 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3853 *change = xx_change;
3855 setEventFlagsFromEventBits(change);
3857 if (real_chunk_size >= chunk_size)
3861 level->file_has_custom_elements = TRUE;
3863 return real_chunk_size;
3866 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3868 int element = getMappedElement(getFile16BitBE(file));
3869 int real_chunk_size = 2;
3870 struct ElementInfo *ei = &element_info[element];
3871 struct ElementGroupInfo *group = ei->group;
3876 xx_ei = *ei; // copy element data into temporary buffer
3877 xx_group = *group; // copy group data into temporary buffer
3879 while (!checkEndOfFile(file))
3881 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3884 if (real_chunk_size >= chunk_size)
3891 level->file_has_custom_elements = TRUE;
3893 return real_chunk_size;
3896 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3898 int element = getMappedElement(getFile16BitBE(file));
3899 int real_chunk_size = 2;
3900 struct ElementInfo *ei = &element_info[element];
3902 xx_ei = *ei; // copy element data into temporary buffer
3904 while (!checkEndOfFile(file))
3906 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3909 if (real_chunk_size >= chunk_size)
3915 level->file_has_custom_elements = TRUE;
3917 return real_chunk_size;
3920 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3921 struct LevelFileInfo *level_file_info,
3922 boolean level_info_only)
3924 char *filename = level_file_info->filename;
3925 char cookie[MAX_LINE_LEN];
3926 char chunk_name[CHUNK_ID_LEN + 1];
3930 if (!(file = openFile(filename, MODE_READ)))
3932 level->no_valid_file = TRUE;
3933 level->no_level_file = TRUE;
3935 if (level_info_only)
3938 Warn("cannot read level '%s' -- using empty level", filename);
3940 if (!setup.editor.use_template_for_new_levels)
3943 // if level file not found, try to initialize level data from template
3944 filename = getGlobalLevelTemplateFilename();
3946 if (!(file = openFile(filename, MODE_READ)))
3949 // default: for empty levels, use level template for custom elements
3950 level->use_custom_template = TRUE;
3952 level->no_valid_file = FALSE;
3955 getFileChunkBE(file, chunk_name, NULL);
3956 if (strEqual(chunk_name, "RND1"))
3958 getFile32BitBE(file); // not used
3960 getFileChunkBE(file, chunk_name, NULL);
3961 if (!strEqual(chunk_name, "CAVE"))
3963 level->no_valid_file = TRUE;
3965 Warn("unknown format of level file '%s'", filename);
3972 else // check for pre-2.0 file format with cookie string
3974 strcpy(cookie, chunk_name);
3975 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3977 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3978 cookie[strlen(cookie) - 1] = '\0';
3980 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3982 level->no_valid_file = TRUE;
3984 Warn("unknown format of level file '%s'", filename);
3991 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3993 level->no_valid_file = TRUE;
3995 Warn("unsupported version of level file '%s'", filename);
4002 // pre-2.0 level files have no game version, so use file version here
4003 level->game_version = level->file_version;
4006 if (level->file_version < FILE_VERSION_1_2)
4008 // level files from versions before 1.2.0 without chunk structure
4009 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4010 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4018 int (*loader)(File *, int, struct LevelInfo *);
4022 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4023 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4024 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4025 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4026 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4027 { "INFO", -1, LoadLevel_INFO },
4028 { "BODY", -1, LoadLevel_BODY },
4029 { "CONT", -1, LoadLevel_CONT },
4030 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4031 { "CNT3", -1, LoadLevel_CNT3 },
4032 { "CUS1", -1, LoadLevel_CUS1 },
4033 { "CUS2", -1, LoadLevel_CUS2 },
4034 { "CUS3", -1, LoadLevel_CUS3 },
4035 { "CUS4", -1, LoadLevel_CUS4 },
4036 { "GRP1", -1, LoadLevel_GRP1 },
4037 { "CONF", -1, LoadLevel_CONF },
4038 { "ELEM", -1, LoadLevel_ELEM },
4039 { "NOTE", -1, LoadLevel_NOTE },
4040 { "CUSX", -1, LoadLevel_CUSX },
4041 { "GRPX", -1, LoadLevel_GRPX },
4042 { "EMPX", -1, LoadLevel_EMPX },
4047 while (getFileChunkBE(file, chunk_name, &chunk_size))
4051 while (chunk_info[i].name != NULL &&
4052 !strEqual(chunk_name, chunk_info[i].name))
4055 if (chunk_info[i].name == NULL)
4057 Warn("unknown chunk '%s' in level file '%s'",
4058 chunk_name, filename);
4060 ReadUnusedBytesFromFile(file, chunk_size);
4062 else if (chunk_info[i].size != -1 &&
4063 chunk_info[i].size != chunk_size)
4065 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4066 chunk_size, chunk_name, filename);
4068 ReadUnusedBytesFromFile(file, chunk_size);
4072 // call function to load this level chunk
4073 int chunk_size_expected =
4074 (chunk_info[i].loader)(file, chunk_size, level);
4076 if (chunk_size_expected < 0)
4078 Warn("error reading chunk '%s' in level file '%s'",
4079 chunk_name, filename);
4084 // the size of some chunks cannot be checked before reading other
4085 // chunks first (like "HEAD" and "BODY") that contain some header
4086 // information, so check them here
4087 if (chunk_size_expected != chunk_size)
4089 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4090 chunk_size, chunk_name, filename);
4102 // ----------------------------------------------------------------------------
4103 // functions for loading BD level
4104 // ----------------------------------------------------------------------------
4106 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4107 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4109 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4111 struct LevelInfo_BD *level_bd = level->native_bd_level;
4112 GdCave *cave = NULL; // will be changed below
4113 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4114 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4117 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4119 // cave and map newly allocated when set to defaults above
4120 cave = level_bd->cave;
4123 cave->intermission = level->bd_intermission;
4126 cave->level_time[0] = level->time;
4127 cave->level_diamonds[0] = level->gems_needed;
4130 cave->scheduling = level->bd_scheduling_type;
4131 cave->pal_timing = level->bd_pal_timing;
4132 cave->level_speed[0] = level->bd_cycle_delay_ms;
4133 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4134 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4135 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4138 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4139 cave->diamond_value = level->score[SC_EMERALD];
4140 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4142 // compatibility settings
4143 cave->lineshift = level->bd_line_shifting_borders;
4144 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4145 cave->short_explosions = level->bd_short_explosions;
4146 cave->gravity_affects_all = level->bd_gravity_affects_all;
4148 // player properties
4149 cave->diagonal_movements = level->bd_diagonal_movements;
4150 cave->active_is_first_found = level->bd_topmost_player_active;
4151 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4152 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4153 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4154 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4156 // element properties
4157 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4158 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4159 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4160 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4161 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4162 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4163 cave->level_magic_wall_time[0] = level->time_magic_wall;
4164 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4165 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4167 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4168 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4169 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4170 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4171 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4172 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4173 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4175 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4176 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4177 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4178 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4179 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4180 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4181 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4182 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4183 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4184 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4185 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4187 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4188 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4189 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4190 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4191 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4192 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4194 cave->slime_predictable = level->bd_slime_is_predictable;
4195 cave->slime_correct_random = level->bd_slime_correct_random;
4196 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4197 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4198 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4199 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4200 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4201 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4202 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4203 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4204 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4205 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4207 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4208 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4209 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4211 cave->biter_delay_frame = level->bd_biter_move_delay;
4212 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4214 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4216 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4218 cave->replicators_active = level->bd_replicators_active;
4219 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4221 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4222 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4224 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4226 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4228 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4229 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4230 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4232 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4233 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4235 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4236 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4238 cave->creatures_backwards = level->bd_creatures_start_backwards;
4239 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4240 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4243 strncpy(cave->name, level->name, sizeof(GdString));
4244 cave->name[sizeof(GdString) - 1] = '\0';
4246 // playfield elements
4247 for (x = 0; x < cave->w; x++)
4248 for (y = 0; y < cave->h; y++)
4249 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4252 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4254 struct LevelInfo_BD *level_bd = level->native_bd_level;
4255 GdCave *cave = level_bd->cave;
4256 int bd_level_nr = level_bd->level_nr;
4259 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4260 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4263 level->bd_intermission = cave->intermission;
4266 level->time = cave->level_time[bd_level_nr];
4267 level->gems_needed = cave->level_diamonds[bd_level_nr];
4270 level->bd_scheduling_type = cave->scheduling;
4271 level->bd_pal_timing = cave->pal_timing;
4272 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4273 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4274 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4275 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4278 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4279 level->score[SC_EMERALD] = cave->diamond_value;
4280 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4282 // compatibility settings
4283 level->bd_line_shifting_borders = cave->lineshift;
4284 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4285 level->bd_short_explosions = cave->short_explosions;
4286 level->bd_gravity_affects_all = cave->gravity_affects_all;
4288 // player properties
4289 level->bd_diagonal_movements = cave->diagonal_movements;
4290 level->bd_topmost_player_active = cave->active_is_first_found;
4291 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4292 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4293 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4294 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4296 // element properties
4297 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4298 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4299 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4300 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4301 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4302 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4303 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4304 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4305 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4307 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4308 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4309 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4310 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4311 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4312 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4313 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4315 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4316 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4317 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4318 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4319 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4320 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4321 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4322 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4323 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4324 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4325 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4327 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4328 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4329 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4330 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4331 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4332 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4334 level->bd_slime_is_predictable = cave->slime_predictable;
4335 level->bd_slime_correct_random = cave->slime_correct_random;
4336 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4337 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4338 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4339 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4340 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4341 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4342 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4343 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4344 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4345 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4347 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4348 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4349 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4351 level->bd_biter_move_delay = cave->biter_delay_frame;
4352 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4354 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4356 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4358 level->bd_replicators_active = cave->replicators_active;
4359 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4361 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4362 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4364 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4366 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4368 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4369 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4370 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4372 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4373 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4375 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4376 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4378 level->bd_creatures_start_backwards = cave->creatures_backwards;
4379 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4380 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4383 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4385 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4386 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4388 // playfield elements
4389 for (x = 0; x < level->fieldx; x++)
4390 for (y = 0; y < level->fieldy; y++)
4391 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4393 checked_free(cave_name);
4396 static void setTapeInfoToDefaults(void);
4398 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4400 struct LevelInfo_BD *level_bd = level->native_bd_level;
4401 GdCave *cave = level_bd->cave;
4402 GdReplay *replay = level_bd->replay;
4408 // always start with reliable default values
4409 setTapeInfoToDefaults();
4411 tape.level_nr = level_nr; // (currently not used)
4412 tape.random_seed = replay->seed;
4414 TapeSetDateFromIsoDateString(replay->date);
4417 tape.pos[tape.counter].delay = 0;
4419 tape.bd_replay = TRUE;
4421 // all time calculations only used to display approximate tape time
4422 int cave_speed = cave->speed;
4423 int milliseconds_game = 0;
4424 int milliseconds_elapsed = 20;
4426 for (i = 0; i < replay->movements->len; i++)
4428 int replay_action = replay->movements->data[i];
4429 int tape_action = map_action_BD_to_RND(replay_action);
4430 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4431 boolean success = 0;
4435 success = TapeAddAction(action);
4437 milliseconds_game += milliseconds_elapsed;
4439 if (milliseconds_game >= cave_speed)
4441 milliseconds_game -= cave_speed;
4448 tape.pos[tape.counter].delay = 0;
4449 tape.pos[tape.counter].action[0] = 0;
4453 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4459 TapeHaltRecording();
4463 // ----------------------------------------------------------------------------
4464 // functions for loading EM level
4465 // ----------------------------------------------------------------------------
4467 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4469 static int ball_xy[8][2] =
4480 struct LevelInfo_EM *level_em = level->native_em_level;
4481 struct CAVE *cav = level_em->cav;
4484 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4485 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4487 cav->time_seconds = level->time;
4488 cav->gems_needed = level->gems_needed;
4490 cav->emerald_score = level->score[SC_EMERALD];
4491 cav->diamond_score = level->score[SC_DIAMOND];
4492 cav->alien_score = level->score[SC_ROBOT];
4493 cav->tank_score = level->score[SC_SPACESHIP];
4494 cav->bug_score = level->score[SC_BUG];
4495 cav->eater_score = level->score[SC_YAMYAM];
4496 cav->nut_score = level->score[SC_NUT];
4497 cav->dynamite_score = level->score[SC_DYNAMITE];
4498 cav->key_score = level->score[SC_KEY];
4499 cav->exit_score = level->score[SC_TIME_BONUS];
4501 cav->num_eater_arrays = level->num_yamyam_contents;
4503 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4504 for (y = 0; y < 3; y++)
4505 for (x = 0; x < 3; x++)
4506 cav->eater_array[i][y * 3 + x] =
4507 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4509 cav->amoeba_time = level->amoeba_speed;
4510 cav->wonderwall_time = level->time_magic_wall;
4511 cav->wheel_time = level->time_wheel;
4513 cav->android_move_time = level->android_move_time;
4514 cav->android_clone_time = level->android_clone_time;
4515 cav->ball_random = level->ball_random;
4516 cav->ball_active = level->ball_active_initial;
4517 cav->ball_time = level->ball_time;
4518 cav->num_ball_arrays = level->num_ball_contents;
4520 cav->lenses_score = level->lenses_score;
4521 cav->magnify_score = level->magnify_score;
4522 cav->slurp_score = level->slurp_score;
4524 cav->lenses_time = level->lenses_time;
4525 cav->magnify_time = level->magnify_time;
4527 cav->wind_time = 9999;
4528 cav->wind_direction =
4529 map_direction_RND_to_EM(level->wind_direction_initial);
4531 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4532 for (j = 0; j < 8; j++)
4533 cav->ball_array[i][j] =
4534 map_element_RND_to_EM_cave(level->ball_content[i].
4535 e[ball_xy[j][0]][ball_xy[j][1]]);
4537 map_android_clone_elements_RND_to_EM(level);
4539 // first fill the complete playfield with the empty space element
4540 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4541 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4542 cav->cave[x][y] = Cblank;
4544 // then copy the real level contents from level file into the playfield
4545 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4547 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4549 if (level->field[x][y] == EL_AMOEBA_DEAD)
4550 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4552 cav->cave[x][y] = new_element;
4555 for (i = 0; i < MAX_PLAYERS; i++)
4557 cav->player_x[i] = -1;
4558 cav->player_y[i] = -1;
4561 // initialize player positions and delete players from the playfield
4562 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4564 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4566 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4568 cav->player_x[player_nr] = x;
4569 cav->player_y[player_nr] = y;
4571 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4576 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4578 static int ball_xy[8][2] =
4589 struct LevelInfo_EM *level_em = level->native_em_level;
4590 struct CAVE *cav = level_em->cav;
4593 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4594 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4596 level->time = cav->time_seconds;
4597 level->gems_needed = cav->gems_needed;
4599 sprintf(level->name, "Level %d", level->file_info.nr);
4601 level->score[SC_EMERALD] = cav->emerald_score;
4602 level->score[SC_DIAMOND] = cav->diamond_score;
4603 level->score[SC_ROBOT] = cav->alien_score;
4604 level->score[SC_SPACESHIP] = cav->tank_score;
4605 level->score[SC_BUG] = cav->bug_score;
4606 level->score[SC_YAMYAM] = cav->eater_score;
4607 level->score[SC_NUT] = cav->nut_score;
4608 level->score[SC_DYNAMITE] = cav->dynamite_score;
4609 level->score[SC_KEY] = cav->key_score;
4610 level->score[SC_TIME_BONUS] = cav->exit_score;
4612 level->num_yamyam_contents = cav->num_eater_arrays;
4614 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4615 for (y = 0; y < 3; y++)
4616 for (x = 0; x < 3; x++)
4617 level->yamyam_content[i].e[x][y] =
4618 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4620 level->amoeba_speed = cav->amoeba_time;
4621 level->time_magic_wall = cav->wonderwall_time;
4622 level->time_wheel = cav->wheel_time;
4624 level->android_move_time = cav->android_move_time;
4625 level->android_clone_time = cav->android_clone_time;
4626 level->ball_random = cav->ball_random;
4627 level->ball_active_initial = cav->ball_active;
4628 level->ball_time = cav->ball_time;
4629 level->num_ball_contents = cav->num_ball_arrays;
4631 level->lenses_score = cav->lenses_score;
4632 level->magnify_score = cav->magnify_score;
4633 level->slurp_score = cav->slurp_score;
4635 level->lenses_time = cav->lenses_time;
4636 level->magnify_time = cav->magnify_time;
4638 level->wind_direction_initial =
4639 map_direction_EM_to_RND(cav->wind_direction);
4641 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4642 for (j = 0; j < 8; j++)
4643 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4644 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4646 map_android_clone_elements_EM_to_RND(level);
4648 // convert the playfield (some elements need special treatment)
4649 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4651 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4653 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4654 new_element = EL_AMOEBA_DEAD;
4656 level->field[x][y] = new_element;
4659 for (i = 0; i < MAX_PLAYERS; i++)
4661 // in case of all players set to the same field, use the first player
4662 int nr = MAX_PLAYERS - i - 1;
4663 int jx = cav->player_x[nr];
4664 int jy = cav->player_y[nr];
4666 if (jx != -1 && jy != -1)
4667 level->field[jx][jy] = EL_PLAYER_1 + nr;
4670 // time score is counted for each 10 seconds left in Emerald Mine levels
4671 level->time_score_base = 10;
4675 // ----------------------------------------------------------------------------
4676 // functions for loading SP level
4677 // ----------------------------------------------------------------------------
4679 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4681 struct LevelInfo_SP *level_sp = level->native_sp_level;
4682 LevelInfoType *header = &level_sp->header;
4685 level_sp->width = level->fieldx;
4686 level_sp->height = level->fieldy;
4688 for (x = 0; x < level->fieldx; x++)
4689 for (y = 0; y < level->fieldy; y++)
4690 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4692 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4694 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4695 header->LevelTitle[i] = level->name[i];
4696 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4698 header->InfotronsNeeded = level->gems_needed;
4700 header->SpecialPortCount = 0;
4702 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4704 boolean gravity_port_found = FALSE;
4705 boolean gravity_port_valid = FALSE;
4706 int gravity_port_flag;
4707 int gravity_port_base_element;
4708 int element = level->field[x][y];
4710 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4711 element <= EL_SP_GRAVITY_ON_PORT_UP)
4713 gravity_port_found = TRUE;
4714 gravity_port_valid = TRUE;
4715 gravity_port_flag = 1;
4716 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4718 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4719 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4721 gravity_port_found = TRUE;
4722 gravity_port_valid = TRUE;
4723 gravity_port_flag = 0;
4724 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4726 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4727 element <= EL_SP_GRAVITY_PORT_UP)
4729 // change R'n'D style gravity inverting special port to normal port
4730 // (there are no gravity inverting ports in native Supaplex engine)
4732 gravity_port_found = TRUE;
4733 gravity_port_valid = FALSE;
4734 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4737 if (gravity_port_found)
4739 if (gravity_port_valid &&
4740 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4742 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4744 port->PortLocation = (y * level->fieldx + x) * 2;
4745 port->Gravity = gravity_port_flag;
4747 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4749 header->SpecialPortCount++;
4753 // change special gravity port to normal port
4755 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4758 level_sp->playfield[x][y] = element - EL_SP_START;
4763 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4765 struct LevelInfo_SP *level_sp = level->native_sp_level;
4766 LevelInfoType *header = &level_sp->header;
4767 boolean num_invalid_elements = 0;
4770 level->fieldx = level_sp->width;
4771 level->fieldy = level_sp->height;
4773 for (x = 0; x < level->fieldx; x++)
4775 for (y = 0; y < level->fieldy; y++)
4777 int element_old = level_sp->playfield[x][y];
4778 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4780 if (element_new == EL_UNKNOWN)
4782 num_invalid_elements++;
4784 Debug("level:native:SP", "invalid element %d at position %d, %d",
4788 level->field[x][y] = element_new;
4792 if (num_invalid_elements > 0)
4793 Warn("found %d invalid elements%s", num_invalid_elements,
4794 (!options.debug ? " (use '--debug' for more details)" : ""));
4796 for (i = 0; i < MAX_PLAYERS; i++)
4797 level->initial_player_gravity[i] =
4798 (header->InitialGravity == 1 ? TRUE : FALSE);
4800 // skip leading spaces
4801 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4802 if (header->LevelTitle[i] != ' ')
4806 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4807 level->name[j] = header->LevelTitle[i];
4808 level->name[j] = '\0';
4810 // cut trailing spaces
4812 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4813 level->name[j - 1] = '\0';
4815 level->gems_needed = header->InfotronsNeeded;
4817 for (i = 0; i < header->SpecialPortCount; i++)
4819 SpecialPortType *port = &header->SpecialPort[i];
4820 int port_location = port->PortLocation;
4821 int gravity = port->Gravity;
4822 int port_x, port_y, port_element;
4824 port_x = (port_location / 2) % level->fieldx;
4825 port_y = (port_location / 2) / level->fieldx;
4827 if (port_x < 0 || port_x >= level->fieldx ||
4828 port_y < 0 || port_y >= level->fieldy)
4830 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4835 port_element = level->field[port_x][port_y];
4837 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4838 port_element > EL_SP_GRAVITY_PORT_UP)
4840 Warn("no special port at position (%d, %d)", port_x, port_y);
4845 // change previous (wrong) gravity inverting special port to either
4846 // gravity enabling special port or gravity disabling special port
4847 level->field[port_x][port_y] +=
4848 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4849 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4852 // change special gravity ports without database entries to normal ports
4853 for (x = 0; x < level->fieldx; x++)
4854 for (y = 0; y < level->fieldy; y++)
4855 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4856 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4857 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4859 level->time = 0; // no time limit
4860 level->amoeba_speed = 0;
4861 level->time_magic_wall = 0;
4862 level->time_wheel = 0;
4863 level->amoeba_content = EL_EMPTY;
4865 // original Supaplex does not use score values -- rate by playing time
4866 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4867 level->score[i] = 0;
4869 level->rate_time_over_score = TRUE;
4871 // there are no yamyams in supaplex levels
4872 for (i = 0; i < level->num_yamyam_contents; i++)
4873 for (x = 0; x < 3; x++)
4874 for (y = 0; y < 3; y++)
4875 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4878 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4880 struct LevelInfo_SP *level_sp = level->native_sp_level;
4881 struct DemoInfo_SP *demo = &level_sp->demo;
4884 // always start with reliable default values
4885 demo->is_available = FALSE;
4888 if (TAPE_IS_EMPTY(tape))
4891 demo->level_nr = tape.level_nr; // (currently not used)
4893 level_sp->header.DemoRandomSeed = tape.random_seed;
4897 for (i = 0; i < tape.length; i++)
4899 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4900 int demo_repeat = tape.pos[i].delay;
4901 int demo_entries = (demo_repeat + 15) / 16;
4903 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4905 Warn("tape truncated: size exceeds maximum SP demo size %d",
4911 for (j = 0; j < demo_repeat / 16; j++)
4912 demo->data[demo->length++] = 0xf0 | demo_action;
4914 if (demo_repeat % 16)
4915 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4918 demo->is_available = TRUE;
4921 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4923 struct LevelInfo_SP *level_sp = level->native_sp_level;
4924 struct DemoInfo_SP *demo = &level_sp->demo;
4925 char *filename = level->file_info.filename;
4928 // always start with reliable default values
4929 setTapeInfoToDefaults();
4931 if (!demo->is_available)
4934 tape.level_nr = demo->level_nr; // (currently not used)
4935 tape.random_seed = level_sp->header.DemoRandomSeed;
4937 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4940 tape.pos[tape.counter].delay = 0;
4942 for (i = 0; i < demo->length; i++)
4944 int demo_action = demo->data[i] & 0x0f;
4945 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4946 int tape_action = map_key_SP_to_RND(demo_action);
4947 int tape_repeat = demo_repeat + 1;
4948 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4949 boolean success = 0;
4952 for (j = 0; j < tape_repeat; j++)
4953 success = TapeAddAction(action);
4957 Warn("SP demo truncated: size exceeds maximum tape size %d",
4964 TapeHaltRecording();
4968 // ----------------------------------------------------------------------------
4969 // functions for loading MM level
4970 // ----------------------------------------------------------------------------
4972 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4974 struct LevelInfo_MM *level_mm = level->native_mm_level;
4977 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4978 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4980 level_mm->time = level->time;
4981 level_mm->kettles_needed = level->gems_needed;
4982 level_mm->auto_count_kettles = level->auto_count_gems;
4984 level_mm->mm_laser_red = level->mm_laser_red;
4985 level_mm->mm_laser_green = level->mm_laser_green;
4986 level_mm->mm_laser_blue = level->mm_laser_blue;
4988 level_mm->df_laser_red = level->df_laser_red;
4989 level_mm->df_laser_green = level->df_laser_green;
4990 level_mm->df_laser_blue = level->df_laser_blue;
4992 strcpy(level_mm->name, level->name);
4993 strcpy(level_mm->author, level->author);
4995 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4996 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4997 level_mm->score[SC_KEY] = level->score[SC_KEY];
4998 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4999 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5001 level_mm->amoeba_speed = level->amoeba_speed;
5002 level_mm->time_fuse = level->mm_time_fuse;
5003 level_mm->time_bomb = level->mm_time_bomb;
5004 level_mm->time_ball = level->mm_time_ball;
5005 level_mm->time_block = level->mm_time_block;
5007 level_mm->num_ball_contents = level->num_mm_ball_contents;
5008 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5009 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5010 level_mm->explode_ball = level->explode_mm_ball;
5012 for (i = 0; i < level->num_mm_ball_contents; i++)
5013 level_mm->ball_content[i] =
5014 map_element_RND_to_MM(level->mm_ball_content[i]);
5016 for (x = 0; x < level->fieldx; x++)
5017 for (y = 0; y < level->fieldy; y++)
5019 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5022 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5024 struct LevelInfo_MM *level_mm = level->native_mm_level;
5027 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5028 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5030 level->time = level_mm->time;
5031 level->gems_needed = level_mm->kettles_needed;
5032 level->auto_count_gems = level_mm->auto_count_kettles;
5034 level->mm_laser_red = level_mm->mm_laser_red;
5035 level->mm_laser_green = level_mm->mm_laser_green;
5036 level->mm_laser_blue = level_mm->mm_laser_blue;
5038 level->df_laser_red = level_mm->df_laser_red;
5039 level->df_laser_green = level_mm->df_laser_green;
5040 level->df_laser_blue = level_mm->df_laser_blue;
5042 strcpy(level->name, level_mm->name);
5044 // only overwrite author from 'levelinfo.conf' if author defined in level
5045 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5046 strcpy(level->author, level_mm->author);
5048 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5049 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5050 level->score[SC_KEY] = level_mm->score[SC_KEY];
5051 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5052 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5054 level->amoeba_speed = level_mm->amoeba_speed;
5055 level->mm_time_fuse = level_mm->time_fuse;
5056 level->mm_time_bomb = level_mm->time_bomb;
5057 level->mm_time_ball = level_mm->time_ball;
5058 level->mm_time_block = level_mm->time_block;
5060 level->num_mm_ball_contents = level_mm->num_ball_contents;
5061 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5062 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5063 level->explode_mm_ball = level_mm->explode_ball;
5065 for (i = 0; i < level->num_mm_ball_contents; i++)
5066 level->mm_ball_content[i] =
5067 map_element_MM_to_RND(level_mm->ball_content[i]);
5069 for (x = 0; x < level->fieldx; x++)
5070 for (y = 0; y < level->fieldy; y++)
5071 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5075 // ----------------------------------------------------------------------------
5076 // functions for loading DC level
5077 // ----------------------------------------------------------------------------
5079 #define DC_LEVEL_HEADER_SIZE 344
5081 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5084 static int last_data_encoded;
5088 int diff_hi, diff_lo;
5089 int data_hi, data_lo;
5090 unsigned short data_decoded;
5094 last_data_encoded = 0;
5101 diff = data_encoded - last_data_encoded;
5102 diff_hi = diff & ~0xff;
5103 diff_lo = diff & 0xff;
5107 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5108 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5109 data_hi = data_hi & 0xff00;
5111 data_decoded = data_hi | data_lo;
5113 last_data_encoded = data_encoded;
5115 offset1 = (offset1 + 1) % 31;
5116 offset2 = offset2 & 0xff;
5118 return data_decoded;
5121 static int getMappedElement_DC(int element)
5129 // 0x0117 - 0x036e: (?)
5132 // 0x042d - 0x0684: (?)
5148 element = EL_CRYSTAL;
5151 case 0x0e77: // quicksand (boulder)
5152 element = EL_QUICKSAND_FAST_FULL;
5155 case 0x0e99: // slow quicksand (boulder)
5156 element = EL_QUICKSAND_FULL;
5160 element = EL_EM_EXIT_OPEN;
5164 element = EL_EM_EXIT_CLOSED;
5168 element = EL_EM_STEEL_EXIT_OPEN;
5172 element = EL_EM_STEEL_EXIT_CLOSED;
5175 case 0x0f4f: // dynamite (lit 1)
5176 element = EL_EM_DYNAMITE_ACTIVE;
5179 case 0x0f57: // dynamite (lit 2)
5180 element = EL_EM_DYNAMITE_ACTIVE;
5183 case 0x0f5f: // dynamite (lit 3)
5184 element = EL_EM_DYNAMITE_ACTIVE;
5187 case 0x0f67: // dynamite (lit 4)
5188 element = EL_EM_DYNAMITE_ACTIVE;
5195 element = EL_AMOEBA_WET;
5199 element = EL_AMOEBA_DROP;
5203 element = EL_DC_MAGIC_WALL;
5207 element = EL_SPACESHIP_UP;
5211 element = EL_SPACESHIP_DOWN;
5215 element = EL_SPACESHIP_LEFT;
5219 element = EL_SPACESHIP_RIGHT;
5223 element = EL_BUG_UP;
5227 element = EL_BUG_DOWN;
5231 element = EL_BUG_LEFT;
5235 element = EL_BUG_RIGHT;
5239 element = EL_MOLE_UP;
5243 element = EL_MOLE_DOWN;
5247 element = EL_MOLE_LEFT;
5251 element = EL_MOLE_RIGHT;
5259 element = EL_YAMYAM_UP;
5263 element = EL_SWITCHGATE_OPEN;
5267 element = EL_SWITCHGATE_CLOSED;
5271 element = EL_DC_SWITCHGATE_SWITCH_UP;
5275 element = EL_TIMEGATE_CLOSED;
5278 case 0x144c: // conveyor belt switch (green)
5279 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5282 case 0x144f: // conveyor belt switch (red)
5283 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5286 case 0x1452: // conveyor belt switch (blue)
5287 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5291 element = EL_CONVEYOR_BELT_3_MIDDLE;
5295 element = EL_CONVEYOR_BELT_3_LEFT;
5299 element = EL_CONVEYOR_BELT_3_RIGHT;
5303 element = EL_CONVEYOR_BELT_1_MIDDLE;
5307 element = EL_CONVEYOR_BELT_1_LEFT;
5311 element = EL_CONVEYOR_BELT_1_RIGHT;
5315 element = EL_CONVEYOR_BELT_4_MIDDLE;
5319 element = EL_CONVEYOR_BELT_4_LEFT;
5323 element = EL_CONVEYOR_BELT_4_RIGHT;
5327 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5331 element = EL_EXPANDABLE_WALL_VERTICAL;
5335 element = EL_EXPANDABLE_WALL_ANY;
5338 case 0x14ce: // growing steel wall (left/right)
5339 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5342 case 0x14df: // growing steel wall (up/down)
5343 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5346 case 0x14e8: // growing steel wall (up/down/left/right)
5347 element = EL_EXPANDABLE_STEELWALL_ANY;
5351 element = EL_SHIELD_DEADLY;
5355 element = EL_EXTRA_TIME;
5363 element = EL_EMPTY_SPACE;
5366 case 0x1578: // quicksand (empty)
5367 element = EL_QUICKSAND_FAST_EMPTY;
5370 case 0x1579: // slow quicksand (empty)
5371 element = EL_QUICKSAND_EMPTY;
5381 element = EL_EM_DYNAMITE;
5384 case 0x15a1: // key (red)
5385 element = EL_EM_KEY_1;
5388 case 0x15a2: // key (yellow)
5389 element = EL_EM_KEY_2;
5392 case 0x15a3: // key (blue)
5393 element = EL_EM_KEY_4;
5396 case 0x15a4: // key (green)
5397 element = EL_EM_KEY_3;
5400 case 0x15a5: // key (white)
5401 element = EL_DC_KEY_WHITE;
5405 element = EL_WALL_SLIPPERY;
5412 case 0x15a8: // wall (not round)
5416 case 0x15a9: // (blue)
5417 element = EL_CHAR_A;
5420 case 0x15aa: // (blue)
5421 element = EL_CHAR_B;
5424 case 0x15ab: // (blue)
5425 element = EL_CHAR_C;
5428 case 0x15ac: // (blue)
5429 element = EL_CHAR_D;
5432 case 0x15ad: // (blue)
5433 element = EL_CHAR_E;
5436 case 0x15ae: // (blue)
5437 element = EL_CHAR_F;
5440 case 0x15af: // (blue)
5441 element = EL_CHAR_G;
5444 case 0x15b0: // (blue)
5445 element = EL_CHAR_H;
5448 case 0x15b1: // (blue)
5449 element = EL_CHAR_I;
5452 case 0x15b2: // (blue)
5453 element = EL_CHAR_J;
5456 case 0x15b3: // (blue)
5457 element = EL_CHAR_K;
5460 case 0x15b4: // (blue)
5461 element = EL_CHAR_L;
5464 case 0x15b5: // (blue)
5465 element = EL_CHAR_M;
5468 case 0x15b6: // (blue)
5469 element = EL_CHAR_N;
5472 case 0x15b7: // (blue)
5473 element = EL_CHAR_O;
5476 case 0x15b8: // (blue)
5477 element = EL_CHAR_P;
5480 case 0x15b9: // (blue)
5481 element = EL_CHAR_Q;
5484 case 0x15ba: // (blue)
5485 element = EL_CHAR_R;
5488 case 0x15bb: // (blue)
5489 element = EL_CHAR_S;
5492 case 0x15bc: // (blue)
5493 element = EL_CHAR_T;
5496 case 0x15bd: // (blue)
5497 element = EL_CHAR_U;
5500 case 0x15be: // (blue)
5501 element = EL_CHAR_V;
5504 case 0x15bf: // (blue)
5505 element = EL_CHAR_W;
5508 case 0x15c0: // (blue)
5509 element = EL_CHAR_X;
5512 case 0x15c1: // (blue)
5513 element = EL_CHAR_Y;
5516 case 0x15c2: // (blue)
5517 element = EL_CHAR_Z;
5520 case 0x15c3: // (blue)
5521 element = EL_CHAR_AUMLAUT;
5524 case 0x15c4: // (blue)
5525 element = EL_CHAR_OUMLAUT;
5528 case 0x15c5: // (blue)
5529 element = EL_CHAR_UUMLAUT;
5532 case 0x15c6: // (blue)
5533 element = EL_CHAR_0;
5536 case 0x15c7: // (blue)
5537 element = EL_CHAR_1;
5540 case 0x15c8: // (blue)
5541 element = EL_CHAR_2;
5544 case 0x15c9: // (blue)
5545 element = EL_CHAR_3;
5548 case 0x15ca: // (blue)
5549 element = EL_CHAR_4;
5552 case 0x15cb: // (blue)
5553 element = EL_CHAR_5;
5556 case 0x15cc: // (blue)
5557 element = EL_CHAR_6;
5560 case 0x15cd: // (blue)
5561 element = EL_CHAR_7;
5564 case 0x15ce: // (blue)
5565 element = EL_CHAR_8;
5568 case 0x15cf: // (blue)
5569 element = EL_CHAR_9;
5572 case 0x15d0: // (blue)
5573 element = EL_CHAR_PERIOD;
5576 case 0x15d1: // (blue)
5577 element = EL_CHAR_EXCLAM;
5580 case 0x15d2: // (blue)
5581 element = EL_CHAR_COLON;
5584 case 0x15d3: // (blue)
5585 element = EL_CHAR_LESS;
5588 case 0x15d4: // (blue)
5589 element = EL_CHAR_GREATER;
5592 case 0x15d5: // (blue)
5593 element = EL_CHAR_QUESTION;
5596 case 0x15d6: // (blue)
5597 element = EL_CHAR_COPYRIGHT;
5600 case 0x15d7: // (blue)
5601 element = EL_CHAR_UP;
5604 case 0x15d8: // (blue)
5605 element = EL_CHAR_DOWN;
5608 case 0x15d9: // (blue)
5609 element = EL_CHAR_BUTTON;
5612 case 0x15da: // (blue)
5613 element = EL_CHAR_PLUS;
5616 case 0x15db: // (blue)
5617 element = EL_CHAR_MINUS;
5620 case 0x15dc: // (blue)
5621 element = EL_CHAR_APOSTROPHE;
5624 case 0x15dd: // (blue)
5625 element = EL_CHAR_PARENLEFT;
5628 case 0x15de: // (blue)
5629 element = EL_CHAR_PARENRIGHT;
5632 case 0x15df: // (green)
5633 element = EL_CHAR_A;
5636 case 0x15e0: // (green)
5637 element = EL_CHAR_B;
5640 case 0x15e1: // (green)
5641 element = EL_CHAR_C;
5644 case 0x15e2: // (green)
5645 element = EL_CHAR_D;
5648 case 0x15e3: // (green)
5649 element = EL_CHAR_E;
5652 case 0x15e4: // (green)
5653 element = EL_CHAR_F;
5656 case 0x15e5: // (green)
5657 element = EL_CHAR_G;
5660 case 0x15e6: // (green)
5661 element = EL_CHAR_H;
5664 case 0x15e7: // (green)
5665 element = EL_CHAR_I;
5668 case 0x15e8: // (green)
5669 element = EL_CHAR_J;
5672 case 0x15e9: // (green)
5673 element = EL_CHAR_K;
5676 case 0x15ea: // (green)
5677 element = EL_CHAR_L;
5680 case 0x15eb: // (green)
5681 element = EL_CHAR_M;
5684 case 0x15ec: // (green)
5685 element = EL_CHAR_N;
5688 case 0x15ed: // (green)
5689 element = EL_CHAR_O;
5692 case 0x15ee: // (green)
5693 element = EL_CHAR_P;
5696 case 0x15ef: // (green)
5697 element = EL_CHAR_Q;
5700 case 0x15f0: // (green)
5701 element = EL_CHAR_R;
5704 case 0x15f1: // (green)
5705 element = EL_CHAR_S;
5708 case 0x15f2: // (green)
5709 element = EL_CHAR_T;
5712 case 0x15f3: // (green)
5713 element = EL_CHAR_U;
5716 case 0x15f4: // (green)
5717 element = EL_CHAR_V;
5720 case 0x15f5: // (green)
5721 element = EL_CHAR_W;
5724 case 0x15f6: // (green)
5725 element = EL_CHAR_X;
5728 case 0x15f7: // (green)
5729 element = EL_CHAR_Y;
5732 case 0x15f8: // (green)
5733 element = EL_CHAR_Z;
5736 case 0x15f9: // (green)
5737 element = EL_CHAR_AUMLAUT;
5740 case 0x15fa: // (green)
5741 element = EL_CHAR_OUMLAUT;
5744 case 0x15fb: // (green)
5745 element = EL_CHAR_UUMLAUT;
5748 case 0x15fc: // (green)
5749 element = EL_CHAR_0;
5752 case 0x15fd: // (green)
5753 element = EL_CHAR_1;
5756 case 0x15fe: // (green)
5757 element = EL_CHAR_2;
5760 case 0x15ff: // (green)
5761 element = EL_CHAR_3;
5764 case 0x1600: // (green)
5765 element = EL_CHAR_4;
5768 case 0x1601: // (green)
5769 element = EL_CHAR_5;
5772 case 0x1602: // (green)
5773 element = EL_CHAR_6;
5776 case 0x1603: // (green)
5777 element = EL_CHAR_7;
5780 case 0x1604: // (green)
5781 element = EL_CHAR_8;
5784 case 0x1605: // (green)
5785 element = EL_CHAR_9;
5788 case 0x1606: // (green)
5789 element = EL_CHAR_PERIOD;
5792 case 0x1607: // (green)
5793 element = EL_CHAR_EXCLAM;
5796 case 0x1608: // (green)
5797 element = EL_CHAR_COLON;
5800 case 0x1609: // (green)
5801 element = EL_CHAR_LESS;
5804 case 0x160a: // (green)
5805 element = EL_CHAR_GREATER;
5808 case 0x160b: // (green)
5809 element = EL_CHAR_QUESTION;
5812 case 0x160c: // (green)
5813 element = EL_CHAR_COPYRIGHT;
5816 case 0x160d: // (green)
5817 element = EL_CHAR_UP;
5820 case 0x160e: // (green)
5821 element = EL_CHAR_DOWN;
5824 case 0x160f: // (green)
5825 element = EL_CHAR_BUTTON;
5828 case 0x1610: // (green)
5829 element = EL_CHAR_PLUS;
5832 case 0x1611: // (green)
5833 element = EL_CHAR_MINUS;
5836 case 0x1612: // (green)
5837 element = EL_CHAR_APOSTROPHE;
5840 case 0x1613: // (green)
5841 element = EL_CHAR_PARENLEFT;
5844 case 0x1614: // (green)
5845 element = EL_CHAR_PARENRIGHT;
5848 case 0x1615: // (blue steel)
5849 element = EL_STEEL_CHAR_A;
5852 case 0x1616: // (blue steel)
5853 element = EL_STEEL_CHAR_B;
5856 case 0x1617: // (blue steel)
5857 element = EL_STEEL_CHAR_C;
5860 case 0x1618: // (blue steel)
5861 element = EL_STEEL_CHAR_D;
5864 case 0x1619: // (blue steel)
5865 element = EL_STEEL_CHAR_E;
5868 case 0x161a: // (blue steel)
5869 element = EL_STEEL_CHAR_F;
5872 case 0x161b: // (blue steel)
5873 element = EL_STEEL_CHAR_G;
5876 case 0x161c: // (blue steel)
5877 element = EL_STEEL_CHAR_H;
5880 case 0x161d: // (blue steel)
5881 element = EL_STEEL_CHAR_I;
5884 case 0x161e: // (blue steel)
5885 element = EL_STEEL_CHAR_J;
5888 case 0x161f: // (blue steel)
5889 element = EL_STEEL_CHAR_K;
5892 case 0x1620: // (blue steel)
5893 element = EL_STEEL_CHAR_L;
5896 case 0x1621: // (blue steel)
5897 element = EL_STEEL_CHAR_M;
5900 case 0x1622: // (blue steel)
5901 element = EL_STEEL_CHAR_N;
5904 case 0x1623: // (blue steel)
5905 element = EL_STEEL_CHAR_O;
5908 case 0x1624: // (blue steel)
5909 element = EL_STEEL_CHAR_P;
5912 case 0x1625: // (blue steel)
5913 element = EL_STEEL_CHAR_Q;
5916 case 0x1626: // (blue steel)
5917 element = EL_STEEL_CHAR_R;
5920 case 0x1627: // (blue steel)
5921 element = EL_STEEL_CHAR_S;
5924 case 0x1628: // (blue steel)
5925 element = EL_STEEL_CHAR_T;
5928 case 0x1629: // (blue steel)
5929 element = EL_STEEL_CHAR_U;
5932 case 0x162a: // (blue steel)
5933 element = EL_STEEL_CHAR_V;
5936 case 0x162b: // (blue steel)
5937 element = EL_STEEL_CHAR_W;
5940 case 0x162c: // (blue steel)
5941 element = EL_STEEL_CHAR_X;
5944 case 0x162d: // (blue steel)
5945 element = EL_STEEL_CHAR_Y;
5948 case 0x162e: // (blue steel)
5949 element = EL_STEEL_CHAR_Z;
5952 case 0x162f: // (blue steel)
5953 element = EL_STEEL_CHAR_AUMLAUT;
5956 case 0x1630: // (blue steel)
5957 element = EL_STEEL_CHAR_OUMLAUT;
5960 case 0x1631: // (blue steel)
5961 element = EL_STEEL_CHAR_UUMLAUT;
5964 case 0x1632: // (blue steel)
5965 element = EL_STEEL_CHAR_0;
5968 case 0x1633: // (blue steel)
5969 element = EL_STEEL_CHAR_1;
5972 case 0x1634: // (blue steel)
5973 element = EL_STEEL_CHAR_2;
5976 case 0x1635: // (blue steel)
5977 element = EL_STEEL_CHAR_3;
5980 case 0x1636: // (blue steel)
5981 element = EL_STEEL_CHAR_4;
5984 case 0x1637: // (blue steel)
5985 element = EL_STEEL_CHAR_5;
5988 case 0x1638: // (blue steel)
5989 element = EL_STEEL_CHAR_6;
5992 case 0x1639: // (blue steel)
5993 element = EL_STEEL_CHAR_7;
5996 case 0x163a: // (blue steel)
5997 element = EL_STEEL_CHAR_8;
6000 case 0x163b: // (blue steel)
6001 element = EL_STEEL_CHAR_9;
6004 case 0x163c: // (blue steel)
6005 element = EL_STEEL_CHAR_PERIOD;
6008 case 0x163d: // (blue steel)
6009 element = EL_STEEL_CHAR_EXCLAM;
6012 case 0x163e: // (blue steel)
6013 element = EL_STEEL_CHAR_COLON;
6016 case 0x163f: // (blue steel)
6017 element = EL_STEEL_CHAR_LESS;
6020 case 0x1640: // (blue steel)
6021 element = EL_STEEL_CHAR_GREATER;
6024 case 0x1641: // (blue steel)
6025 element = EL_STEEL_CHAR_QUESTION;
6028 case 0x1642: // (blue steel)
6029 element = EL_STEEL_CHAR_COPYRIGHT;
6032 case 0x1643: // (blue steel)
6033 element = EL_STEEL_CHAR_UP;
6036 case 0x1644: // (blue steel)
6037 element = EL_STEEL_CHAR_DOWN;
6040 case 0x1645: // (blue steel)
6041 element = EL_STEEL_CHAR_BUTTON;
6044 case 0x1646: // (blue steel)
6045 element = EL_STEEL_CHAR_PLUS;
6048 case 0x1647: // (blue steel)
6049 element = EL_STEEL_CHAR_MINUS;
6052 case 0x1648: // (blue steel)
6053 element = EL_STEEL_CHAR_APOSTROPHE;
6056 case 0x1649: // (blue steel)
6057 element = EL_STEEL_CHAR_PARENLEFT;
6060 case 0x164a: // (blue steel)
6061 element = EL_STEEL_CHAR_PARENRIGHT;
6064 case 0x164b: // (green steel)
6065 element = EL_STEEL_CHAR_A;
6068 case 0x164c: // (green steel)
6069 element = EL_STEEL_CHAR_B;
6072 case 0x164d: // (green steel)
6073 element = EL_STEEL_CHAR_C;
6076 case 0x164e: // (green steel)
6077 element = EL_STEEL_CHAR_D;
6080 case 0x164f: // (green steel)
6081 element = EL_STEEL_CHAR_E;
6084 case 0x1650: // (green steel)
6085 element = EL_STEEL_CHAR_F;
6088 case 0x1651: // (green steel)
6089 element = EL_STEEL_CHAR_G;
6092 case 0x1652: // (green steel)
6093 element = EL_STEEL_CHAR_H;
6096 case 0x1653: // (green steel)
6097 element = EL_STEEL_CHAR_I;
6100 case 0x1654: // (green steel)
6101 element = EL_STEEL_CHAR_J;
6104 case 0x1655: // (green steel)
6105 element = EL_STEEL_CHAR_K;
6108 case 0x1656: // (green steel)
6109 element = EL_STEEL_CHAR_L;
6112 case 0x1657: // (green steel)
6113 element = EL_STEEL_CHAR_M;
6116 case 0x1658: // (green steel)
6117 element = EL_STEEL_CHAR_N;
6120 case 0x1659: // (green steel)
6121 element = EL_STEEL_CHAR_O;
6124 case 0x165a: // (green steel)
6125 element = EL_STEEL_CHAR_P;
6128 case 0x165b: // (green steel)
6129 element = EL_STEEL_CHAR_Q;
6132 case 0x165c: // (green steel)
6133 element = EL_STEEL_CHAR_R;
6136 case 0x165d: // (green steel)
6137 element = EL_STEEL_CHAR_S;
6140 case 0x165e: // (green steel)
6141 element = EL_STEEL_CHAR_T;
6144 case 0x165f: // (green steel)
6145 element = EL_STEEL_CHAR_U;
6148 case 0x1660: // (green steel)
6149 element = EL_STEEL_CHAR_V;
6152 case 0x1661: // (green steel)
6153 element = EL_STEEL_CHAR_W;
6156 case 0x1662: // (green steel)
6157 element = EL_STEEL_CHAR_X;
6160 case 0x1663: // (green steel)
6161 element = EL_STEEL_CHAR_Y;
6164 case 0x1664: // (green steel)
6165 element = EL_STEEL_CHAR_Z;
6168 case 0x1665: // (green steel)
6169 element = EL_STEEL_CHAR_AUMLAUT;
6172 case 0x1666: // (green steel)
6173 element = EL_STEEL_CHAR_OUMLAUT;
6176 case 0x1667: // (green steel)
6177 element = EL_STEEL_CHAR_UUMLAUT;
6180 case 0x1668: // (green steel)
6181 element = EL_STEEL_CHAR_0;
6184 case 0x1669: // (green steel)
6185 element = EL_STEEL_CHAR_1;
6188 case 0x166a: // (green steel)
6189 element = EL_STEEL_CHAR_2;
6192 case 0x166b: // (green steel)
6193 element = EL_STEEL_CHAR_3;
6196 case 0x166c: // (green steel)
6197 element = EL_STEEL_CHAR_4;
6200 case 0x166d: // (green steel)
6201 element = EL_STEEL_CHAR_5;
6204 case 0x166e: // (green steel)
6205 element = EL_STEEL_CHAR_6;
6208 case 0x166f: // (green steel)
6209 element = EL_STEEL_CHAR_7;
6212 case 0x1670: // (green steel)
6213 element = EL_STEEL_CHAR_8;
6216 case 0x1671: // (green steel)
6217 element = EL_STEEL_CHAR_9;
6220 case 0x1672: // (green steel)
6221 element = EL_STEEL_CHAR_PERIOD;
6224 case 0x1673: // (green steel)
6225 element = EL_STEEL_CHAR_EXCLAM;
6228 case 0x1674: // (green steel)
6229 element = EL_STEEL_CHAR_COLON;
6232 case 0x1675: // (green steel)
6233 element = EL_STEEL_CHAR_LESS;
6236 case 0x1676: // (green steel)
6237 element = EL_STEEL_CHAR_GREATER;
6240 case 0x1677: // (green steel)
6241 element = EL_STEEL_CHAR_QUESTION;
6244 case 0x1678: // (green steel)
6245 element = EL_STEEL_CHAR_COPYRIGHT;
6248 case 0x1679: // (green steel)
6249 element = EL_STEEL_CHAR_UP;
6252 case 0x167a: // (green steel)
6253 element = EL_STEEL_CHAR_DOWN;
6256 case 0x167b: // (green steel)
6257 element = EL_STEEL_CHAR_BUTTON;
6260 case 0x167c: // (green steel)
6261 element = EL_STEEL_CHAR_PLUS;
6264 case 0x167d: // (green steel)
6265 element = EL_STEEL_CHAR_MINUS;
6268 case 0x167e: // (green steel)
6269 element = EL_STEEL_CHAR_APOSTROPHE;
6272 case 0x167f: // (green steel)
6273 element = EL_STEEL_CHAR_PARENLEFT;
6276 case 0x1680: // (green steel)
6277 element = EL_STEEL_CHAR_PARENRIGHT;
6280 case 0x1681: // gate (red)
6281 element = EL_EM_GATE_1;
6284 case 0x1682: // secret gate (red)
6285 element = EL_EM_GATE_1_GRAY;
6288 case 0x1683: // gate (yellow)
6289 element = EL_EM_GATE_2;
6292 case 0x1684: // secret gate (yellow)
6293 element = EL_EM_GATE_2_GRAY;
6296 case 0x1685: // gate (blue)
6297 element = EL_EM_GATE_4;
6300 case 0x1686: // secret gate (blue)
6301 element = EL_EM_GATE_4_GRAY;
6304 case 0x1687: // gate (green)
6305 element = EL_EM_GATE_3;
6308 case 0x1688: // secret gate (green)
6309 element = EL_EM_GATE_3_GRAY;
6312 case 0x1689: // gate (white)
6313 element = EL_DC_GATE_WHITE;
6316 case 0x168a: // secret gate (white)
6317 element = EL_DC_GATE_WHITE_GRAY;
6320 case 0x168b: // secret gate (no key)
6321 element = EL_DC_GATE_FAKE_GRAY;
6325 element = EL_ROBOT_WHEEL;
6329 element = EL_DC_TIMEGATE_SWITCH;
6333 element = EL_ACID_POOL_BOTTOM;
6337 element = EL_ACID_POOL_TOPLEFT;
6341 element = EL_ACID_POOL_TOPRIGHT;
6345 element = EL_ACID_POOL_BOTTOMLEFT;
6349 element = EL_ACID_POOL_BOTTOMRIGHT;
6353 element = EL_STEELWALL;
6357 element = EL_STEELWALL_SLIPPERY;
6360 case 0x1695: // steel wall (not round)
6361 element = EL_STEELWALL;
6364 case 0x1696: // steel wall (left)
6365 element = EL_DC_STEELWALL_1_LEFT;
6368 case 0x1697: // steel wall (bottom)
6369 element = EL_DC_STEELWALL_1_BOTTOM;
6372 case 0x1698: // steel wall (right)
6373 element = EL_DC_STEELWALL_1_RIGHT;
6376 case 0x1699: // steel wall (top)
6377 element = EL_DC_STEELWALL_1_TOP;
6380 case 0x169a: // steel wall (left/bottom)
6381 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6384 case 0x169b: // steel wall (right/bottom)
6385 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6388 case 0x169c: // steel wall (right/top)
6389 element = EL_DC_STEELWALL_1_TOPRIGHT;
6392 case 0x169d: // steel wall (left/top)
6393 element = EL_DC_STEELWALL_1_TOPLEFT;
6396 case 0x169e: // steel wall (right/bottom small)
6397 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6400 case 0x169f: // steel wall (left/bottom small)
6401 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6404 case 0x16a0: // steel wall (right/top small)
6405 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6408 case 0x16a1: // steel wall (left/top small)
6409 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6412 case 0x16a2: // steel wall (left/right)
6413 element = EL_DC_STEELWALL_1_VERTICAL;
6416 case 0x16a3: // steel wall (top/bottom)
6417 element = EL_DC_STEELWALL_1_HORIZONTAL;
6420 case 0x16a4: // steel wall 2 (left end)
6421 element = EL_DC_STEELWALL_2_LEFT;
6424 case 0x16a5: // steel wall 2 (right end)
6425 element = EL_DC_STEELWALL_2_RIGHT;
6428 case 0x16a6: // steel wall 2 (top end)
6429 element = EL_DC_STEELWALL_2_TOP;
6432 case 0x16a7: // steel wall 2 (bottom end)
6433 element = EL_DC_STEELWALL_2_BOTTOM;
6436 case 0x16a8: // steel wall 2 (left/right)
6437 element = EL_DC_STEELWALL_2_HORIZONTAL;
6440 case 0x16a9: // steel wall 2 (up/down)
6441 element = EL_DC_STEELWALL_2_VERTICAL;
6444 case 0x16aa: // steel wall 2 (mid)
6445 element = EL_DC_STEELWALL_2_MIDDLE;
6449 element = EL_SIGN_EXCLAMATION;
6453 element = EL_SIGN_RADIOACTIVITY;
6457 element = EL_SIGN_STOP;
6461 element = EL_SIGN_WHEELCHAIR;
6465 element = EL_SIGN_PARKING;
6469 element = EL_SIGN_NO_ENTRY;
6473 element = EL_SIGN_HEART;
6477 element = EL_SIGN_GIVE_WAY;
6481 element = EL_SIGN_ENTRY_FORBIDDEN;
6485 element = EL_SIGN_EMERGENCY_EXIT;
6489 element = EL_SIGN_YIN_YANG;
6493 element = EL_WALL_EMERALD;
6497 element = EL_WALL_DIAMOND;
6501 element = EL_WALL_PEARL;
6505 element = EL_WALL_CRYSTAL;
6509 element = EL_INVISIBLE_WALL;
6513 element = EL_INVISIBLE_STEELWALL;
6517 // EL_INVISIBLE_SAND
6520 element = EL_LIGHT_SWITCH;
6524 element = EL_ENVELOPE_1;
6528 if (element >= 0x0117 && element <= 0x036e) // (?)
6529 element = EL_DIAMOND;
6530 else if (element >= 0x042d && element <= 0x0684) // (?)
6531 element = EL_EMERALD;
6532 else if (element >= 0x157c && element <= 0x158b)
6534 else if (element >= 0x1590 && element <= 0x159f)
6535 element = EL_DC_LANDMINE;
6536 else if (element >= 0x16bc && element <= 0x16cb)
6537 element = EL_INVISIBLE_SAND;
6540 Warn("unknown Diamond Caves element 0x%04x", element);
6542 element = EL_UNKNOWN;
6547 return getMappedElement(element);
6550 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6552 byte header[DC_LEVEL_HEADER_SIZE];
6554 int envelope_header_pos = 62;
6555 int envelope_content_pos = 94;
6556 int level_name_pos = 251;
6557 int level_author_pos = 292;
6558 int envelope_header_len;
6559 int envelope_content_len;
6561 int level_author_len;
6563 int num_yamyam_contents;
6566 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6568 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6570 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6572 header[i * 2 + 0] = header_word >> 8;
6573 header[i * 2 + 1] = header_word & 0xff;
6576 // read some values from level header to check level decoding integrity
6577 fieldx = header[6] | (header[7] << 8);
6578 fieldy = header[8] | (header[9] << 8);
6579 num_yamyam_contents = header[60] | (header[61] << 8);
6581 // do some simple sanity checks to ensure that level was correctly decoded
6582 if (fieldx < 1 || fieldx > 256 ||
6583 fieldy < 1 || fieldy > 256 ||
6584 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6586 level->no_valid_file = TRUE;
6588 Warn("cannot decode level from stream -- using empty level");
6593 // maximum envelope header size is 31 bytes
6594 envelope_header_len = header[envelope_header_pos];
6595 // maximum envelope content size is 110 (156?) bytes
6596 envelope_content_len = header[envelope_content_pos];
6598 // maximum level title size is 40 bytes
6599 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6600 // maximum level author size is 30 (51?) bytes
6601 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6605 for (i = 0; i < envelope_header_len; i++)
6606 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6607 level->envelope[0].text[envelope_size++] =
6608 header[envelope_header_pos + 1 + i];
6610 if (envelope_header_len > 0 && envelope_content_len > 0)
6612 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6613 level->envelope[0].text[envelope_size++] = '\n';
6614 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6615 level->envelope[0].text[envelope_size++] = '\n';
6618 for (i = 0; i < envelope_content_len; i++)
6619 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6620 level->envelope[0].text[envelope_size++] =
6621 header[envelope_content_pos + 1 + i];
6623 level->envelope[0].text[envelope_size] = '\0';
6625 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6626 level->envelope[0].ysize = 10;
6627 level->envelope[0].autowrap = TRUE;
6628 level->envelope[0].centered = TRUE;
6630 for (i = 0; i < level_name_len; i++)
6631 level->name[i] = header[level_name_pos + 1 + i];
6632 level->name[level_name_len] = '\0';
6634 for (i = 0; i < level_author_len; i++)
6635 level->author[i] = header[level_author_pos + 1 + i];
6636 level->author[level_author_len] = '\0';
6638 num_yamyam_contents = header[60] | (header[61] << 8);
6639 level->num_yamyam_contents =
6640 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6642 for (i = 0; i < num_yamyam_contents; i++)
6644 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6646 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6647 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6649 if (i < MAX_ELEMENT_CONTENTS)
6650 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6654 fieldx = header[6] | (header[7] << 8);
6655 fieldy = header[8] | (header[9] << 8);
6656 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6657 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6659 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6661 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6662 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6664 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6665 level->field[x][y] = getMappedElement_DC(element_dc);
6668 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6669 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6670 level->field[x][y] = EL_PLAYER_1;
6672 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6673 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6674 level->field[x][y] = EL_PLAYER_2;
6676 level->gems_needed = header[18] | (header[19] << 8);
6678 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6679 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6680 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6681 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6682 level->score[SC_NUT] = header[28] | (header[29] << 8);
6683 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6684 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6685 level->score[SC_BUG] = header[34] | (header[35] << 8);
6686 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6687 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6688 level->score[SC_KEY] = header[40] | (header[41] << 8);
6689 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6691 level->time = header[44] | (header[45] << 8);
6693 level->amoeba_speed = header[46] | (header[47] << 8);
6694 level->time_light = header[48] | (header[49] << 8);
6695 level->time_timegate = header[50] | (header[51] << 8);
6696 level->time_wheel = header[52] | (header[53] << 8);
6697 level->time_magic_wall = header[54] | (header[55] << 8);
6698 level->extra_time = header[56] | (header[57] << 8);
6699 level->shield_normal_time = header[58] | (header[59] << 8);
6701 // shield and extra time elements do not have a score
6702 level->score[SC_SHIELD] = 0;
6703 level->extra_time_score = 0;
6705 // set time for normal and deadly shields to the same value
6706 level->shield_deadly_time = level->shield_normal_time;
6708 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6709 // can slip down from flat walls, like normal walls and steel walls
6710 level->em_slippery_gems = TRUE;
6712 // time score is counted for each 10 seconds left in Diamond Caves levels
6713 level->time_score_base = 10;
6716 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6717 struct LevelFileInfo *level_file_info,
6718 boolean level_info_only)
6720 char *filename = level_file_info->filename;
6722 int num_magic_bytes = 8;
6723 char magic_bytes[num_magic_bytes + 1];
6724 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6726 if (!(file = openFile(filename, MODE_READ)))
6728 level->no_valid_file = TRUE;
6730 if (!level_info_only)
6731 Warn("cannot read level '%s' -- using empty level", filename);
6736 // fseek(file, 0x0000, SEEK_SET);
6738 if (level_file_info->packed)
6740 // read "magic bytes" from start of file
6741 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6742 magic_bytes[0] = '\0';
6744 // check "magic bytes" for correct file format
6745 if (!strPrefix(magic_bytes, "DC2"))
6747 level->no_valid_file = TRUE;
6749 Warn("unknown DC level file '%s' -- using empty level", filename);
6754 if (strPrefix(magic_bytes, "DC2Win95") ||
6755 strPrefix(magic_bytes, "DC2Win98"))
6757 int position_first_level = 0x00fa;
6758 int extra_bytes = 4;
6761 // advance file stream to first level inside the level package
6762 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6764 // each block of level data is followed by block of non-level data
6765 num_levels_to_skip *= 2;
6767 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6768 while (num_levels_to_skip >= 0)
6770 // advance file stream to next level inside the level package
6771 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6773 level->no_valid_file = TRUE;
6775 Warn("cannot fseek in file '%s' -- using empty level", filename);
6780 // skip apparently unused extra bytes following each level
6781 ReadUnusedBytesFromFile(file, extra_bytes);
6783 // read size of next level in level package
6784 skip_bytes = getFile32BitLE(file);
6786 num_levels_to_skip--;
6791 level->no_valid_file = TRUE;
6793 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6799 LoadLevelFromFileStream_DC(file, level);
6805 // ----------------------------------------------------------------------------
6806 // functions for loading SB level
6807 // ----------------------------------------------------------------------------
6809 int getMappedElement_SB(int element_ascii, boolean use_ces)
6817 sb_element_mapping[] =
6819 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6820 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6821 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6822 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6823 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6824 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6825 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6826 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6833 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6834 if (element_ascii == sb_element_mapping[i].ascii)
6835 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6837 return EL_UNDEFINED;
6840 static void SetLevelSettings_SB(struct LevelInfo *level)
6844 level->use_step_counter = TRUE;
6847 level->score[SC_TIME_BONUS] = 0;
6848 level->time_score_base = 1;
6849 level->rate_time_over_score = TRUE;
6852 level->auto_exit_sokoban = TRUE;
6855 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6856 struct LevelFileInfo *level_file_info,
6857 boolean level_info_only)
6859 char *filename = level_file_info->filename;
6860 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6861 char last_comment[MAX_LINE_LEN];
6862 char level_name[MAX_LINE_LEN];
6865 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6866 boolean read_continued_line = FALSE;
6867 boolean reading_playfield = FALSE;
6868 boolean got_valid_playfield_line = FALSE;
6869 boolean invalid_playfield_char = FALSE;
6870 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6871 int file_level_nr = 0;
6872 int x = 0, y = 0; // initialized to make compilers happy
6874 last_comment[0] = '\0';
6875 level_name[0] = '\0';
6877 if (!(file = openFile(filename, MODE_READ)))
6879 level->no_valid_file = TRUE;
6881 if (!level_info_only)
6882 Warn("cannot read level '%s' -- using empty level", filename);
6887 while (!checkEndOfFile(file))
6889 // level successfully read, but next level may follow here
6890 if (!got_valid_playfield_line && reading_playfield)
6892 // read playfield from single level file -- skip remaining file
6893 if (!level_file_info->packed)
6896 if (file_level_nr >= num_levels_to_skip)
6901 last_comment[0] = '\0';
6902 level_name[0] = '\0';
6904 reading_playfield = FALSE;
6907 got_valid_playfield_line = FALSE;
6909 // read next line of input file
6910 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6913 // cut trailing line break (this can be newline and/or carriage return)
6914 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6915 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6918 // copy raw input line for later use (mainly debugging output)
6919 strcpy(line_raw, line);
6921 if (read_continued_line)
6923 // append new line to existing line, if there is enough space
6924 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6925 strcat(previous_line, line_ptr);
6927 strcpy(line, previous_line); // copy storage buffer to line
6929 read_continued_line = FALSE;
6932 // if the last character is '\', continue at next line
6933 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6935 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6936 strcpy(previous_line, line); // copy line to storage buffer
6938 read_continued_line = TRUE;
6944 if (line[0] == '\0')
6947 // extract comment text from comment line
6950 for (line_ptr = line; *line_ptr; line_ptr++)
6951 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6954 strcpy(last_comment, line_ptr);
6959 // extract level title text from line containing level title
6960 if (line[0] == '\'')
6962 strcpy(level_name, &line[1]);
6964 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6965 level_name[strlen(level_name) - 1] = '\0';
6970 // skip lines containing only spaces (or empty lines)
6971 for (line_ptr = line; *line_ptr; line_ptr++)
6972 if (*line_ptr != ' ')
6974 if (*line_ptr == '\0')
6977 // at this point, we have found a line containing part of a playfield
6979 got_valid_playfield_line = TRUE;
6981 if (!reading_playfield)
6983 reading_playfield = TRUE;
6984 invalid_playfield_char = FALSE;
6986 for (x = 0; x < MAX_LEV_FIELDX; x++)
6987 for (y = 0; y < MAX_LEV_FIELDY; y++)
6988 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6993 // start with topmost tile row
6997 // skip playfield line if larger row than allowed
6998 if (y >= MAX_LEV_FIELDY)
7001 // start with leftmost tile column
7004 // read playfield elements from line
7005 for (line_ptr = line; *line_ptr; line_ptr++)
7007 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7009 // stop parsing playfield line if larger column than allowed
7010 if (x >= MAX_LEV_FIELDX)
7013 if (mapped_sb_element == EL_UNDEFINED)
7015 invalid_playfield_char = TRUE;
7020 level->field[x][y] = mapped_sb_element;
7022 // continue with next tile column
7025 level->fieldx = MAX(x, level->fieldx);
7028 if (invalid_playfield_char)
7030 // if first playfield line, treat invalid lines as comment lines
7032 reading_playfield = FALSE;
7037 // continue with next tile row
7045 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7046 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7048 if (!reading_playfield)
7050 level->no_valid_file = TRUE;
7052 Warn("cannot read level '%s' -- using empty level", filename);
7057 if (*level_name != '\0')
7059 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7060 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7062 else if (*last_comment != '\0')
7064 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7065 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7069 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7072 // set all empty fields beyond the border walls to invisible steel wall
7073 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7075 if ((x == 0 || x == level->fieldx - 1 ||
7076 y == 0 || y == level->fieldy - 1) &&
7077 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7078 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7079 level->field, level->fieldx, level->fieldy);
7082 // set special level settings for Sokoban levels
7083 SetLevelSettings_SB(level);
7085 if (load_xsb_to_ces)
7087 // special global settings can now be set in level template
7088 level->use_custom_template = TRUE;
7093 // -------------------------------------------------------------------------
7094 // functions for handling native levels
7095 // -------------------------------------------------------------------------
7097 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7098 struct LevelFileInfo *level_file_info,
7099 boolean level_info_only)
7103 // determine position of requested level inside level package
7104 if (level_file_info->packed)
7105 pos = level_file_info->nr - leveldir_current->first_level;
7107 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7108 level->no_valid_file = TRUE;
7111 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7112 struct LevelFileInfo *level_file_info,
7113 boolean level_info_only)
7115 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7116 level->no_valid_file = TRUE;
7119 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7120 struct LevelFileInfo *level_file_info,
7121 boolean level_info_only)
7125 // determine position of requested level inside level package
7126 if (level_file_info->packed)
7127 pos = level_file_info->nr - leveldir_current->first_level;
7129 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7130 level->no_valid_file = TRUE;
7133 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7134 struct LevelFileInfo *level_file_info,
7135 boolean level_info_only)
7137 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7138 level->no_valid_file = TRUE;
7141 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7143 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7144 CopyNativeLevel_RND_to_BD(level);
7145 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7146 CopyNativeLevel_RND_to_EM(level);
7147 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7148 CopyNativeLevel_RND_to_SP(level);
7149 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7150 CopyNativeLevel_RND_to_MM(level);
7153 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7155 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7156 CopyNativeLevel_BD_to_RND(level);
7157 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7158 CopyNativeLevel_EM_to_RND(level);
7159 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7160 CopyNativeLevel_SP_to_RND(level);
7161 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7162 CopyNativeLevel_MM_to_RND(level);
7165 void SaveNativeLevel(struct LevelInfo *level)
7167 // saving native level files only supported for some game engines
7168 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7169 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7172 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7173 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7174 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7175 char *filename = getLevelFilenameFromBasename(basename);
7177 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7180 boolean success = FALSE;
7182 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7184 CopyNativeLevel_RND_to_BD(level);
7185 // CopyNativeTape_RND_to_BD(level);
7187 success = SaveNativeLevel_BD(filename);
7189 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7191 CopyNativeLevel_RND_to_SP(level);
7192 CopyNativeTape_RND_to_SP(level);
7194 success = SaveNativeLevel_SP(filename);
7198 Request("Native level file saved!", REQ_CONFIRM);
7200 Request("Failed to save native level file!", REQ_CONFIRM);
7204 // ----------------------------------------------------------------------------
7205 // functions for loading generic level
7206 // ----------------------------------------------------------------------------
7208 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7209 struct LevelFileInfo *level_file_info,
7210 boolean level_info_only)
7212 // always start with reliable default values
7213 setLevelInfoToDefaults(level, level_info_only, TRUE);
7215 switch (level_file_info->type)
7217 case LEVEL_FILE_TYPE_RND:
7218 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7221 case LEVEL_FILE_TYPE_BD:
7222 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7223 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7226 case LEVEL_FILE_TYPE_EM:
7227 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7228 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7231 case LEVEL_FILE_TYPE_SP:
7232 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7233 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7236 case LEVEL_FILE_TYPE_MM:
7237 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7238 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7241 case LEVEL_FILE_TYPE_DC:
7242 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7245 case LEVEL_FILE_TYPE_SB:
7246 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7250 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7254 // if level file is invalid, restore level structure to default values
7255 if (level->no_valid_file)
7256 setLevelInfoToDefaults(level, level_info_only, FALSE);
7258 if (check_special_flags("use_native_bd_game_engine"))
7259 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7261 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7262 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7264 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7265 CopyNativeLevel_Native_to_RND(level);
7268 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7270 static struct LevelFileInfo level_file_info;
7272 // always start with reliable default values
7273 setFileInfoToDefaults(&level_file_info);
7275 level_file_info.nr = 0; // unknown level number
7276 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7278 setString(&level_file_info.filename, filename);
7280 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7283 static void LoadLevel_InitVersion(struct LevelInfo *level)
7287 if (leveldir_current == NULL) // only when dumping level
7290 // all engine modifications also valid for levels which use latest engine
7291 if (level->game_version < VERSION_IDENT(3,2,0,5))
7293 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7294 level->time_score_base = 10;
7297 if (leveldir_current->latest_engine)
7299 // ---------- use latest game engine --------------------------------------
7301 /* For all levels which are forced to use the latest game engine version
7302 (normally all but user contributed, private and undefined levels), set
7303 the game engine version to the actual version; this allows for actual
7304 corrections in the game engine to take effect for existing, converted
7305 levels (from "classic" or other existing games) to make the emulation
7306 of the corresponding game more accurate, while (hopefully) not breaking
7307 existing levels created from other players. */
7309 level->game_version = GAME_VERSION_ACTUAL;
7311 /* Set special EM style gems behaviour: EM style gems slip down from
7312 normal, steel and growing wall. As this is a more fundamental change,
7313 it seems better to set the default behaviour to "off" (as it is more
7314 natural) and make it configurable in the level editor (as a property
7315 of gem style elements). Already existing converted levels (neither
7316 private nor contributed levels) are changed to the new behaviour. */
7318 if (level->file_version < FILE_VERSION_2_0)
7319 level->em_slippery_gems = TRUE;
7324 // ---------- use game engine the level was created with --------------------
7326 /* For all levels which are not forced to use the latest game engine
7327 version (normally user contributed, private and undefined levels),
7328 use the version of the game engine the levels were created for.
7330 Since 2.0.1, the game engine version is now directly stored
7331 in the level file (chunk "VERS"), so there is no need anymore
7332 to set the game version from the file version (except for old,
7333 pre-2.0 levels, where the game version is still taken from the
7334 file format version used to store the level -- see above). */
7336 // player was faster than enemies in 1.0.0 and before
7337 if (level->file_version == FILE_VERSION_1_0)
7338 for (i = 0; i < MAX_PLAYERS; i++)
7339 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7341 // default behaviour for EM style gems was "slippery" only in 2.0.1
7342 if (level->game_version == VERSION_IDENT(2,0,1,0))
7343 level->em_slippery_gems = TRUE;
7345 // springs could be pushed over pits before (pre-release version) 2.2.0
7346 if (level->game_version < VERSION_IDENT(2,2,0,0))
7347 level->use_spring_bug = TRUE;
7349 if (level->game_version < VERSION_IDENT(3,2,0,5))
7351 // time orb caused limited time in endless time levels before 3.2.0-5
7352 level->use_time_orb_bug = TRUE;
7354 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7355 level->block_snap_field = FALSE;
7357 // extra time score was same value as time left score before 3.2.0-5
7358 level->extra_time_score = level->score[SC_TIME_BONUS];
7361 if (level->game_version < VERSION_IDENT(3,2,0,7))
7363 // default behaviour for snapping was "not continuous" before 3.2.0-7
7364 level->continuous_snapping = FALSE;
7367 // only few elements were able to actively move into acid before 3.1.0
7368 // trigger settings did not exist before 3.1.0; set to default "any"
7369 if (level->game_version < VERSION_IDENT(3,1,0,0))
7371 // correct "can move into acid" settings (all zero in old levels)
7373 level->can_move_into_acid_bits = 0; // nothing can move into acid
7374 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7376 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7377 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7378 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7379 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7381 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7382 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7384 // correct trigger settings (stored as zero == "none" in old levels)
7386 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7388 int element = EL_CUSTOM_START + i;
7389 struct ElementInfo *ei = &element_info[element];
7391 for (j = 0; j < ei->num_change_pages; j++)
7393 struct ElementChangeInfo *change = &ei->change_page[j];
7395 change->trigger_player = CH_PLAYER_ANY;
7396 change->trigger_page = CH_PAGE_ANY;
7401 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7403 int element = EL_CUSTOM_256;
7404 struct ElementInfo *ei = &element_info[element];
7405 struct ElementChangeInfo *change = &ei->change_page[0];
7407 /* This is needed to fix a problem that was caused by a bugfix in function
7408 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7409 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7410 not replace walkable elements, but instead just placed the player on it,
7411 without placing the Sokoban field under the player). Unfortunately, this
7412 breaks "Snake Bite" style levels when the snake is halfway through a door
7413 that just closes (the snake head is still alive and can be moved in this
7414 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7415 player (without Sokoban element) which then gets killed as designed). */
7417 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7418 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7419 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7420 change->target_element = EL_PLAYER_1;
7423 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7424 if (level->game_version < VERSION_IDENT(3,2,5,0))
7426 /* This is needed to fix a problem that was caused by a bugfix in function
7427 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7428 corrects the behaviour when a custom element changes to another custom
7429 element with a higher element number that has change actions defined.
7430 Normally, only one change per frame is allowed for custom elements.
7431 Therefore, it is checked if a custom element already changed in the
7432 current frame; if it did, subsequent changes are suppressed.
7433 Unfortunately, this is only checked for element changes, but not for
7434 change actions, which are still executed. As the function above loops
7435 through all custom elements from lower to higher, an element change
7436 resulting in a lower CE number won't be checked again, while a target
7437 element with a higher number will also be checked, and potential change
7438 actions will get executed for this CE, too (which is wrong), while
7439 further changes are ignored (which is correct). As this bugfix breaks
7440 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7441 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7442 behaviour for existing levels and tapes that make use of this bug */
7444 level->use_action_after_change_bug = TRUE;
7447 // not centering level after relocating player was default only in 3.2.3
7448 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7449 level->shifted_relocation = TRUE;
7451 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7452 if (level->game_version < VERSION_IDENT(3,2,6,0))
7453 level->em_explodes_by_fire = TRUE;
7455 // levels were solved by the first player entering an exit up to 4.1.0.0
7456 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7457 level->solved_by_one_player = TRUE;
7459 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7460 if (level->game_version < VERSION_IDENT(4,1,1,1))
7461 level->use_life_bugs = TRUE;
7463 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7464 if (level->game_version < VERSION_IDENT(4,1,1,1))
7465 level->sb_objects_needed = FALSE;
7467 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7468 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7469 level->finish_dig_collect = FALSE;
7471 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7472 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7473 level->keep_walkable_ce = TRUE;
7476 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7478 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7481 // check if this level is (not) a Sokoban level
7482 for (y = 0; y < level->fieldy; y++)
7483 for (x = 0; x < level->fieldx; x++)
7484 if (!IS_SB_ELEMENT(Tile[x][y]))
7485 is_sokoban_level = FALSE;
7487 if (is_sokoban_level)
7489 // set special level settings for Sokoban levels
7490 SetLevelSettings_SB(level);
7494 static void LoadLevel_InitSettings(struct LevelInfo *level)
7496 // adjust level settings for (non-native) Sokoban-style levels
7497 LoadLevel_InitSettings_SB(level);
7499 // rename levels with title "nameless level" or if renaming is forced
7500 if (leveldir_current->empty_level_name != NULL &&
7501 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7502 leveldir_current->force_level_name))
7503 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7504 leveldir_current->empty_level_name, level_nr);
7507 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7511 // map elements that have changed in newer versions
7512 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7513 level->game_version);
7514 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7515 for (x = 0; x < 3; x++)
7516 for (y = 0; y < 3; y++)
7517 level->yamyam_content[i].e[x][y] =
7518 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7519 level->game_version);
7523 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7527 // map custom element change events that have changed in newer versions
7528 // (these following values were accidentally changed in version 3.0.1)
7529 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7530 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7532 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7534 int element = EL_CUSTOM_START + i;
7536 // order of checking and copying events to be mapped is important
7537 // (do not change the start and end value -- they are constant)
7538 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7540 if (HAS_CHANGE_EVENT(element, j - 2))
7542 SET_CHANGE_EVENT(element, j - 2, FALSE);
7543 SET_CHANGE_EVENT(element, j, TRUE);
7547 // order of checking and copying events to be mapped is important
7548 // (do not change the start and end value -- they are constant)
7549 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7551 if (HAS_CHANGE_EVENT(element, j - 1))
7553 SET_CHANGE_EVENT(element, j - 1, FALSE);
7554 SET_CHANGE_EVENT(element, j, TRUE);
7560 // initialize "can_change" field for old levels with only one change page
7561 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7563 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7565 int element = EL_CUSTOM_START + i;
7567 if (CAN_CHANGE(element))
7568 element_info[element].change->can_change = TRUE;
7572 // correct custom element values (for old levels without these options)
7573 if (level->game_version < VERSION_IDENT(3,1,1,0))
7575 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7577 int element = EL_CUSTOM_START + i;
7578 struct ElementInfo *ei = &element_info[element];
7580 if (ei->access_direction == MV_NO_DIRECTION)
7581 ei->access_direction = MV_ALL_DIRECTIONS;
7585 // correct custom element values (fix invalid values for all versions)
7588 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7590 int element = EL_CUSTOM_START + i;
7591 struct ElementInfo *ei = &element_info[element];
7593 for (j = 0; j < ei->num_change_pages; j++)
7595 struct ElementChangeInfo *change = &ei->change_page[j];
7597 if (change->trigger_player == CH_PLAYER_NONE)
7598 change->trigger_player = CH_PLAYER_ANY;
7600 if (change->trigger_side == CH_SIDE_NONE)
7601 change->trigger_side = CH_SIDE_ANY;
7606 // initialize "can_explode" field for old levels which did not store this
7607 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7608 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7610 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7612 int element = EL_CUSTOM_START + i;
7614 if (EXPLODES_1X1_OLD(element))
7615 element_info[element].explosion_type = EXPLODES_1X1;
7617 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7618 EXPLODES_SMASHED(element) ||
7619 EXPLODES_IMPACT(element)));
7623 // correct previously hard-coded move delay values for maze runner style
7624 if (level->game_version < VERSION_IDENT(3,1,1,0))
7626 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7628 int element = EL_CUSTOM_START + i;
7630 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7632 // previously hard-coded and therefore ignored
7633 element_info[element].move_delay_fixed = 9;
7634 element_info[element].move_delay_random = 0;
7639 // set some other uninitialized values of custom elements in older levels
7640 if (level->game_version < VERSION_IDENT(3,1,0,0))
7642 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7644 int element = EL_CUSTOM_START + i;
7646 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7648 element_info[element].explosion_delay = 17;
7649 element_info[element].ignition_delay = 8;
7653 // set mouse click change events to work for left/middle/right mouse button
7654 if (level->game_version < VERSION_IDENT(4,2,3,0))
7656 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7658 int element = EL_CUSTOM_START + i;
7659 struct ElementInfo *ei = &element_info[element];
7661 for (j = 0; j < ei->num_change_pages; j++)
7663 struct ElementChangeInfo *change = &ei->change_page[j];
7665 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7666 change->has_event[CE_PRESSED_BY_MOUSE] ||
7667 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7668 change->has_event[CE_MOUSE_PRESSED_ON_X])
7669 change->trigger_side = CH_SIDE_ANY;
7675 static void LoadLevel_InitElements(struct LevelInfo *level)
7677 LoadLevel_InitStandardElements(level);
7679 if (level->file_has_custom_elements)
7680 LoadLevel_InitCustomElements(level);
7682 // initialize element properties for level editor etc.
7683 InitElementPropertiesEngine(level->game_version);
7684 InitElementPropertiesGfxElement();
7687 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7691 // map elements that have changed in newer versions
7692 for (y = 0; y < level->fieldy; y++)
7693 for (x = 0; x < level->fieldx; x++)
7694 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7695 level->game_version);
7697 // clear unused playfield data (nicer if level gets resized in editor)
7698 for (x = 0; x < MAX_LEV_FIELDX; x++)
7699 for (y = 0; y < MAX_LEV_FIELDY; y++)
7700 if (x >= level->fieldx || y >= level->fieldy)
7701 level->field[x][y] = EL_EMPTY;
7703 // copy elements to runtime playfield array
7704 for (x = 0; x < MAX_LEV_FIELDX; x++)
7705 for (y = 0; y < MAX_LEV_FIELDY; y++)
7706 Tile[x][y] = level->field[x][y];
7708 // initialize level size variables for faster access
7709 lev_fieldx = level->fieldx;
7710 lev_fieldy = level->fieldy;
7712 // determine border element for this level
7713 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7714 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7719 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7721 struct LevelFileInfo *level_file_info = &level->file_info;
7723 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7724 CopyNativeLevel_RND_to_Native(level);
7727 static void LoadLevelTemplate_LoadAndInit(void)
7729 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7731 LoadLevel_InitVersion(&level_template);
7732 LoadLevel_InitElements(&level_template);
7733 LoadLevel_InitSettings(&level_template);
7735 ActivateLevelTemplate();
7738 void LoadLevelTemplate(int nr)
7740 if (!fileExists(getGlobalLevelTemplateFilename()))
7742 Warn("no level template found for this level");
7747 setLevelFileInfo(&level_template.file_info, nr);
7749 LoadLevelTemplate_LoadAndInit();
7752 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7754 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7756 LoadLevelTemplate_LoadAndInit();
7759 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7761 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7763 if (level.use_custom_template)
7765 if (network_level != NULL)
7766 LoadNetworkLevelTemplate(network_level);
7768 LoadLevelTemplate(-1);
7771 LoadLevel_InitVersion(&level);
7772 LoadLevel_InitElements(&level);
7773 LoadLevel_InitPlayfield(&level);
7774 LoadLevel_InitSettings(&level);
7776 LoadLevel_InitNativeEngines(&level);
7779 void LoadLevel(int nr)
7781 SetLevelSetInfo(leveldir_current->identifier, nr);
7783 setLevelFileInfo(&level.file_info, nr);
7785 LoadLevel_LoadAndInit(NULL);
7788 void LoadLevelInfoOnly(int nr)
7790 setLevelFileInfo(&level.file_info, nr);
7792 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7795 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7797 SetLevelSetInfo(network_level->leveldir_identifier,
7798 network_level->file_info.nr);
7800 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7802 LoadLevel_LoadAndInit(network_level);
7805 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7809 chunk_size += putFileVersion(file, level->file_version);
7810 chunk_size += putFileVersion(file, level->game_version);
7815 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7819 chunk_size += putFile16BitBE(file, level->creation_date.year);
7820 chunk_size += putFile8Bit(file, level->creation_date.month);
7821 chunk_size += putFile8Bit(file, level->creation_date.day);
7826 #if ENABLE_HISTORIC_CHUNKS
7827 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7831 putFile8Bit(file, level->fieldx);
7832 putFile8Bit(file, level->fieldy);
7834 putFile16BitBE(file, level->time);
7835 putFile16BitBE(file, level->gems_needed);
7837 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7838 putFile8Bit(file, level->name[i]);
7840 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7841 putFile8Bit(file, level->score[i]);
7843 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7844 for (y = 0; y < 3; y++)
7845 for (x = 0; x < 3; x++)
7846 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7847 level->yamyam_content[i].e[x][y]));
7848 putFile8Bit(file, level->amoeba_speed);
7849 putFile8Bit(file, level->time_magic_wall);
7850 putFile8Bit(file, level->time_wheel);
7851 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7852 level->amoeba_content));
7853 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7854 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7855 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7856 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7858 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7860 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7861 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7862 putFile32BitBE(file, level->can_move_into_acid_bits);
7863 putFile8Bit(file, level->dont_collide_with_bits);
7865 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7866 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7868 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7869 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7870 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7872 putFile8Bit(file, level->game_engine_type);
7874 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7878 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7883 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7884 chunk_size += putFile8Bit(file, level->name[i]);
7889 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7894 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7895 chunk_size += putFile8Bit(file, level->author[i]);
7900 #if ENABLE_HISTORIC_CHUNKS
7901 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7906 for (y = 0; y < level->fieldy; y++)
7907 for (x = 0; x < level->fieldx; x++)
7908 if (level->encoding_16bit_field)
7909 chunk_size += putFile16BitBE(file, level->field[x][y]);
7911 chunk_size += putFile8Bit(file, level->field[x][y]);
7917 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7922 for (y = 0; y < level->fieldy; y++)
7923 for (x = 0; x < level->fieldx; x++)
7924 chunk_size += putFile16BitBE(file, level->field[x][y]);
7929 #if ENABLE_HISTORIC_CHUNKS
7930 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7934 putFile8Bit(file, EL_YAMYAM);
7935 putFile8Bit(file, level->num_yamyam_contents);
7936 putFile8Bit(file, 0);
7937 putFile8Bit(file, 0);
7939 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7940 for (y = 0; y < 3; y++)
7941 for (x = 0; x < 3; x++)
7942 if (level->encoding_16bit_field)
7943 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7945 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7949 #if ENABLE_HISTORIC_CHUNKS
7950 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7953 int num_contents, content_xsize, content_ysize;
7954 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7956 if (element == EL_YAMYAM)
7958 num_contents = level->num_yamyam_contents;
7962 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7963 for (y = 0; y < 3; y++)
7964 for (x = 0; x < 3; x++)
7965 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7967 else if (element == EL_BD_AMOEBA)
7973 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7974 for (y = 0; y < 3; y++)
7975 for (x = 0; x < 3; x++)
7976 content_array[i][x][y] = EL_EMPTY;
7977 content_array[0][0][0] = level->amoeba_content;
7981 // chunk header already written -- write empty chunk data
7982 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7984 Warn("cannot save content for element '%d'", element);
7989 putFile16BitBE(file, element);
7990 putFile8Bit(file, num_contents);
7991 putFile8Bit(file, content_xsize);
7992 putFile8Bit(file, content_ysize);
7994 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7996 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7997 for (y = 0; y < 3; y++)
7998 for (x = 0; x < 3; x++)
7999 putFile16BitBE(file, content_array[i][x][y]);
8003 #if ENABLE_HISTORIC_CHUNKS
8004 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8006 int envelope_nr = element - EL_ENVELOPE_1;
8007 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8011 chunk_size += putFile16BitBE(file, element);
8012 chunk_size += putFile16BitBE(file, envelope_len);
8013 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8014 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8016 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8017 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8019 for (i = 0; i < envelope_len; i++)
8020 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8026 #if ENABLE_HISTORIC_CHUNKS
8027 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8028 int num_changed_custom_elements)
8032 putFile16BitBE(file, num_changed_custom_elements);
8034 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8036 int element = EL_CUSTOM_START + i;
8038 struct ElementInfo *ei = &element_info[element];
8040 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8042 if (check < num_changed_custom_elements)
8044 putFile16BitBE(file, element);
8045 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8052 if (check != num_changed_custom_elements) // should not happen
8053 Warn("inconsistent number of custom element properties");
8057 #if ENABLE_HISTORIC_CHUNKS
8058 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8059 int num_changed_custom_elements)
8063 putFile16BitBE(file, num_changed_custom_elements);
8065 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8067 int element = EL_CUSTOM_START + i;
8069 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8071 if (check < num_changed_custom_elements)
8073 putFile16BitBE(file, element);
8074 putFile16BitBE(file, element_info[element].change->target_element);
8081 if (check != num_changed_custom_elements) // should not happen
8082 Warn("inconsistent number of custom target elements");
8086 #if ENABLE_HISTORIC_CHUNKS
8087 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8088 int num_changed_custom_elements)
8090 int i, j, x, y, check = 0;
8092 putFile16BitBE(file, num_changed_custom_elements);
8094 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8096 int element = EL_CUSTOM_START + i;
8097 struct ElementInfo *ei = &element_info[element];
8099 if (ei->modified_settings)
8101 if (check < num_changed_custom_elements)
8103 putFile16BitBE(file, element);
8105 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8106 putFile8Bit(file, ei->description[j]);
8108 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8110 // some free bytes for future properties and padding
8111 WriteUnusedBytesToFile(file, 7);
8113 putFile8Bit(file, ei->use_gfx_element);
8114 putFile16BitBE(file, ei->gfx_element_initial);
8116 putFile8Bit(file, ei->collect_score_initial);
8117 putFile8Bit(file, ei->collect_count_initial);
8119 putFile16BitBE(file, ei->push_delay_fixed);
8120 putFile16BitBE(file, ei->push_delay_random);
8121 putFile16BitBE(file, ei->move_delay_fixed);
8122 putFile16BitBE(file, ei->move_delay_random);
8124 putFile16BitBE(file, ei->move_pattern);
8125 putFile8Bit(file, ei->move_direction_initial);
8126 putFile8Bit(file, ei->move_stepsize);
8128 for (y = 0; y < 3; y++)
8129 for (x = 0; x < 3; x++)
8130 putFile16BitBE(file, ei->content.e[x][y]);
8132 putFile32BitBE(file, ei->change->events);
8134 putFile16BitBE(file, ei->change->target_element);
8136 putFile16BitBE(file, ei->change->delay_fixed);
8137 putFile16BitBE(file, ei->change->delay_random);
8138 putFile16BitBE(file, ei->change->delay_frames);
8140 putFile16BitBE(file, ei->change->initial_trigger_element);
8142 putFile8Bit(file, ei->change->explode);
8143 putFile8Bit(file, ei->change->use_target_content);
8144 putFile8Bit(file, ei->change->only_if_complete);
8145 putFile8Bit(file, ei->change->use_random_replace);
8147 putFile8Bit(file, ei->change->random_percentage);
8148 putFile8Bit(file, ei->change->replace_when);
8150 for (y = 0; y < 3; y++)
8151 for (x = 0; x < 3; x++)
8152 putFile16BitBE(file, ei->change->content.e[x][y]);
8154 putFile8Bit(file, ei->slippery_type);
8156 // some free bytes for future properties and padding
8157 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8164 if (check != num_changed_custom_elements) // should not happen
8165 Warn("inconsistent number of custom element properties");
8169 #if ENABLE_HISTORIC_CHUNKS
8170 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8172 struct ElementInfo *ei = &element_info[element];
8175 // ---------- custom element base property values (96 bytes) ----------------
8177 putFile16BitBE(file, element);
8179 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8180 putFile8Bit(file, ei->description[i]);
8182 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8184 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8186 putFile8Bit(file, ei->num_change_pages);
8188 putFile16BitBE(file, ei->ce_value_fixed_initial);
8189 putFile16BitBE(file, ei->ce_value_random_initial);
8190 putFile8Bit(file, ei->use_last_ce_value);
8192 putFile8Bit(file, ei->use_gfx_element);
8193 putFile16BitBE(file, ei->gfx_element_initial);
8195 putFile8Bit(file, ei->collect_score_initial);
8196 putFile8Bit(file, ei->collect_count_initial);
8198 putFile8Bit(file, ei->drop_delay_fixed);
8199 putFile8Bit(file, ei->push_delay_fixed);
8200 putFile8Bit(file, ei->drop_delay_random);
8201 putFile8Bit(file, ei->push_delay_random);
8202 putFile16BitBE(file, ei->move_delay_fixed);
8203 putFile16BitBE(file, ei->move_delay_random);
8205 // bits 0 - 15 of "move_pattern" ...
8206 putFile16BitBE(file, ei->move_pattern & 0xffff);
8207 putFile8Bit(file, ei->move_direction_initial);
8208 putFile8Bit(file, ei->move_stepsize);
8210 putFile8Bit(file, ei->slippery_type);
8212 for (y = 0; y < 3; y++)
8213 for (x = 0; x < 3; x++)
8214 putFile16BitBE(file, ei->content.e[x][y]);
8216 putFile16BitBE(file, ei->move_enter_element);
8217 putFile16BitBE(file, ei->move_leave_element);
8218 putFile8Bit(file, ei->move_leave_type);
8220 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8221 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8223 putFile8Bit(file, ei->access_direction);
8225 putFile8Bit(file, ei->explosion_delay);
8226 putFile8Bit(file, ei->ignition_delay);
8227 putFile8Bit(file, ei->explosion_type);
8229 // some free bytes for future custom property values and padding
8230 WriteUnusedBytesToFile(file, 1);
8232 // ---------- change page property values (48 bytes) ------------------------
8234 for (i = 0; i < ei->num_change_pages; i++)
8236 struct ElementChangeInfo *change = &ei->change_page[i];
8237 unsigned int event_bits;
8239 // bits 0 - 31 of "has_event[]" ...
8241 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8242 if (change->has_event[j])
8243 event_bits |= (1u << j);
8244 putFile32BitBE(file, event_bits);
8246 putFile16BitBE(file, change->target_element);
8248 putFile16BitBE(file, change->delay_fixed);
8249 putFile16BitBE(file, change->delay_random);
8250 putFile16BitBE(file, change->delay_frames);
8252 putFile16BitBE(file, change->initial_trigger_element);
8254 putFile8Bit(file, change->explode);
8255 putFile8Bit(file, change->use_target_content);
8256 putFile8Bit(file, change->only_if_complete);
8257 putFile8Bit(file, change->use_random_replace);
8259 putFile8Bit(file, change->random_percentage);
8260 putFile8Bit(file, change->replace_when);
8262 for (y = 0; y < 3; y++)
8263 for (x = 0; x < 3; x++)
8264 putFile16BitBE(file, change->target_content.e[x][y]);
8266 putFile8Bit(file, change->can_change);
8268 putFile8Bit(file, change->trigger_side);
8270 putFile8Bit(file, change->trigger_player);
8271 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8272 log_2(change->trigger_page)));
8274 putFile8Bit(file, change->has_action);
8275 putFile8Bit(file, change->action_type);
8276 putFile8Bit(file, change->action_mode);
8277 putFile16BitBE(file, change->action_arg);
8279 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8281 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8282 if (change->has_event[j])
8283 event_bits |= (1u << (j - 32));
8284 putFile8Bit(file, event_bits);
8289 #if ENABLE_HISTORIC_CHUNKS
8290 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8292 struct ElementInfo *ei = &element_info[element];
8293 struct ElementGroupInfo *group = ei->group;
8296 putFile16BitBE(file, element);
8298 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8299 putFile8Bit(file, ei->description[i]);
8301 putFile8Bit(file, group->num_elements);
8303 putFile8Bit(file, ei->use_gfx_element);
8304 putFile16BitBE(file, ei->gfx_element_initial);
8306 putFile8Bit(file, group->choice_mode);
8308 // some free bytes for future values and padding
8309 WriteUnusedBytesToFile(file, 3);
8311 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8312 putFile16BitBE(file, group->element[i]);
8316 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8317 boolean write_element)
8319 int save_type = entry->save_type;
8320 int data_type = entry->data_type;
8321 int conf_type = entry->conf_type;
8322 int byte_mask = conf_type & CONF_MASK_BYTES;
8323 int element = entry->element;
8324 int default_value = entry->default_value;
8326 boolean modified = FALSE;
8328 if (byte_mask != CONF_MASK_MULTI_BYTES)
8330 void *value_ptr = entry->value;
8331 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8334 // check if any settings have been modified before saving them
8335 if (value != default_value)
8338 // do not save if explicitly told or if unmodified default settings
8339 if ((save_type == SAVE_CONF_NEVER) ||
8340 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8344 num_bytes += putFile16BitBE(file, element);
8346 num_bytes += putFile8Bit(file, conf_type);
8347 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8348 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8349 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8352 else if (data_type == TYPE_STRING)
8354 char *default_string = entry->default_string;
8355 char *string = (char *)(entry->value);
8356 int string_length = strlen(string);
8359 // check if any settings have been modified before saving them
8360 if (!strEqual(string, default_string))
8363 // do not save if explicitly told or if unmodified default settings
8364 if ((save_type == SAVE_CONF_NEVER) ||
8365 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8369 num_bytes += putFile16BitBE(file, element);
8371 num_bytes += putFile8Bit(file, conf_type);
8372 num_bytes += putFile16BitBE(file, string_length);
8374 for (i = 0; i < string_length; i++)
8375 num_bytes += putFile8Bit(file, string[i]);
8377 else if (data_type == TYPE_ELEMENT_LIST)
8379 int *element_array = (int *)(entry->value);
8380 int num_elements = *(int *)(entry->num_entities);
8383 // check if any settings have been modified before saving them
8384 for (i = 0; i < num_elements; i++)
8385 if (element_array[i] != default_value)
8388 // do not save if explicitly told or if unmodified default settings
8389 if ((save_type == SAVE_CONF_NEVER) ||
8390 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8394 num_bytes += putFile16BitBE(file, element);
8396 num_bytes += putFile8Bit(file, conf_type);
8397 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8399 for (i = 0; i < num_elements; i++)
8400 num_bytes += putFile16BitBE(file, element_array[i]);
8402 else if (data_type == TYPE_CONTENT_LIST)
8404 struct Content *content = (struct Content *)(entry->value);
8405 int num_contents = *(int *)(entry->num_entities);
8408 // check if any settings have been modified before saving them
8409 for (i = 0; i < num_contents; i++)
8410 for (y = 0; y < 3; y++)
8411 for (x = 0; x < 3; x++)
8412 if (content[i].e[x][y] != default_value)
8415 // do not save if explicitly told or if unmodified default settings
8416 if ((save_type == SAVE_CONF_NEVER) ||
8417 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8421 num_bytes += putFile16BitBE(file, element);
8423 num_bytes += putFile8Bit(file, conf_type);
8424 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8426 for (i = 0; i < num_contents; i++)
8427 for (y = 0; y < 3; y++)
8428 for (x = 0; x < 3; x++)
8429 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8435 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8440 li = *level; // copy level data into temporary buffer
8442 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8443 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8448 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8453 li = *level; // copy level data into temporary buffer
8455 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8456 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8461 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8463 int envelope_nr = element - EL_ENVELOPE_1;
8467 chunk_size += putFile16BitBE(file, element);
8469 // copy envelope data into temporary buffer
8470 xx_envelope = level->envelope[envelope_nr];
8472 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8473 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8478 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8480 struct ElementInfo *ei = &element_info[element];
8484 chunk_size += putFile16BitBE(file, element);
8486 xx_ei = *ei; // copy element data into temporary buffer
8488 // set default description string for this specific element
8489 strcpy(xx_default_description, getDefaultElementDescription(ei));
8491 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8492 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8494 for (i = 0; i < ei->num_change_pages; i++)
8496 struct ElementChangeInfo *change = &ei->change_page[i];
8498 xx_current_change_page = i;
8500 xx_change = *change; // copy change data into temporary buffer
8503 setEventBitsFromEventFlags(change);
8505 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8506 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8513 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8515 struct ElementInfo *ei = &element_info[element];
8516 struct ElementGroupInfo *group = ei->group;
8520 chunk_size += putFile16BitBE(file, element);
8522 xx_ei = *ei; // copy element data into temporary buffer
8523 xx_group = *group; // copy group data into temporary buffer
8525 // set default description string for this specific element
8526 strcpy(xx_default_description, getDefaultElementDescription(ei));
8528 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8529 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8534 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8536 struct ElementInfo *ei = &element_info[element];
8540 chunk_size += putFile16BitBE(file, element);
8542 xx_ei = *ei; // copy element data into temporary buffer
8544 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8545 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8550 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8551 boolean save_as_template)
8557 if (!(file = fopen(filename, MODE_WRITE)))
8559 Warn("cannot save level file '%s'", filename);
8564 level->file_version = FILE_VERSION_ACTUAL;
8565 level->game_version = GAME_VERSION_ACTUAL;
8567 level->creation_date = getCurrentDate();
8569 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8570 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8572 chunk_size = SaveLevel_VERS(NULL, level);
8573 putFileChunkBE(file, "VERS", chunk_size);
8574 SaveLevel_VERS(file, level);
8576 chunk_size = SaveLevel_DATE(NULL, level);
8577 putFileChunkBE(file, "DATE", chunk_size);
8578 SaveLevel_DATE(file, level);
8580 chunk_size = SaveLevel_NAME(NULL, level);
8581 putFileChunkBE(file, "NAME", chunk_size);
8582 SaveLevel_NAME(file, level);
8584 chunk_size = SaveLevel_AUTH(NULL, level);
8585 putFileChunkBE(file, "AUTH", chunk_size);
8586 SaveLevel_AUTH(file, level);
8588 chunk_size = SaveLevel_INFO(NULL, level);
8589 putFileChunkBE(file, "INFO", chunk_size);
8590 SaveLevel_INFO(file, level);
8592 chunk_size = SaveLevel_BODY(NULL, level);
8593 putFileChunkBE(file, "BODY", chunk_size);
8594 SaveLevel_BODY(file, level);
8596 chunk_size = SaveLevel_ELEM(NULL, level);
8597 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8599 putFileChunkBE(file, "ELEM", chunk_size);
8600 SaveLevel_ELEM(file, level);
8603 for (i = 0; i < NUM_ENVELOPES; i++)
8605 int element = EL_ENVELOPE_1 + i;
8607 chunk_size = SaveLevel_NOTE(NULL, level, element);
8608 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8610 putFileChunkBE(file, "NOTE", chunk_size);
8611 SaveLevel_NOTE(file, level, element);
8615 // if not using template level, check for non-default custom/group elements
8616 if (!level->use_custom_template || save_as_template)
8618 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8620 int element = EL_CUSTOM_START + i;
8622 chunk_size = SaveLevel_CUSX(NULL, level, element);
8623 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8625 putFileChunkBE(file, "CUSX", chunk_size);
8626 SaveLevel_CUSX(file, level, element);
8630 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8632 int element = EL_GROUP_START + i;
8634 chunk_size = SaveLevel_GRPX(NULL, level, element);
8635 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8637 putFileChunkBE(file, "GRPX", chunk_size);
8638 SaveLevel_GRPX(file, level, element);
8642 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8644 int element = GET_EMPTY_ELEMENT(i);
8646 chunk_size = SaveLevel_EMPX(NULL, level, element);
8647 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8649 putFileChunkBE(file, "EMPX", chunk_size);
8650 SaveLevel_EMPX(file, level, element);
8657 SetFilePermissions(filename, PERMS_PRIVATE);
8660 void SaveLevel(int nr)
8662 char *filename = getDefaultLevelFilename(nr);
8664 SaveLevelFromFilename(&level, filename, FALSE);
8667 void SaveLevelTemplate(void)
8669 char *filename = getLocalLevelTemplateFilename();
8671 SaveLevelFromFilename(&level, filename, TRUE);
8674 boolean SaveLevelChecked(int nr)
8676 char *filename = getDefaultLevelFilename(nr);
8677 boolean new_level = !fileExists(filename);
8678 boolean level_saved = FALSE;
8680 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8685 Request("Level saved!", REQ_CONFIRM);
8693 void DumpLevel(struct LevelInfo *level)
8695 if (level->no_level_file || level->no_valid_file)
8697 Warn("cannot dump -- no valid level file found");
8703 Print("Level xxx (file version %08d, game version %08d)\n",
8704 level->file_version, level->game_version);
8707 Print("Level author: '%s'\n", level->author);
8708 Print("Level title: '%s'\n", level->name);
8710 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8712 Print("Level time: %d seconds\n", level->time);
8713 Print("Gems needed: %d\n", level->gems_needed);
8715 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8716 Print("Time for wheel: %d seconds\n", level->time_wheel);
8717 Print("Time for light: %d seconds\n", level->time_light);
8718 Print("Time for timegate: %d seconds\n", level->time_timegate);
8720 Print("Amoeba speed: %d\n", level->amoeba_speed);
8723 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8724 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8725 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8726 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8727 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8728 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8734 for (i = 0; i < NUM_ENVELOPES; i++)
8736 char *text = level->envelope[i].text;
8737 int text_len = strlen(text);
8738 boolean has_text = FALSE;
8740 for (j = 0; j < text_len; j++)
8741 if (text[j] != ' ' && text[j] != '\n')
8747 Print("Envelope %d:\n'%s'\n", i + 1, text);
8755 void DumpLevels(void)
8757 static LevelDirTree *dumplevel_leveldir = NULL;
8759 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8760 global.dumplevel_leveldir);
8762 if (dumplevel_leveldir == NULL)
8763 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8765 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8766 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8767 Fail("no such level number: %d", global.dumplevel_level_nr);
8769 leveldir_current = dumplevel_leveldir;
8771 LoadLevel(global.dumplevel_level_nr);
8778 // ============================================================================
8779 // tape file functions
8780 // ============================================================================
8782 static void setTapeInfoToDefaults(void)
8786 // always start with reliable default values (empty tape)
8789 // default values (also for pre-1.2 tapes) with only the first player
8790 tape.player_participates[0] = TRUE;
8791 for (i = 1; i < MAX_PLAYERS; i++)
8792 tape.player_participates[i] = FALSE;
8794 // at least one (default: the first) player participates in every tape
8795 tape.num_participating_players = 1;
8797 tape.property_bits = TAPE_PROPERTY_NONE;
8799 tape.level_nr = level_nr;
8801 tape.changed = FALSE;
8802 tape.solved = FALSE;
8804 tape.recording = FALSE;
8805 tape.playing = FALSE;
8806 tape.pausing = FALSE;
8808 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8809 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8811 tape.no_info_chunk = TRUE;
8812 tape.no_valid_file = FALSE;
8815 static int getTapePosSize(struct TapeInfo *tape)
8817 int tape_pos_size = 0;
8819 if (tape->use_key_actions)
8820 tape_pos_size += tape->num_participating_players;
8822 if (tape->use_mouse_actions)
8823 tape_pos_size += 3; // x and y position and mouse button mask
8825 tape_pos_size += 1; // tape action delay value
8827 return tape_pos_size;
8830 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8832 tape->use_key_actions = FALSE;
8833 tape->use_mouse_actions = FALSE;
8835 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8836 tape->use_key_actions = TRUE;
8838 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8839 tape->use_mouse_actions = TRUE;
8842 static int getTapeActionValue(struct TapeInfo *tape)
8844 return (tape->use_key_actions &&
8845 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8846 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8847 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8848 TAPE_ACTIONS_DEFAULT);
8851 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8853 tape->file_version = getFileVersion(file);
8854 tape->game_version = getFileVersion(file);
8859 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8863 tape->random_seed = getFile32BitBE(file);
8864 tape->date = getFile32BitBE(file);
8865 tape->length = getFile32BitBE(file);
8867 // read header fields that are new since version 1.2
8868 if (tape->file_version >= FILE_VERSION_1_2)
8870 byte store_participating_players = getFile8Bit(file);
8873 // since version 1.2, tapes store which players participate in the tape
8874 tape->num_participating_players = 0;
8875 for (i = 0; i < MAX_PLAYERS; i++)
8877 tape->player_participates[i] = FALSE;
8879 if (store_participating_players & (1 << i))
8881 tape->player_participates[i] = TRUE;
8882 tape->num_participating_players++;
8886 setTapeActionFlags(tape, getFile8Bit(file));
8888 tape->property_bits = getFile8Bit(file);
8889 tape->solved = getFile8Bit(file);
8891 engine_version = getFileVersion(file);
8892 if (engine_version > 0)
8893 tape->engine_version = engine_version;
8895 tape->engine_version = tape->game_version;
8901 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8903 tape->scr_fieldx = getFile8Bit(file);
8904 tape->scr_fieldy = getFile8Bit(file);
8909 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8911 char *level_identifier = NULL;
8912 int level_identifier_size;
8915 tape->no_info_chunk = FALSE;
8917 level_identifier_size = getFile16BitBE(file);
8919 level_identifier = checked_malloc(level_identifier_size);
8921 for (i = 0; i < level_identifier_size; i++)
8922 level_identifier[i] = getFile8Bit(file);
8924 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8925 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8927 checked_free(level_identifier);
8929 tape->level_nr = getFile16BitBE(file);
8931 chunk_size = 2 + level_identifier_size + 2;
8936 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8939 int tape_pos_size = getTapePosSize(tape);
8940 int chunk_size_expected = tape_pos_size * tape->length;
8942 if (chunk_size_expected != chunk_size)
8944 ReadUnusedBytesFromFile(file, chunk_size);
8945 return chunk_size_expected;
8948 for (i = 0; i < tape->length; i++)
8950 if (i >= MAX_TAPE_LEN)
8952 Warn("tape truncated -- size exceeds maximum tape size %d",
8955 // tape too large; read and ignore remaining tape data from this chunk
8956 for (;i < tape->length; i++)
8957 ReadUnusedBytesFromFile(file, tape_pos_size);
8962 if (tape->use_key_actions)
8964 for (j = 0; j < MAX_PLAYERS; j++)
8966 tape->pos[i].action[j] = MV_NONE;
8968 if (tape->player_participates[j])
8969 tape->pos[i].action[j] = getFile8Bit(file);
8973 if (tape->use_mouse_actions)
8975 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8976 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8977 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8980 tape->pos[i].delay = getFile8Bit(file);
8982 if (tape->file_version == FILE_VERSION_1_0)
8984 // eliminate possible diagonal moves in old tapes
8985 // this is only for backward compatibility
8987 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8988 byte action = tape->pos[i].action[0];
8989 int k, num_moves = 0;
8991 for (k = 0; k < 4; k++)
8993 if (action & joy_dir[k])
8995 tape->pos[i + num_moves].action[0] = joy_dir[k];
8997 tape->pos[i + num_moves].delay = 0;
9006 tape->length += num_moves;
9009 else if (tape->file_version < FILE_VERSION_2_0)
9011 // convert pre-2.0 tapes to new tape format
9013 if (tape->pos[i].delay > 1)
9016 tape->pos[i + 1] = tape->pos[i];
9017 tape->pos[i + 1].delay = 1;
9020 for (j = 0; j < MAX_PLAYERS; j++)
9021 tape->pos[i].action[j] = MV_NONE;
9022 tape->pos[i].delay--;
9029 if (checkEndOfFile(file))
9033 if (i != tape->length)
9034 chunk_size = tape_pos_size * i;
9039 static void LoadTape_SokobanSolution(char *filename)
9042 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9044 if (!(file = openFile(filename, MODE_READ)))
9046 tape.no_valid_file = TRUE;
9051 while (!checkEndOfFile(file))
9053 unsigned char c = getByteFromFile(file);
9055 if (checkEndOfFile(file))
9062 tape.pos[tape.length].action[0] = MV_UP;
9063 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9069 tape.pos[tape.length].action[0] = MV_DOWN;
9070 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9076 tape.pos[tape.length].action[0] = MV_LEFT;
9077 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9083 tape.pos[tape.length].action[0] = MV_RIGHT;
9084 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9092 // ignore white-space characters
9096 tape.no_valid_file = TRUE;
9098 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9106 if (tape.no_valid_file)
9109 tape.length_frames = GetTapeLengthFrames();
9110 tape.length_seconds = GetTapeLengthSeconds();
9113 void LoadTapeFromFilename(char *filename)
9115 char cookie[MAX_LINE_LEN];
9116 char chunk_name[CHUNK_ID_LEN + 1];
9120 // always start with reliable default values
9121 setTapeInfoToDefaults();
9123 if (strSuffix(filename, ".sln"))
9125 LoadTape_SokobanSolution(filename);
9130 if (!(file = openFile(filename, MODE_READ)))
9132 tape.no_valid_file = TRUE;
9137 getFileChunkBE(file, chunk_name, NULL);
9138 if (strEqual(chunk_name, "RND1"))
9140 getFile32BitBE(file); // not used
9142 getFileChunkBE(file, chunk_name, NULL);
9143 if (!strEqual(chunk_name, "TAPE"))
9145 tape.no_valid_file = TRUE;
9147 Warn("unknown format of tape file '%s'", filename);
9154 else // check for pre-2.0 file format with cookie string
9156 strcpy(cookie, chunk_name);
9157 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9159 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9160 cookie[strlen(cookie) - 1] = '\0';
9162 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9164 tape.no_valid_file = TRUE;
9166 Warn("unknown format of tape file '%s'", filename);
9173 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9175 tape.no_valid_file = TRUE;
9177 Warn("unsupported version of tape file '%s'", filename);
9184 // pre-2.0 tape files have no game version, so use file version here
9185 tape.game_version = tape.file_version;
9188 if (tape.file_version < FILE_VERSION_1_2)
9190 // tape files from versions before 1.2.0 without chunk structure
9191 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9192 LoadTape_BODY(file, 2 * tape.length, &tape);
9200 int (*loader)(File *, int, struct TapeInfo *);
9204 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9205 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9206 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9207 { "INFO", -1, LoadTape_INFO },
9208 { "BODY", -1, LoadTape_BODY },
9212 while (getFileChunkBE(file, chunk_name, &chunk_size))
9216 while (chunk_info[i].name != NULL &&
9217 !strEqual(chunk_name, chunk_info[i].name))
9220 if (chunk_info[i].name == NULL)
9222 Warn("unknown chunk '%s' in tape file '%s'",
9223 chunk_name, filename);
9225 ReadUnusedBytesFromFile(file, chunk_size);
9227 else if (chunk_info[i].size != -1 &&
9228 chunk_info[i].size != chunk_size)
9230 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9231 chunk_size, chunk_name, filename);
9233 ReadUnusedBytesFromFile(file, chunk_size);
9237 // call function to load this tape chunk
9238 int chunk_size_expected =
9239 (chunk_info[i].loader)(file, chunk_size, &tape);
9241 // the size of some chunks cannot be checked before reading other
9242 // chunks first (like "HEAD" and "BODY") that contain some header
9243 // information, so check them here
9244 if (chunk_size_expected != chunk_size)
9246 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9247 chunk_size, chunk_name, filename);
9255 tape.length_frames = GetTapeLengthFrames();
9256 tape.length_seconds = GetTapeLengthSeconds();
9259 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9261 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9263 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9264 tape.engine_version);
9268 void LoadTape(int nr)
9270 char *filename = getTapeFilename(nr);
9272 LoadTapeFromFilename(filename);
9275 void LoadSolutionTape(int nr)
9277 char *filename = getSolutionTapeFilename(nr);
9279 LoadTapeFromFilename(filename);
9281 if (TAPE_IS_EMPTY(tape))
9283 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9284 level.native_bd_level->replay != NULL)
9285 CopyNativeTape_BD_to_RND(&level);
9286 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9287 level.native_sp_level->demo.is_available)
9288 CopyNativeTape_SP_to_RND(&level);
9292 void LoadScoreTape(char *score_tape_basename, int nr)
9294 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9296 LoadTapeFromFilename(filename);
9299 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9301 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9303 LoadTapeFromFilename(filename);
9306 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9308 // chunk required for team mode tapes with non-default screen size
9309 return (tape->num_participating_players > 1 &&
9310 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9311 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9314 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9316 putFileVersion(file, tape->file_version);
9317 putFileVersion(file, tape->game_version);
9320 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9323 byte store_participating_players = 0;
9325 // set bits for participating players for compact storage
9326 for (i = 0; i < MAX_PLAYERS; i++)
9327 if (tape->player_participates[i])
9328 store_participating_players |= (1 << i);
9330 putFile32BitBE(file, tape->random_seed);
9331 putFile32BitBE(file, tape->date);
9332 putFile32BitBE(file, tape->length);
9334 putFile8Bit(file, store_participating_players);
9336 putFile8Bit(file, getTapeActionValue(tape));
9338 putFile8Bit(file, tape->property_bits);
9339 putFile8Bit(file, tape->solved);
9341 putFileVersion(file, tape->engine_version);
9344 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9346 putFile8Bit(file, tape->scr_fieldx);
9347 putFile8Bit(file, tape->scr_fieldy);
9350 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9352 int level_identifier_size = strlen(tape->level_identifier) + 1;
9355 putFile16BitBE(file, level_identifier_size);
9357 for (i = 0; i < level_identifier_size; i++)
9358 putFile8Bit(file, tape->level_identifier[i]);
9360 putFile16BitBE(file, tape->level_nr);
9363 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9367 for (i = 0; i < tape->length; i++)
9369 if (tape->use_key_actions)
9371 for (j = 0; j < MAX_PLAYERS; j++)
9372 if (tape->player_participates[j])
9373 putFile8Bit(file, tape->pos[i].action[j]);
9376 if (tape->use_mouse_actions)
9378 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9379 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9380 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9383 putFile8Bit(file, tape->pos[i].delay);
9387 void SaveTapeToFilename(char *filename)
9391 int info_chunk_size;
9392 int body_chunk_size;
9394 if (!(file = fopen(filename, MODE_WRITE)))
9396 Warn("cannot save level recording file '%s'", filename);
9401 tape_pos_size = getTapePosSize(&tape);
9403 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9404 body_chunk_size = tape_pos_size * tape.length;
9406 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9407 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9409 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9410 SaveTape_VERS(file, &tape);
9412 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9413 SaveTape_HEAD(file, &tape);
9415 if (checkSaveTape_SCRN(&tape))
9417 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9418 SaveTape_SCRN(file, &tape);
9421 putFileChunkBE(file, "INFO", info_chunk_size);
9422 SaveTape_INFO(file, &tape);
9424 putFileChunkBE(file, "BODY", body_chunk_size);
9425 SaveTape_BODY(file, &tape);
9429 SetFilePermissions(filename, PERMS_PRIVATE);
9432 static void SaveTapeExt(char *filename)
9436 tape.file_version = FILE_VERSION_ACTUAL;
9437 tape.game_version = GAME_VERSION_ACTUAL;
9439 tape.num_participating_players = 0;
9441 // count number of participating players
9442 for (i = 0; i < MAX_PLAYERS; i++)
9443 if (tape.player_participates[i])
9444 tape.num_participating_players++;
9446 SaveTapeToFilename(filename);
9448 tape.changed = FALSE;
9451 void SaveTape(int nr)
9453 char *filename = getTapeFilename(nr);
9455 InitTapeDirectory(leveldir_current->subdir);
9457 SaveTapeExt(filename);
9460 void SaveScoreTape(int nr)
9462 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9464 // used instead of "leveldir_current->subdir" (for network games)
9465 InitScoreTapeDirectory(levelset.identifier, nr);
9467 SaveTapeExt(filename);
9470 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9471 unsigned int req_state_added)
9473 char *filename = getTapeFilename(nr);
9474 boolean new_tape = !fileExists(filename);
9475 boolean tape_saved = FALSE;
9477 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9482 Request(msg_saved, REQ_CONFIRM | req_state_added);
9490 boolean SaveTapeChecked(int nr)
9492 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9495 boolean SaveTapeChecked_LevelSolved(int nr)
9497 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9498 "Level solved! Tape saved!", REQ_STAY_OPEN);
9501 void DumpTape(struct TapeInfo *tape)
9503 int tape_frame_counter;
9506 if (tape->no_valid_file)
9508 Warn("cannot dump -- no valid tape file found");
9515 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9516 tape->level_nr, tape->file_version, tape->game_version);
9517 Print(" (effective engine version %08d)\n",
9518 tape->engine_version);
9519 Print("Level series identifier: '%s'\n", tape->level_identifier);
9521 Print("Solution tape: %s\n",
9522 tape->solved ? "yes" :
9523 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9525 Print("Special tape properties: ");
9526 if (tape->property_bits == TAPE_PROPERTY_NONE)
9528 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9529 Print("[em_random_bug]");
9530 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9531 Print("[game_speed]");
9532 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9534 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9535 Print("[single_step]");
9536 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9537 Print("[snapshot]");
9538 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9539 Print("[replayed]");
9540 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9541 Print("[tas_keys]");
9542 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9543 Print("[small_graphics]");
9546 int year2 = tape->date / 10000;
9547 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9548 int month_index_raw = (tape->date / 100) % 100;
9549 int month_index = month_index_raw % 12; // prevent invalid index
9550 int month = month_index + 1;
9551 int day = tape->date % 100;
9553 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9557 tape_frame_counter = 0;
9559 for (i = 0; i < tape->length; i++)
9561 if (i >= MAX_TAPE_LEN)
9566 for (j = 0; j < MAX_PLAYERS; j++)
9568 if (tape->player_participates[j])
9570 int action = tape->pos[i].action[j];
9572 Print("%d:%02x ", j, action);
9573 Print("[%c%c%c%c|%c%c] - ",
9574 (action & JOY_LEFT ? '<' : ' '),
9575 (action & JOY_RIGHT ? '>' : ' '),
9576 (action & JOY_UP ? '^' : ' '),
9577 (action & JOY_DOWN ? 'v' : ' '),
9578 (action & JOY_BUTTON_1 ? '1' : ' '),
9579 (action & JOY_BUTTON_2 ? '2' : ' '));
9583 Print("(%03d) ", tape->pos[i].delay);
9584 Print("[%05d]\n", tape_frame_counter);
9586 tape_frame_counter += tape->pos[i].delay;
9592 void DumpTapes(void)
9594 static LevelDirTree *dumptape_leveldir = NULL;
9596 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9597 global.dumptape_leveldir);
9599 if (dumptape_leveldir == NULL)
9600 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9602 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9603 global.dumptape_level_nr > dumptape_leveldir->last_level)
9604 Fail("no such level number: %d", global.dumptape_level_nr);
9606 leveldir_current = dumptape_leveldir;
9608 if (options.mytapes)
9609 LoadTape(global.dumptape_level_nr);
9611 LoadSolutionTape(global.dumptape_level_nr);
9619 // ============================================================================
9620 // score file functions
9621 // ============================================================================
9623 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9627 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9629 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9630 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9631 scores->entry[i].score = 0;
9632 scores->entry[i].time = 0;
9634 scores->entry[i].id = -1;
9635 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9636 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9637 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9638 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9639 strcpy(scores->entry[i].country_code, "??");
9642 scores->num_entries = 0;
9643 scores->last_added = -1;
9644 scores->last_added_local = -1;
9646 scores->updated = FALSE;
9647 scores->uploaded = FALSE;
9648 scores->tape_downloaded = FALSE;
9649 scores->force_last_added = FALSE;
9651 // The following values are intentionally not reset here:
9655 // - continue_playing
9656 // - continue_on_return
9659 static void setScoreInfoToDefaults(void)
9661 setScoreInfoToDefaultsExt(&scores);
9664 static void setServerScoreInfoToDefaults(void)
9666 setScoreInfoToDefaultsExt(&server_scores);
9669 static void LoadScore_OLD(int nr)
9672 char *filename = getScoreFilename(nr);
9673 char cookie[MAX_LINE_LEN];
9674 char line[MAX_LINE_LEN];
9678 if (!(file = fopen(filename, MODE_READ)))
9681 // check file identifier
9682 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9684 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9685 cookie[strlen(cookie) - 1] = '\0';
9687 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9689 Warn("unknown format of score file '%s'", filename);
9696 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9698 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9699 Warn("fscanf() failed; %s", strerror(errno));
9701 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9704 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9705 line[strlen(line) - 1] = '\0';
9707 for (line_ptr = line; *line_ptr; line_ptr++)
9709 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9711 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9712 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9721 static void ConvertScore_OLD(void)
9723 // only convert score to time for levels that rate playing time over score
9724 if (!level.rate_time_over_score)
9727 // convert old score to playing time for score-less levels (like Supaplex)
9728 int time_final_max = 999;
9731 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9733 int score = scores.entry[i].score;
9735 if (score > 0 && score < time_final_max)
9736 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9740 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9742 scores->file_version = getFileVersion(file);
9743 scores->game_version = getFileVersion(file);
9748 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9750 char *level_identifier = NULL;
9751 int level_identifier_size;
9754 level_identifier_size = getFile16BitBE(file);
9756 level_identifier = checked_malloc(level_identifier_size);
9758 for (i = 0; i < level_identifier_size; i++)
9759 level_identifier[i] = getFile8Bit(file);
9761 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9762 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9764 checked_free(level_identifier);
9766 scores->level_nr = getFile16BitBE(file);
9767 scores->num_entries = getFile16BitBE(file);
9769 chunk_size = 2 + level_identifier_size + 2 + 2;
9774 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9778 for (i = 0; i < scores->num_entries; i++)
9780 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9781 scores->entry[i].name[j] = getFile8Bit(file);
9783 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9786 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9791 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9795 for (i = 0; i < scores->num_entries; i++)
9796 scores->entry[i].score = getFile16BitBE(file);
9798 chunk_size = scores->num_entries * 2;
9803 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9807 for (i = 0; i < scores->num_entries; i++)
9808 scores->entry[i].score = getFile32BitBE(file);
9810 chunk_size = scores->num_entries * 4;
9815 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9819 for (i = 0; i < scores->num_entries; i++)
9820 scores->entry[i].time = getFile32BitBE(file);
9822 chunk_size = scores->num_entries * 4;
9827 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9831 for (i = 0; i < scores->num_entries; i++)
9833 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9834 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9836 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9839 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9844 void LoadScore(int nr)
9846 char *filename = getScoreFilename(nr);
9847 char cookie[MAX_LINE_LEN];
9848 char chunk_name[CHUNK_ID_LEN + 1];
9850 boolean old_score_file_format = FALSE;
9853 // always start with reliable default values
9854 setScoreInfoToDefaults();
9856 if (!(file = openFile(filename, MODE_READ)))
9859 getFileChunkBE(file, chunk_name, NULL);
9860 if (strEqual(chunk_name, "RND1"))
9862 getFile32BitBE(file); // not used
9864 getFileChunkBE(file, chunk_name, NULL);
9865 if (!strEqual(chunk_name, "SCOR"))
9867 Warn("unknown format of score file '%s'", filename);
9874 else // check for old file format with cookie string
9876 strcpy(cookie, chunk_name);
9877 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9879 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9880 cookie[strlen(cookie) - 1] = '\0';
9882 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9884 Warn("unknown format of score file '%s'", filename);
9891 old_score_file_format = TRUE;
9894 if (old_score_file_format)
9896 // score files from versions before 4.2.4.0 without chunk structure
9899 // convert score to time, if possible (mainly for Supaplex levels)
9908 int (*loader)(File *, int, struct ScoreInfo *);
9912 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9913 { "INFO", -1, LoadScore_INFO },
9914 { "NAME", -1, LoadScore_NAME },
9915 { "SCOR", -1, LoadScore_SCOR },
9916 { "SC4R", -1, LoadScore_SC4R },
9917 { "TIME", -1, LoadScore_TIME },
9918 { "TAPE", -1, LoadScore_TAPE },
9923 while (getFileChunkBE(file, chunk_name, &chunk_size))
9927 while (chunk_info[i].name != NULL &&
9928 !strEqual(chunk_name, chunk_info[i].name))
9931 if (chunk_info[i].name == NULL)
9933 Warn("unknown chunk '%s' in score file '%s'",
9934 chunk_name, filename);
9936 ReadUnusedBytesFromFile(file, chunk_size);
9938 else if (chunk_info[i].size != -1 &&
9939 chunk_info[i].size != chunk_size)
9941 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9942 chunk_size, chunk_name, filename);
9944 ReadUnusedBytesFromFile(file, chunk_size);
9948 // call function to load this score chunk
9949 int chunk_size_expected =
9950 (chunk_info[i].loader)(file, chunk_size, &scores);
9952 // the size of some chunks cannot be checked before reading other
9953 // chunks first (like "HEAD" and "BODY") that contain some header
9954 // information, so check them here
9955 if (chunk_size_expected != chunk_size)
9957 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9958 chunk_size, chunk_name, filename);
9967 #if ENABLE_HISTORIC_CHUNKS
9968 void SaveScore_OLD(int nr)
9971 char *filename = getScoreFilename(nr);
9974 // used instead of "leveldir_current->subdir" (for network games)
9975 InitScoreDirectory(levelset.identifier);
9977 if (!(file = fopen(filename, MODE_WRITE)))
9979 Warn("cannot save score for level %d", nr);
9984 fprintf(file, "%s\n\n", SCORE_COOKIE);
9986 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9987 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9991 SetFilePermissions(filename, PERMS_PRIVATE);
9995 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9997 putFileVersion(file, scores->file_version);
9998 putFileVersion(file, scores->game_version);
10001 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10003 int level_identifier_size = strlen(scores->level_identifier) + 1;
10006 putFile16BitBE(file, level_identifier_size);
10008 for (i = 0; i < level_identifier_size; i++)
10009 putFile8Bit(file, scores->level_identifier[i]);
10011 putFile16BitBE(file, scores->level_nr);
10012 putFile16BitBE(file, scores->num_entries);
10015 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10019 for (i = 0; i < scores->num_entries; i++)
10021 int name_size = strlen(scores->entry[i].name);
10023 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10024 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10028 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10032 for (i = 0; i < scores->num_entries; i++)
10033 putFile16BitBE(file, scores->entry[i].score);
10036 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10040 for (i = 0; i < scores->num_entries; i++)
10041 putFile32BitBE(file, scores->entry[i].score);
10044 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10048 for (i = 0; i < scores->num_entries; i++)
10049 putFile32BitBE(file, scores->entry[i].time);
10052 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10056 for (i = 0; i < scores->num_entries; i++)
10058 int size = strlen(scores->entry[i].tape_basename);
10060 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10061 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10065 static void SaveScoreToFilename(char *filename)
10068 int info_chunk_size;
10069 int name_chunk_size;
10070 int scor_chunk_size;
10071 int sc4r_chunk_size;
10072 int time_chunk_size;
10073 int tape_chunk_size;
10074 boolean has_large_score_values;
10077 if (!(file = fopen(filename, MODE_WRITE)))
10079 Warn("cannot save score file '%s'", filename);
10084 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10085 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10086 scor_chunk_size = scores.num_entries * 2;
10087 sc4r_chunk_size = scores.num_entries * 4;
10088 time_chunk_size = scores.num_entries * 4;
10089 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10091 has_large_score_values = FALSE;
10092 for (i = 0; i < scores.num_entries; i++)
10093 if (scores.entry[i].score > 0xffff)
10094 has_large_score_values = TRUE;
10096 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10097 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10099 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10100 SaveScore_VERS(file, &scores);
10102 putFileChunkBE(file, "INFO", info_chunk_size);
10103 SaveScore_INFO(file, &scores);
10105 putFileChunkBE(file, "NAME", name_chunk_size);
10106 SaveScore_NAME(file, &scores);
10108 if (has_large_score_values)
10110 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10111 SaveScore_SC4R(file, &scores);
10115 putFileChunkBE(file, "SCOR", scor_chunk_size);
10116 SaveScore_SCOR(file, &scores);
10119 putFileChunkBE(file, "TIME", time_chunk_size);
10120 SaveScore_TIME(file, &scores);
10122 putFileChunkBE(file, "TAPE", tape_chunk_size);
10123 SaveScore_TAPE(file, &scores);
10127 SetFilePermissions(filename, PERMS_PRIVATE);
10130 void SaveScore(int nr)
10132 char *filename = getScoreFilename(nr);
10135 // used instead of "leveldir_current->subdir" (for network games)
10136 InitScoreDirectory(levelset.identifier);
10138 scores.file_version = FILE_VERSION_ACTUAL;
10139 scores.game_version = GAME_VERSION_ACTUAL;
10141 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10142 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10143 scores.level_nr = level_nr;
10145 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10146 if (scores.entry[i].score == 0 &&
10147 scores.entry[i].time == 0 &&
10148 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10151 scores.num_entries = i;
10153 if (scores.num_entries == 0)
10156 SaveScoreToFilename(filename);
10159 static void LoadServerScoreFromCache(int nr)
10161 struct ScoreEntry score_entry;
10170 { &score_entry.score, FALSE, 0 },
10171 { &score_entry.time, FALSE, 0 },
10172 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10173 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10174 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10175 { &score_entry.id, FALSE, 0 },
10176 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10177 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10178 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10179 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10183 char *filename = getScoreCacheFilename(nr);
10184 SetupFileHash *score_hash = loadSetupFileHash(filename);
10187 server_scores.num_entries = 0;
10189 if (score_hash == NULL)
10192 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10194 score_entry = server_scores.entry[i];
10196 for (j = 0; score_mapping[j].value != NULL; j++)
10200 sprintf(token, "%02d.%d", i, j);
10202 char *value = getHashEntry(score_hash, token);
10207 if (score_mapping[j].is_string)
10209 char *score_value = (char *)score_mapping[j].value;
10210 int value_size = score_mapping[j].string_size;
10212 strncpy(score_value, value, value_size);
10213 score_value[value_size] = '\0';
10217 int *score_value = (int *)score_mapping[j].value;
10219 *score_value = atoi(value);
10222 server_scores.num_entries = i + 1;
10225 server_scores.entry[i] = score_entry;
10228 freeSetupFileHash(score_hash);
10231 void LoadServerScore(int nr, boolean download_score)
10233 if (!setup.use_api_server)
10236 // always start with reliable default values
10237 setServerScoreInfoToDefaults();
10239 // 1st step: load server scores from cache file (which may not exist)
10240 // (this should prevent reading it while the thread is writing to it)
10241 LoadServerScoreFromCache(nr);
10243 if (download_score && runtime.use_api_server)
10245 // 2nd step: download server scores from score server to cache file
10246 // (as thread, as it might time out if the server is not reachable)
10247 ApiGetScoreAsThread(nr);
10251 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10253 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10255 // if score tape not uploaded, ask for uploading missing tapes later
10256 if (!setup.has_remaining_tapes)
10257 setup.ask_for_remaining_tapes = TRUE;
10259 setup.provide_uploading_tapes = TRUE;
10260 setup.has_remaining_tapes = TRUE;
10262 SaveSetup_ServerSetup();
10265 void SaveServerScore(int nr, boolean tape_saved)
10267 if (!runtime.use_api_server)
10269 PrepareScoreTapesForUpload(leveldir_current->subdir);
10274 ApiAddScoreAsThread(nr, tape_saved, NULL);
10277 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10278 char *score_tape_filename)
10280 if (!runtime.use_api_server)
10283 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10286 void LoadLocalAndServerScore(int nr, boolean download_score)
10288 int last_added_local = scores.last_added_local;
10289 boolean force_last_added = scores.force_last_added;
10291 // needed if only showing server scores
10292 setScoreInfoToDefaults();
10294 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10297 // restore last added local score entry (before merging server scores)
10298 scores.last_added = scores.last_added_local = last_added_local;
10300 if (setup.use_api_server &&
10301 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10303 // load server scores from cache file and trigger update from server
10304 LoadServerScore(nr, download_score);
10306 // merge local scores with scores from server
10307 MergeServerScore();
10310 if (force_last_added)
10311 scores.force_last_added = force_last_added;
10315 // ============================================================================
10316 // setup file functions
10317 // ============================================================================
10319 #define TOKEN_STR_PLAYER_PREFIX "player_"
10322 static struct TokenInfo global_setup_tokens[] =
10326 &setup.player_name, "player_name"
10330 &setup.multiple_users, "multiple_users"
10334 &setup.sound, "sound"
10338 &setup.sound_loops, "repeating_sound_loops"
10342 &setup.sound_music, "background_music"
10346 &setup.sound_simple, "simple_sound_effects"
10350 &setup.toons, "toons"
10354 &setup.global_animations, "global_animations"
10358 &setup.scroll_delay, "scroll_delay"
10362 &setup.forced_scroll_delay, "forced_scroll_delay"
10366 &setup.scroll_delay_value, "scroll_delay_value"
10370 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10374 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10378 &setup.fade_screens, "fade_screens"
10382 &setup.autorecord, "automatic_tape_recording"
10386 &setup.autorecord_after_replay, "autorecord_after_replay"
10390 &setup.auto_pause_on_start, "auto_pause_on_start"
10394 &setup.show_titlescreen, "show_titlescreen"
10398 &setup.quick_doors, "quick_doors"
10402 &setup.team_mode, "team_mode"
10406 &setup.handicap, "handicap"
10410 &setup.skip_levels, "skip_levels"
10414 &setup.increment_levels, "increment_levels"
10418 &setup.auto_play_next_level, "auto_play_next_level"
10422 &setup.count_score_after_game, "count_score_after_game"
10426 &setup.show_scores_after_game, "show_scores_after_game"
10430 &setup.time_limit, "time_limit"
10434 &setup.fullscreen, "fullscreen"
10438 &setup.window_scaling_percent, "window_scaling_percent"
10442 &setup.window_scaling_quality, "window_scaling_quality"
10446 &setup.screen_rendering_mode, "screen_rendering_mode"
10450 &setup.vsync_mode, "vsync_mode"
10454 &setup.ask_on_escape, "ask_on_escape"
10458 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10462 &setup.ask_on_game_over, "ask_on_game_over"
10466 &setup.ask_on_quit_game, "ask_on_quit_game"
10470 &setup.ask_on_quit_program, "ask_on_quit_program"
10474 &setup.quick_switch, "quick_player_switch"
10478 &setup.input_on_focus, "input_on_focus"
10482 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10486 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10490 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10494 &setup.game_speed_extended, "game_speed_extended"
10498 &setup.game_frame_delay, "game_frame_delay"
10502 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10506 &setup.bd_skip_hatching, "bd_skip_hatching"
10510 &setup.bd_scroll_delay, "bd_scroll_delay"
10514 &setup.bd_smooth_movements, "bd_smooth_movements"
10518 &setup.sp_show_border_elements, "sp_show_border_elements"
10522 &setup.small_game_graphics, "small_game_graphics"
10526 &setup.show_load_save_buttons, "show_load_save_buttons"
10530 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10534 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10538 &setup.graphics_set, "graphics_set"
10542 &setup.sounds_set, "sounds_set"
10546 &setup.music_set, "music_set"
10550 &setup.override_level_graphics, "override_level_graphics"
10554 &setup.override_level_sounds, "override_level_sounds"
10558 &setup.override_level_music, "override_level_music"
10562 &setup.volume_simple, "volume_simple"
10566 &setup.volume_loops, "volume_loops"
10570 &setup.volume_music, "volume_music"
10574 &setup.network_mode, "network_mode"
10578 &setup.network_player_nr, "network_player"
10582 &setup.network_server_hostname, "network_server_hostname"
10586 &setup.touch.control_type, "touch.control_type"
10590 &setup.touch.move_distance, "touch.move_distance"
10594 &setup.touch.drop_distance, "touch.drop_distance"
10598 &setup.touch.transparency, "touch.transparency"
10602 &setup.touch.draw_outlined, "touch.draw_outlined"
10606 &setup.touch.draw_pressed, "touch.draw_pressed"
10610 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10614 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10618 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10622 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10626 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10630 static struct TokenInfo auto_setup_tokens[] =
10634 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10638 static struct TokenInfo server_setup_tokens[] =
10642 &setup.player_uuid, "player_uuid"
10646 &setup.player_version, "player_version"
10650 &setup.use_api_server, TEST_PREFIX "use_api_server"
10654 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10658 &setup.api_server_password, TEST_PREFIX "api_server_password"
10662 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10666 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10670 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10674 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10678 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10682 static struct TokenInfo editor_setup_tokens[] =
10686 &setup.editor.el_classic, "editor.el_classic"
10690 &setup.editor.el_custom, "editor.el_custom"
10694 &setup.editor.el_user_defined, "editor.el_user_defined"
10698 &setup.editor.el_dynamic, "editor.el_dynamic"
10702 &setup.editor.el_headlines, "editor.el_headlines"
10706 &setup.editor.show_element_token, "editor.show_element_token"
10710 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10714 static struct TokenInfo editor_cascade_setup_tokens[] =
10718 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10722 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10726 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10730 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10734 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10738 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10742 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10746 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10750 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10754 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10758 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10762 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10766 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10770 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10774 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10778 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10782 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10786 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10790 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10794 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10798 static struct TokenInfo shortcut_setup_tokens[] =
10802 &setup.shortcut.save_game, "shortcut.save_game"
10806 &setup.shortcut.load_game, "shortcut.load_game"
10810 &setup.shortcut.restart_game, "shortcut.restart_game"
10814 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10818 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10822 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10826 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10830 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10834 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10838 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10842 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10846 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10850 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10854 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10858 &setup.shortcut.tape_record, "shortcut.tape_record"
10862 &setup.shortcut.tape_play, "shortcut.tape_play"
10866 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10870 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10874 &setup.shortcut.sound_music, "shortcut.sound_music"
10878 &setup.shortcut.snap_left, "shortcut.snap_left"
10882 &setup.shortcut.snap_right, "shortcut.snap_right"
10886 &setup.shortcut.snap_up, "shortcut.snap_up"
10890 &setup.shortcut.snap_down, "shortcut.snap_down"
10894 static struct SetupInputInfo setup_input;
10895 static struct TokenInfo player_setup_tokens[] =
10899 &setup_input.use_joystick, ".use_joystick"
10903 &setup_input.joy.device_name, ".joy.device_name"
10907 &setup_input.joy.xleft, ".joy.xleft"
10911 &setup_input.joy.xmiddle, ".joy.xmiddle"
10915 &setup_input.joy.xright, ".joy.xright"
10919 &setup_input.joy.yupper, ".joy.yupper"
10923 &setup_input.joy.ymiddle, ".joy.ymiddle"
10927 &setup_input.joy.ylower, ".joy.ylower"
10931 &setup_input.joy.snap, ".joy.snap_field"
10935 &setup_input.joy.drop, ".joy.place_bomb"
10939 &setup_input.key.left, ".key.move_left"
10943 &setup_input.key.right, ".key.move_right"
10947 &setup_input.key.up, ".key.move_up"
10951 &setup_input.key.down, ".key.move_down"
10955 &setup_input.key.snap, ".key.snap_field"
10959 &setup_input.key.drop, ".key.place_bomb"
10963 static struct TokenInfo system_setup_tokens[] =
10967 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10971 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10975 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10979 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10983 static struct TokenInfo internal_setup_tokens[] =
10987 &setup.internal.program_title, "program_title"
10991 &setup.internal.program_version, "program_version"
10995 &setup.internal.program_author, "program_author"
10999 &setup.internal.program_email, "program_email"
11003 &setup.internal.program_website, "program_website"
11007 &setup.internal.program_copyright, "program_copyright"
11011 &setup.internal.program_company, "program_company"
11015 &setup.internal.program_icon_file, "program_icon_file"
11019 &setup.internal.default_graphics_set, "default_graphics_set"
11023 &setup.internal.default_sounds_set, "default_sounds_set"
11027 &setup.internal.default_music_set, "default_music_set"
11031 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11035 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11039 &setup.internal.fallback_music_file, "fallback_music_file"
11043 &setup.internal.default_level_series, "default_level_series"
11047 &setup.internal.default_window_width, "default_window_width"
11051 &setup.internal.default_window_height, "default_window_height"
11055 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11059 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11063 &setup.internal.create_user_levelset, "create_user_levelset"
11067 &setup.internal.info_screens_from_main, "info_screens_from_main"
11071 &setup.internal.menu_game, "menu_game"
11075 &setup.internal.menu_engines, "menu_engines"
11079 &setup.internal.menu_editor, "menu_editor"
11083 &setup.internal.menu_graphics, "menu_graphics"
11087 &setup.internal.menu_sound, "menu_sound"
11091 &setup.internal.menu_artwork, "menu_artwork"
11095 &setup.internal.menu_input, "menu_input"
11099 &setup.internal.menu_touch, "menu_touch"
11103 &setup.internal.menu_shortcuts, "menu_shortcuts"
11107 &setup.internal.menu_exit, "menu_exit"
11111 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11115 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11119 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11123 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11127 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11131 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11135 &setup.internal.info_title, "info_title"
11139 &setup.internal.info_elements, "info_elements"
11143 &setup.internal.info_music, "info_music"
11147 &setup.internal.info_credits, "info_credits"
11151 &setup.internal.info_program, "info_program"
11155 &setup.internal.info_version, "info_version"
11159 &setup.internal.info_levelset, "info_levelset"
11163 &setup.internal.info_exit, "info_exit"
11167 static struct TokenInfo debug_setup_tokens[] =
11171 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11175 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11179 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11183 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11187 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11191 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11195 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11199 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11203 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11207 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11211 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11215 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11219 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11223 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11227 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11231 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11235 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11239 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11243 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11247 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11251 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11254 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11258 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11262 &setup.debug.xsn_mode, "debug.xsn_mode"
11266 &setup.debug.xsn_percent, "debug.xsn_percent"
11270 static struct TokenInfo options_setup_tokens[] =
11274 &setup.options.verbose, "options.verbose"
11278 &setup.options.debug, "options.debug"
11282 &setup.options.debug_mode, "options.debug_mode"
11286 static void setSetupInfoToDefaults(struct SetupInfo *si)
11290 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11292 si->multiple_users = TRUE;
11295 si->sound_loops = TRUE;
11296 si->sound_music = TRUE;
11297 si->sound_simple = TRUE;
11299 si->global_animations = TRUE;
11300 si->scroll_delay = TRUE;
11301 si->forced_scroll_delay = FALSE;
11302 si->scroll_delay_value = STD_SCROLL_DELAY;
11303 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11304 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11305 si->fade_screens = TRUE;
11306 si->autorecord = TRUE;
11307 si->autorecord_after_replay = TRUE;
11308 si->auto_pause_on_start = FALSE;
11309 si->show_titlescreen = TRUE;
11310 si->quick_doors = FALSE;
11311 si->team_mode = FALSE;
11312 si->handicap = TRUE;
11313 si->skip_levels = TRUE;
11314 si->increment_levels = TRUE;
11315 si->auto_play_next_level = TRUE;
11316 si->count_score_after_game = TRUE;
11317 si->show_scores_after_game = TRUE;
11318 si->time_limit = TRUE;
11319 si->fullscreen = FALSE;
11320 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11321 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11322 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11323 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11324 si->ask_on_escape = TRUE;
11325 si->ask_on_escape_editor = TRUE;
11326 si->ask_on_game_over = TRUE;
11327 si->ask_on_quit_game = TRUE;
11328 si->ask_on_quit_program = TRUE;
11329 si->quick_switch = FALSE;
11330 si->input_on_focus = FALSE;
11331 si->prefer_aga_graphics = TRUE;
11332 si->prefer_lowpass_sounds = FALSE;
11333 si->prefer_extra_panel_items = TRUE;
11334 si->game_speed_extended = FALSE;
11335 si->game_frame_delay = GAME_FRAME_DELAY;
11336 si->bd_skip_uncovering = FALSE;
11337 si->bd_skip_hatching = FALSE;
11338 si->bd_scroll_delay = TRUE;
11339 si->bd_smooth_movements = AUTO;
11340 si->sp_show_border_elements = FALSE;
11341 si->small_game_graphics = FALSE;
11342 si->show_load_save_buttons = FALSE;
11343 si->show_undo_redo_buttons = FALSE;
11344 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11346 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11347 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11348 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11350 si->override_level_graphics = FALSE;
11351 si->override_level_sounds = FALSE;
11352 si->override_level_music = FALSE;
11354 si->volume_simple = 100; // percent
11355 si->volume_loops = 100; // percent
11356 si->volume_music = 100; // percent
11358 si->network_mode = FALSE;
11359 si->network_player_nr = 0; // first player
11360 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11362 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11363 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11364 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11365 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11366 si->touch.draw_outlined = TRUE;
11367 si->touch.draw_pressed = TRUE;
11369 for (i = 0; i < 2; i++)
11371 char *default_grid_button[6][2] =
11377 { "111222", " vv " },
11378 { "111222", " vv " }
11380 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11381 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11382 int min_xsize = MIN(6, grid_xsize);
11383 int min_ysize = MIN(6, grid_ysize);
11384 int startx = grid_xsize - min_xsize;
11385 int starty = grid_ysize - min_ysize;
11388 // virtual buttons grid can only be set to defaults if video is initialized
11389 // (this will be repeated if virtual buttons are not loaded from setup file)
11390 if (video.initialized)
11392 si->touch.grid_xsize[i] = grid_xsize;
11393 si->touch.grid_ysize[i] = grid_ysize;
11397 si->touch.grid_xsize[i] = -1;
11398 si->touch.grid_ysize[i] = -1;
11401 for (x = 0; x < MAX_GRID_XSIZE; x++)
11402 for (y = 0; y < MAX_GRID_YSIZE; y++)
11403 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11405 for (x = 0; x < min_xsize; x++)
11406 for (y = 0; y < min_ysize; y++)
11407 si->touch.grid_button[i][x][starty + y] =
11408 default_grid_button[y][0][x];
11410 for (x = 0; x < min_xsize; x++)
11411 for (y = 0; y < min_ysize; y++)
11412 si->touch.grid_button[i][startx + x][starty + y] =
11413 default_grid_button[y][1][x];
11416 si->touch.grid_initialized = video.initialized;
11418 si->touch.overlay_buttons = FALSE;
11420 si->editor.el_boulderdash = TRUE;
11421 si->editor.el_boulderdash_native = TRUE;
11422 si->editor.el_boulderdash_effects = TRUE;
11423 si->editor.el_emerald_mine = TRUE;
11424 si->editor.el_emerald_mine_club = TRUE;
11425 si->editor.el_more = TRUE;
11426 si->editor.el_sokoban = TRUE;
11427 si->editor.el_supaplex = TRUE;
11428 si->editor.el_diamond_caves = TRUE;
11429 si->editor.el_dx_boulderdash = TRUE;
11431 si->editor.el_mirror_magic = TRUE;
11432 si->editor.el_deflektor = TRUE;
11434 si->editor.el_chars = TRUE;
11435 si->editor.el_steel_chars = TRUE;
11437 si->editor.el_classic = TRUE;
11438 si->editor.el_custom = TRUE;
11440 si->editor.el_user_defined = FALSE;
11441 si->editor.el_dynamic = TRUE;
11443 si->editor.el_headlines = TRUE;
11445 si->editor.show_element_token = FALSE;
11447 si->editor.show_read_only_warning = TRUE;
11449 si->editor.use_template_for_new_levels = TRUE;
11451 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11452 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11453 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11454 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11455 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11457 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11458 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11459 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11460 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11461 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11463 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11464 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11465 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11466 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11467 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11468 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11470 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11471 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11472 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11474 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11475 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11476 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11477 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11479 for (i = 0; i < MAX_PLAYERS; i++)
11481 si->input[i].use_joystick = FALSE;
11482 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11483 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11484 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11485 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11486 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11487 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11488 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11489 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11490 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11491 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11492 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11493 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11494 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11495 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11496 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11499 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11500 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11501 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11502 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11504 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11505 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11506 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11507 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11508 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11509 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11510 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11512 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11514 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11515 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11516 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11518 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11519 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11520 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11522 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11523 si->internal.choose_from_top_leveldir = FALSE;
11524 si->internal.show_scaling_in_title = TRUE;
11525 si->internal.create_user_levelset = TRUE;
11526 si->internal.info_screens_from_main = FALSE;
11528 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11529 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11531 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11532 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11533 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11534 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11535 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11536 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11537 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11538 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11539 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11540 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11542 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11543 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11544 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11545 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11546 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11547 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11548 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11549 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11550 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11551 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11553 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11554 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11556 si->debug.show_frames_per_second = FALSE;
11558 si->debug.xsn_mode = AUTO;
11559 si->debug.xsn_percent = 0;
11561 si->options.verbose = FALSE;
11562 si->options.debug = FALSE;
11563 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11565 #if defined(PLATFORM_ANDROID)
11566 si->fullscreen = TRUE;
11567 si->touch.overlay_buttons = TRUE;
11570 setHideSetupEntry(&setup.debug.xsn_mode);
11573 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11575 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11578 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11580 si->player_uuid = NULL; // (will be set later)
11581 si->player_version = 1; // (will be set later)
11583 si->use_api_server = TRUE;
11584 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11585 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11586 si->ask_for_uploading_tapes = TRUE;
11587 si->ask_for_remaining_tapes = FALSE;
11588 si->provide_uploading_tapes = TRUE;
11589 si->ask_for_using_api_server = TRUE;
11590 si->has_remaining_tapes = FALSE;
11593 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11595 si->editor_cascade.el_bd = TRUE;
11596 si->editor_cascade.el_bd_native = TRUE;
11597 si->editor_cascade.el_bd_effects = FALSE;
11598 si->editor_cascade.el_em = TRUE;
11599 si->editor_cascade.el_emc = TRUE;
11600 si->editor_cascade.el_rnd = TRUE;
11601 si->editor_cascade.el_sb = TRUE;
11602 si->editor_cascade.el_sp = TRUE;
11603 si->editor_cascade.el_dc = TRUE;
11604 si->editor_cascade.el_dx = TRUE;
11606 si->editor_cascade.el_mm = TRUE;
11607 si->editor_cascade.el_df = TRUE;
11609 si->editor_cascade.el_chars = FALSE;
11610 si->editor_cascade.el_steel_chars = FALSE;
11611 si->editor_cascade.el_ce = FALSE;
11612 si->editor_cascade.el_ge = FALSE;
11613 si->editor_cascade.el_es = FALSE;
11614 si->editor_cascade.el_ref = FALSE;
11615 si->editor_cascade.el_user = FALSE;
11616 si->editor_cascade.el_dynamic = FALSE;
11619 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11621 static char *getHideSetupToken(void *setup_value)
11623 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11625 if (setup_value != NULL)
11626 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11628 return hide_setup_token;
11631 void setHideSetupEntry(void *setup_value)
11633 char *hide_setup_token = getHideSetupToken(setup_value);
11635 if (hide_setup_hash == NULL)
11636 hide_setup_hash = newSetupFileHash();
11638 if (setup_value != NULL)
11639 setHashEntry(hide_setup_hash, hide_setup_token, "");
11642 void removeHideSetupEntry(void *setup_value)
11644 char *hide_setup_token = getHideSetupToken(setup_value);
11646 if (setup_value != NULL)
11647 removeHashEntry(hide_setup_hash, hide_setup_token);
11650 boolean hideSetupEntry(void *setup_value)
11652 char *hide_setup_token = getHideSetupToken(setup_value);
11654 return (setup_value != NULL &&
11655 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11658 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11659 struct TokenInfo *token_info,
11660 int token_nr, char *token_text)
11662 char *token_hide_text = getStringCat2(token_text, ".hide");
11663 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11665 // set the value of this setup option in the setup option structure
11666 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11668 // check if this setup option should be hidden in the setup menu
11669 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11670 setHideSetupEntry(token_info[token_nr].value);
11672 free(token_hide_text);
11675 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11676 struct TokenInfo *token_info,
11679 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11680 token_info[token_nr].text);
11683 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11687 if (!setup_file_hash)
11690 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11691 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11693 setup.touch.grid_initialized = TRUE;
11694 for (i = 0; i < 2; i++)
11696 int grid_xsize = setup.touch.grid_xsize[i];
11697 int grid_ysize = setup.touch.grid_ysize[i];
11700 // if virtual buttons are not loaded from setup file, repeat initializing
11701 // virtual buttons grid with default values later when video is initialized
11702 if (grid_xsize == -1 ||
11705 setup.touch.grid_initialized = FALSE;
11710 for (y = 0; y < grid_ysize; y++)
11712 char token_string[MAX_LINE_LEN];
11714 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11716 char *value_string = getHashEntry(setup_file_hash, token_string);
11718 if (value_string == NULL)
11721 for (x = 0; x < grid_xsize; x++)
11723 char c = value_string[x];
11725 setup.touch.grid_button[i][x][y] =
11726 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11731 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11732 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11734 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11735 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11737 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11741 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11743 setup_input = setup.input[pnr];
11744 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11746 char full_token[100];
11748 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11749 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11752 setup.input[pnr] = setup_input;
11755 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11756 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11758 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11759 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11761 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11762 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11764 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11765 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11767 setHideRelatedSetupEntries();
11770 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11774 if (!setup_file_hash)
11777 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11778 setSetupInfo(auto_setup_tokens, i,
11779 getHashEntry(setup_file_hash,
11780 auto_setup_tokens[i].text));
11783 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11787 if (!setup_file_hash)
11790 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11791 setSetupInfo(server_setup_tokens, i,
11792 getHashEntry(setup_file_hash,
11793 server_setup_tokens[i].text));
11796 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11800 if (!setup_file_hash)
11803 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11804 setSetupInfo(editor_cascade_setup_tokens, i,
11805 getHashEntry(setup_file_hash,
11806 editor_cascade_setup_tokens[i].text));
11809 void LoadUserNames(void)
11811 int last_user_nr = user.nr;
11814 if (global.user_names != NULL)
11816 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11817 checked_free(global.user_names[i]);
11819 checked_free(global.user_names);
11822 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11824 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11828 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11830 if (setup_file_hash)
11832 char *player_name = getHashEntry(setup_file_hash, "player_name");
11834 global.user_names[i] = getFixedUserName(player_name);
11836 freeSetupFileHash(setup_file_hash);
11839 if (global.user_names[i] == NULL)
11840 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11843 user.nr = last_user_nr;
11846 void LoadSetupFromFilename(char *filename)
11848 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11850 if (setup_file_hash)
11852 decodeSetupFileHash_Default(setup_file_hash);
11854 freeSetupFileHash(setup_file_hash);
11858 Debug("setup", "using default setup values");
11862 static void LoadSetup_SpecialPostProcessing(void)
11864 char *player_name_new;
11866 // needed to work around problems with fixed length strings
11867 player_name_new = getFixedUserName(setup.player_name);
11868 free(setup.player_name);
11869 setup.player_name = player_name_new;
11871 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11872 if (setup.scroll_delay == FALSE)
11874 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11875 setup.scroll_delay = TRUE; // now always "on"
11878 // make sure that scroll delay value stays inside valid range
11879 setup.scroll_delay_value =
11880 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11883 void LoadSetup_Default(void)
11887 // always start with reliable default values
11888 setSetupInfoToDefaults(&setup);
11890 // try to load setup values from default setup file
11891 filename = getDefaultSetupFilename();
11893 if (fileExists(filename))
11894 LoadSetupFromFilename(filename);
11896 // try to load setup values from platform setup file
11897 filename = getPlatformSetupFilename();
11899 if (fileExists(filename))
11900 LoadSetupFromFilename(filename);
11902 // try to load setup values from user setup file
11903 filename = getSetupFilename();
11905 LoadSetupFromFilename(filename);
11907 LoadSetup_SpecialPostProcessing();
11910 void LoadSetup_AutoSetup(void)
11912 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11913 SetupFileHash *setup_file_hash = NULL;
11915 // always start with reliable default values
11916 setSetupInfoToDefaults_AutoSetup(&setup);
11918 setup_file_hash = loadSetupFileHash(filename);
11920 if (setup_file_hash)
11922 decodeSetupFileHash_AutoSetup(setup_file_hash);
11924 freeSetupFileHash(setup_file_hash);
11930 void LoadSetup_ServerSetup(void)
11932 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11933 SetupFileHash *setup_file_hash = NULL;
11935 // always start with reliable default values
11936 setSetupInfoToDefaults_ServerSetup(&setup);
11938 setup_file_hash = loadSetupFileHash(filename);
11940 if (setup_file_hash)
11942 decodeSetupFileHash_ServerSetup(setup_file_hash);
11944 freeSetupFileHash(setup_file_hash);
11949 if (setup.player_uuid == NULL)
11951 // player UUID does not yet exist in setup file
11952 setup.player_uuid = getStringCopy(getUUID());
11953 setup.player_version = 2;
11955 SaveSetup_ServerSetup();
11959 void LoadSetup_EditorCascade(void)
11961 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11962 SetupFileHash *setup_file_hash = NULL;
11964 // always start with reliable default values
11965 setSetupInfoToDefaults_EditorCascade(&setup);
11967 setup_file_hash = loadSetupFileHash(filename);
11969 if (setup_file_hash)
11971 decodeSetupFileHash_EditorCascade(setup_file_hash);
11973 freeSetupFileHash(setup_file_hash);
11979 void LoadSetup(void)
11981 LoadSetup_Default();
11982 LoadSetup_AutoSetup();
11983 LoadSetup_ServerSetup();
11984 LoadSetup_EditorCascade();
11987 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11988 char *mapping_line)
11990 char mapping_guid[MAX_LINE_LEN];
11991 char *mapping_start, *mapping_end;
11993 // get GUID from game controller mapping line: copy complete line
11994 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11995 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11997 // get GUID from game controller mapping line: cut after GUID part
11998 mapping_start = strchr(mapping_guid, ',');
11999 if (mapping_start != NULL)
12000 *mapping_start = '\0';
12002 // cut newline from game controller mapping line
12003 mapping_end = strchr(mapping_line, '\n');
12004 if (mapping_end != NULL)
12005 *mapping_end = '\0';
12007 // add mapping entry to game controller mappings hash
12008 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12011 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12016 if (!(file = fopen(filename, MODE_READ)))
12018 Warn("cannot read game controller mappings file '%s'", filename);
12023 while (!feof(file))
12025 char line[MAX_LINE_LEN];
12027 if (!fgets(line, MAX_LINE_LEN, file))
12030 addGameControllerMappingToHash(mappings_hash, line);
12036 void SaveSetup_Default(void)
12038 char *filename = getSetupFilename();
12042 InitUserDataDirectory();
12044 if (!(file = fopen(filename, MODE_WRITE)))
12046 Warn("cannot write setup file '%s'", filename);
12051 fprintFileHeader(file, SETUP_FILENAME);
12053 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12055 // just to make things nicer :)
12056 if (global_setup_tokens[i].value == &setup.multiple_users ||
12057 global_setup_tokens[i].value == &setup.sound ||
12058 global_setup_tokens[i].value == &setup.graphics_set ||
12059 global_setup_tokens[i].value == &setup.volume_simple ||
12060 global_setup_tokens[i].value == &setup.network_mode ||
12061 global_setup_tokens[i].value == &setup.touch.control_type ||
12062 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12063 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12064 fprintf(file, "\n");
12066 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12069 for (i = 0; i < 2; i++)
12071 int grid_xsize = setup.touch.grid_xsize[i];
12072 int grid_ysize = setup.touch.grid_ysize[i];
12075 fprintf(file, "\n");
12077 for (y = 0; y < grid_ysize; y++)
12079 char token_string[MAX_LINE_LEN];
12080 char value_string[MAX_LINE_LEN];
12082 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12084 for (x = 0; x < grid_xsize; x++)
12086 char c = setup.touch.grid_button[i][x][y];
12088 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12091 value_string[grid_xsize] = '\0';
12093 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12097 fprintf(file, "\n");
12098 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12099 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12101 fprintf(file, "\n");
12102 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12103 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12105 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12109 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12110 fprintf(file, "\n");
12112 setup_input = setup.input[pnr];
12113 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12114 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12117 fprintf(file, "\n");
12118 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12119 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12121 // (internal setup values not saved to user setup file)
12123 fprintf(file, "\n");
12124 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12125 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12126 setup.debug.xsn_mode != AUTO)
12127 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12129 fprintf(file, "\n");
12130 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12131 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12135 SetFilePermissions(filename, PERMS_PRIVATE);
12138 void SaveSetup_AutoSetup(void)
12140 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12144 InitUserDataDirectory();
12146 if (!(file = fopen(filename, MODE_WRITE)))
12148 Warn("cannot write auto setup file '%s'", filename);
12155 fprintFileHeader(file, AUTOSETUP_FILENAME);
12157 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12158 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12162 SetFilePermissions(filename, PERMS_PRIVATE);
12167 void SaveSetup_ServerSetup(void)
12169 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12173 InitUserDataDirectory();
12175 if (!(file = fopen(filename, MODE_WRITE)))
12177 Warn("cannot write server setup file '%s'", filename);
12184 fprintFileHeader(file, SERVERSETUP_FILENAME);
12186 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12188 // just to make things nicer :)
12189 if (server_setup_tokens[i].value == &setup.use_api_server)
12190 fprintf(file, "\n");
12192 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12197 SetFilePermissions(filename, PERMS_PRIVATE);
12202 void SaveSetup_EditorCascade(void)
12204 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12208 InitUserDataDirectory();
12210 if (!(file = fopen(filename, MODE_WRITE)))
12212 Warn("cannot write editor cascade state file '%s'", filename);
12219 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12221 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12222 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12226 SetFilePermissions(filename, PERMS_PRIVATE);
12231 void SaveSetup(void)
12233 SaveSetup_Default();
12234 SaveSetup_AutoSetup();
12235 SaveSetup_ServerSetup();
12236 SaveSetup_EditorCascade();
12239 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12244 if (!(file = fopen(filename, MODE_WRITE)))
12246 Warn("cannot write game controller mappings file '%s'", filename);
12251 BEGIN_HASH_ITERATION(mappings_hash, itr)
12253 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12255 END_HASH_ITERATION(mappings_hash, itr)
12260 void SaveSetup_AddGameControllerMapping(char *mapping)
12262 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12263 SetupFileHash *mappings_hash = newSetupFileHash();
12265 InitUserDataDirectory();
12267 // load existing personal game controller mappings
12268 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12270 // add new mapping to personal game controller mappings
12271 addGameControllerMappingToHash(mappings_hash, mapping);
12273 // save updated personal game controller mappings
12274 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12276 freeSetupFileHash(mappings_hash);
12280 void LoadCustomElementDescriptions(void)
12282 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12283 SetupFileHash *setup_file_hash;
12286 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12288 if (element_info[i].custom_description != NULL)
12290 free(element_info[i].custom_description);
12291 element_info[i].custom_description = NULL;
12295 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12298 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12300 char *token = getStringCat2(element_info[i].token_name, ".name");
12301 char *value = getHashEntry(setup_file_hash, token);
12304 element_info[i].custom_description = getStringCopy(value);
12309 freeSetupFileHash(setup_file_hash);
12312 static int getElementFromToken(char *token)
12314 char *value = getHashEntry(element_token_hash, token);
12317 return atoi(value);
12319 Warn("unknown element token '%s'", token);
12321 return EL_UNDEFINED;
12324 void FreeGlobalAnimEventInfo(void)
12326 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12328 if (gaei->event_list == NULL)
12333 for (i = 0; i < gaei->num_event_lists; i++)
12335 checked_free(gaei->event_list[i]->event_value);
12336 checked_free(gaei->event_list[i]);
12339 checked_free(gaei->event_list);
12341 gaei->event_list = NULL;
12342 gaei->num_event_lists = 0;
12345 static int AddGlobalAnimEventList(void)
12347 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12348 int list_pos = gaei->num_event_lists++;
12350 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12351 sizeof(struct GlobalAnimEventListInfo *));
12353 gaei->event_list[list_pos] =
12354 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12356 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12358 gaeli->event_value = NULL;
12359 gaeli->num_event_values = 0;
12364 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12366 // do not add empty global animation events
12367 if (event_value == ANIM_EVENT_NONE)
12370 // if list position is undefined, create new list
12371 if (list_pos == ANIM_EVENT_UNDEFINED)
12372 list_pos = AddGlobalAnimEventList();
12374 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12375 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12376 int value_pos = gaeli->num_event_values++;
12378 gaeli->event_value = checked_realloc(gaeli->event_value,
12379 gaeli->num_event_values * sizeof(int *));
12381 gaeli->event_value[value_pos] = event_value;
12386 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12388 if (list_pos == ANIM_EVENT_UNDEFINED)
12389 return ANIM_EVENT_NONE;
12391 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12392 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12394 return gaeli->event_value[value_pos];
12397 int GetGlobalAnimEventValueCount(int list_pos)
12399 if (list_pos == ANIM_EVENT_UNDEFINED)
12402 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12403 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12405 return gaeli->num_event_values;
12408 // This function checks if a string <s> of the format "string1, string2, ..."
12409 // exactly contains a string <s_contained>.
12411 static boolean string_has_parameter(char *s, char *s_contained)
12415 if (s == NULL || s_contained == NULL)
12418 if (strlen(s_contained) > strlen(s))
12421 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12423 char next_char = s[strlen(s_contained)];
12425 // check if next character is delimiter or whitespace
12426 if (next_char == ',' || next_char == '\0' ||
12427 next_char == ' ' || next_char == '\t')
12431 // check if string contains another parameter string after a comma
12432 substring = strchr(s, ',');
12433 if (substring == NULL) // string does not contain a comma
12436 // advance string pointer to next character after the comma
12439 // skip potential whitespaces after the comma
12440 while (*substring == ' ' || *substring == '\t')
12443 return string_has_parameter(substring, s_contained);
12446 static int get_anim_parameter_value_ce(char *s)
12449 char *pattern_1 = "ce_change:custom_";
12450 char *pattern_2 = ".page_";
12451 int pattern_1_len = strlen(pattern_1);
12452 char *matching_char = strstr(s_ptr, pattern_1);
12453 int result = ANIM_EVENT_NONE;
12455 if (matching_char == NULL)
12456 return ANIM_EVENT_NONE;
12458 result = ANIM_EVENT_CE_CHANGE;
12460 s_ptr = matching_char + pattern_1_len;
12462 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12463 if (*s_ptr >= '0' && *s_ptr <= '9')
12465 int gic_ce_nr = (*s_ptr++ - '0');
12467 if (*s_ptr >= '0' && *s_ptr <= '9')
12469 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12471 if (*s_ptr >= '0' && *s_ptr <= '9')
12472 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12475 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12476 return ANIM_EVENT_NONE;
12478 // custom element stored as 0 to 255
12481 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12485 // invalid custom element number specified
12487 return ANIM_EVENT_NONE;
12490 // check for change page number ("page_X" or "page_XX") (optional)
12491 if (strPrefix(s_ptr, pattern_2))
12493 s_ptr += strlen(pattern_2);
12495 if (*s_ptr >= '0' && *s_ptr <= '9')
12497 int gic_page_nr = (*s_ptr++ - '0');
12499 if (*s_ptr >= '0' && *s_ptr <= '9')
12500 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12502 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12503 return ANIM_EVENT_NONE;
12505 // change page stored as 1 to 32 (0 means "all change pages")
12507 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12511 // invalid animation part number specified
12513 return ANIM_EVENT_NONE;
12517 // discard result if next character is neither delimiter nor whitespace
12518 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12519 *s_ptr == ' ' || *s_ptr == '\t'))
12520 return ANIM_EVENT_NONE;
12525 static int get_anim_parameter_value(char *s)
12527 int event_value[] =
12535 char *pattern_1[] =
12543 char *pattern_2 = ".part_";
12544 char *matching_char = NULL;
12546 int pattern_1_len = 0;
12547 int result = ANIM_EVENT_NONE;
12550 result = get_anim_parameter_value_ce(s);
12552 if (result != ANIM_EVENT_NONE)
12555 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12557 matching_char = strstr(s_ptr, pattern_1[i]);
12558 pattern_1_len = strlen(pattern_1[i]);
12559 result = event_value[i];
12561 if (matching_char != NULL)
12565 if (matching_char == NULL)
12566 return ANIM_EVENT_NONE;
12568 s_ptr = matching_char + pattern_1_len;
12570 // check for main animation number ("anim_X" or "anim_XX")
12571 if (*s_ptr >= '0' && *s_ptr <= '9')
12573 int gic_anim_nr = (*s_ptr++ - '0');
12575 if (*s_ptr >= '0' && *s_ptr <= '9')
12576 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12578 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12579 return ANIM_EVENT_NONE;
12581 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12585 // invalid main animation number specified
12587 return ANIM_EVENT_NONE;
12590 // check for animation part number ("part_X" or "part_XX") (optional)
12591 if (strPrefix(s_ptr, pattern_2))
12593 s_ptr += strlen(pattern_2);
12595 if (*s_ptr >= '0' && *s_ptr <= '9')
12597 int gic_part_nr = (*s_ptr++ - '0');
12599 if (*s_ptr >= '0' && *s_ptr <= '9')
12600 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12602 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12603 return ANIM_EVENT_NONE;
12605 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12609 // invalid animation part number specified
12611 return ANIM_EVENT_NONE;
12615 // discard result if next character is neither delimiter nor whitespace
12616 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12617 *s_ptr == ' ' || *s_ptr == '\t'))
12618 return ANIM_EVENT_NONE;
12623 static int get_anim_parameter_values(char *s)
12625 int list_pos = ANIM_EVENT_UNDEFINED;
12626 int event_value = ANIM_EVENT_DEFAULT;
12628 if (string_has_parameter(s, "any"))
12629 event_value |= ANIM_EVENT_ANY;
12631 if (string_has_parameter(s, "click:self") ||
12632 string_has_parameter(s, "click") ||
12633 string_has_parameter(s, "self"))
12634 event_value |= ANIM_EVENT_SELF;
12636 if (string_has_parameter(s, "unclick:any"))
12637 event_value |= ANIM_EVENT_UNCLICK_ANY;
12639 // if animation event found, add it to global animation event list
12640 if (event_value != ANIM_EVENT_NONE)
12641 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12645 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12646 event_value = get_anim_parameter_value(s);
12648 // if animation event found, add it to global animation event list
12649 if (event_value != ANIM_EVENT_NONE)
12650 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12652 // continue with next part of the string, starting with next comma
12653 s = strchr(s + 1, ',');
12659 static int get_anim_action_parameter_value(char *token)
12661 // check most common default case first to massively speed things up
12662 if (strEqual(token, ARG_UNDEFINED))
12663 return ANIM_EVENT_ACTION_NONE;
12665 int result = getImageIDFromToken(token);
12669 char *gfx_token = getStringCat2("gfx.", token);
12671 result = getImageIDFromToken(gfx_token);
12673 checked_free(gfx_token);
12678 Key key = getKeyFromX11KeyName(token);
12680 if (key != KSYM_UNDEFINED)
12681 result = -(int)key;
12688 result = get_hash_from_string(token); // unsigned int => int
12689 result = ABS(result); // may be negative now
12690 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12692 setHashEntry(anim_url_hash, int2str(result, 0), token);
12697 result = ANIM_EVENT_ACTION_NONE;
12702 int get_parameter_value(char *value_raw, char *suffix, int type)
12704 char *value = getStringToLower(value_raw);
12705 int result = 0; // probably a save default value
12707 if (strEqual(suffix, ".direction"))
12709 result = (strEqual(value, "left") ? MV_LEFT :
12710 strEqual(value, "right") ? MV_RIGHT :
12711 strEqual(value, "up") ? MV_UP :
12712 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12714 else if (strEqual(suffix, ".position"))
12716 result = (strEqual(value, "left") ? POS_LEFT :
12717 strEqual(value, "right") ? POS_RIGHT :
12718 strEqual(value, "top") ? POS_TOP :
12719 strEqual(value, "upper") ? POS_UPPER :
12720 strEqual(value, "middle") ? POS_MIDDLE :
12721 strEqual(value, "lower") ? POS_LOWER :
12722 strEqual(value, "bottom") ? POS_BOTTOM :
12723 strEqual(value, "any") ? POS_ANY :
12724 strEqual(value, "ce") ? POS_CE :
12725 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12726 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12728 else if (strEqual(suffix, ".align"))
12730 result = (strEqual(value, "left") ? ALIGN_LEFT :
12731 strEqual(value, "right") ? ALIGN_RIGHT :
12732 strEqual(value, "center") ? ALIGN_CENTER :
12733 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12735 else if (strEqual(suffix, ".valign"))
12737 result = (strEqual(value, "top") ? VALIGN_TOP :
12738 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12739 strEqual(value, "middle") ? VALIGN_MIDDLE :
12740 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12742 else if (strEqual(suffix, ".anim_mode"))
12744 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12745 string_has_parameter(value, "loop") ? ANIM_LOOP :
12746 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12747 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12748 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12749 string_has_parameter(value, "random") ? ANIM_RANDOM :
12750 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12751 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12752 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12753 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12754 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12755 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12756 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12757 string_has_parameter(value, "all") ? ANIM_ALL :
12758 string_has_parameter(value, "tiled") ? ANIM_TILED :
12759 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12762 if (string_has_parameter(value, "once"))
12763 result |= ANIM_ONCE;
12765 if (string_has_parameter(value, "reverse"))
12766 result |= ANIM_REVERSE;
12768 if (string_has_parameter(value, "opaque_player"))
12769 result |= ANIM_OPAQUE_PLAYER;
12771 if (string_has_parameter(value, "static_panel"))
12772 result |= ANIM_STATIC_PANEL;
12774 else if (strEqual(suffix, ".init_event") ||
12775 strEqual(suffix, ".anim_event"))
12777 result = get_anim_parameter_values(value);
12779 else if (strEqual(suffix, ".init_delay_action") ||
12780 strEqual(suffix, ".anim_delay_action") ||
12781 strEqual(suffix, ".post_delay_action") ||
12782 strEqual(suffix, ".init_event_action") ||
12783 strEqual(suffix, ".anim_event_action"))
12785 result = get_anim_action_parameter_value(value_raw);
12787 else if (strEqual(suffix, ".class"))
12789 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12790 get_hash_from_string(value));
12792 else if (strEqual(suffix, ".style"))
12794 result = STYLE_DEFAULT;
12796 if (string_has_parameter(value, "accurate_borders"))
12797 result |= STYLE_ACCURATE_BORDERS;
12799 if (string_has_parameter(value, "inner_corners"))
12800 result |= STYLE_INNER_CORNERS;
12802 if (string_has_parameter(value, "reverse"))
12803 result |= STYLE_REVERSE;
12805 if (string_has_parameter(value, "leftmost_position"))
12806 result |= STYLE_LEFTMOST_POSITION;
12808 if (string_has_parameter(value, "block_clicks"))
12809 result |= STYLE_BLOCK;
12811 if (string_has_parameter(value, "passthrough_clicks"))
12812 result |= STYLE_PASSTHROUGH;
12814 if (string_has_parameter(value, "multiple_actions"))
12815 result |= STYLE_MULTIPLE_ACTIONS;
12817 if (string_has_parameter(value, "consume_ce_event"))
12818 result |= STYLE_CONSUME_CE_EVENT;
12820 else if (strEqual(suffix, ".fade_mode"))
12822 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12823 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12824 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12825 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12826 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12827 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12828 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12829 FADE_MODE_DEFAULT);
12831 else if (strEqual(suffix, ".auto_delay_unit"))
12833 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12834 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12835 AUTO_DELAY_UNIT_DEFAULT);
12837 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12839 result = gfx.get_font_from_token_function(value);
12841 else // generic parameter of type integer or boolean
12843 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12844 type == TYPE_INTEGER ? get_integer_from_string(value) :
12845 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12846 ARG_UNDEFINED_VALUE);
12854 static int get_token_parameter_value(char *token, char *value_raw)
12858 if (token == NULL || value_raw == NULL)
12859 return ARG_UNDEFINED_VALUE;
12861 suffix = strrchr(token, '.');
12862 if (suffix == NULL)
12865 if (strEqual(suffix, ".element"))
12866 return getElementFromToken(value_raw);
12868 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12869 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12872 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12873 boolean ignore_defaults)
12877 for (i = 0; image_config_vars[i].token != NULL; i++)
12879 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12881 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12882 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12886 *image_config_vars[i].value =
12887 get_token_parameter_value(image_config_vars[i].token, value);
12891 void InitMenuDesignSettings_Static(void)
12893 // always start with reliable default values from static default config
12894 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12897 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12901 // the following initializes hierarchical values from static configuration
12903 // special case: initialize "ARG_DEFAULT" values in static default config
12904 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12905 titlescreen_initial_first_default.fade_mode =
12906 title_initial_first_default.fade_mode;
12907 titlescreen_initial_first_default.fade_delay =
12908 title_initial_first_default.fade_delay;
12909 titlescreen_initial_first_default.post_delay =
12910 title_initial_first_default.post_delay;
12911 titlescreen_initial_first_default.auto_delay =
12912 title_initial_first_default.auto_delay;
12913 titlescreen_initial_first_default.auto_delay_unit =
12914 title_initial_first_default.auto_delay_unit;
12915 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12916 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12917 titlescreen_first_default.post_delay = title_first_default.post_delay;
12918 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12919 titlescreen_first_default.auto_delay_unit =
12920 title_first_default.auto_delay_unit;
12921 titlemessage_initial_first_default.fade_mode =
12922 title_initial_first_default.fade_mode;
12923 titlemessage_initial_first_default.fade_delay =
12924 title_initial_first_default.fade_delay;
12925 titlemessage_initial_first_default.post_delay =
12926 title_initial_first_default.post_delay;
12927 titlemessage_initial_first_default.auto_delay =
12928 title_initial_first_default.auto_delay;
12929 titlemessage_initial_first_default.auto_delay_unit =
12930 title_initial_first_default.auto_delay_unit;
12931 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12932 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12933 titlemessage_first_default.post_delay = title_first_default.post_delay;
12934 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12935 titlemessage_first_default.auto_delay_unit =
12936 title_first_default.auto_delay_unit;
12938 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12939 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12940 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12941 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12942 titlescreen_initial_default.auto_delay_unit =
12943 title_initial_default.auto_delay_unit;
12944 titlescreen_default.fade_mode = title_default.fade_mode;
12945 titlescreen_default.fade_delay = title_default.fade_delay;
12946 titlescreen_default.post_delay = title_default.post_delay;
12947 titlescreen_default.auto_delay = title_default.auto_delay;
12948 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12949 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12950 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12951 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12952 titlemessage_initial_default.auto_delay_unit =
12953 title_initial_default.auto_delay_unit;
12954 titlemessage_default.fade_mode = title_default.fade_mode;
12955 titlemessage_default.fade_delay = title_default.fade_delay;
12956 titlemessage_default.post_delay = title_default.post_delay;
12957 titlemessage_default.auto_delay = title_default.auto_delay;
12958 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12960 // special case: initialize "ARG_DEFAULT" values in static default config
12961 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12962 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12964 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12965 titlescreen_first[i] = titlescreen_first_default;
12966 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12967 titlemessage_first[i] = titlemessage_first_default;
12969 titlescreen_initial[i] = titlescreen_initial_default;
12970 titlescreen[i] = titlescreen_default;
12971 titlemessage_initial[i] = titlemessage_initial_default;
12972 titlemessage[i] = titlemessage_default;
12975 // special case: initialize "ARG_DEFAULT" values in static default config
12976 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12977 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12979 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12982 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12983 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12984 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12987 // special case: initialize "ARG_DEFAULT" values in static default config
12988 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12989 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12991 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12992 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12993 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12995 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12998 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13002 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13006 struct XY *dst, *src;
13008 game_buttons_xy[] =
13010 { &game.button.save, &game.button.stop },
13011 { &game.button.pause2, &game.button.pause },
13012 { &game.button.load, &game.button.play },
13013 { &game.button.undo, &game.button.stop },
13014 { &game.button.redo, &game.button.play },
13020 // special case: initialize later added SETUP list size from LEVELS value
13021 if (menu.list_size[GAME_MODE_SETUP] == -1)
13022 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13024 // set default position for snapshot buttons to stop/pause/play buttons
13025 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13026 if ((*game_buttons_xy[i].dst).x == -1 &&
13027 (*game_buttons_xy[i].dst).y == -1)
13028 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13030 // --------------------------------------------------------------------------
13031 // dynamic viewports (including playfield margins, borders and alignments)
13032 // --------------------------------------------------------------------------
13034 // dynamic viewports currently only supported for landscape mode
13035 int display_width = MAX(video.display_width, video.display_height);
13036 int display_height = MIN(video.display_width, video.display_height);
13038 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13040 struct RectWithBorder *vp_window = &viewport.window[i];
13041 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13042 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13043 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13044 boolean dynamic_window_width = (vp_window->min_width != -1);
13045 boolean dynamic_window_height = (vp_window->min_height != -1);
13046 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13047 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13049 // adjust window size if min/max width/height is specified
13051 if (vp_window->min_width != -1)
13053 int window_width = display_width;
13055 // when using static window height, use aspect ratio of display
13056 if (vp_window->min_height == -1)
13057 window_width = vp_window->height * display_width / display_height;
13059 vp_window->width = MAX(vp_window->min_width, window_width);
13062 if (vp_window->min_height != -1)
13064 int window_height = display_height;
13066 // when using static window width, use aspect ratio of display
13067 if (vp_window->min_width == -1)
13068 window_height = vp_window->width * display_height / display_width;
13070 vp_window->height = MAX(vp_window->min_height, window_height);
13073 if (vp_window->max_width != -1)
13074 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13076 if (vp_window->max_height != -1)
13077 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13079 int playfield_width = vp_window->width;
13080 int playfield_height = vp_window->height;
13082 // adjust playfield size and position according to specified margins
13084 playfield_width -= vp_playfield->margin_left;
13085 playfield_width -= vp_playfield->margin_right;
13087 playfield_height -= vp_playfield->margin_top;
13088 playfield_height -= vp_playfield->margin_bottom;
13090 // adjust playfield size if min/max width/height is specified
13092 if (vp_playfield->min_width != -1)
13093 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13095 if (vp_playfield->min_height != -1)
13096 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13098 if (vp_playfield->max_width != -1)
13099 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13101 if (vp_playfield->max_height != -1)
13102 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13104 // adjust playfield position according to specified alignment
13106 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13107 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13108 else if (vp_playfield->align == ALIGN_CENTER)
13109 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13110 else if (vp_playfield->align == ALIGN_RIGHT)
13111 vp_playfield->x += playfield_width - vp_playfield->width;
13113 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13114 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13115 else if (vp_playfield->valign == VALIGN_MIDDLE)
13116 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13117 else if (vp_playfield->valign == VALIGN_BOTTOM)
13118 vp_playfield->y += playfield_height - vp_playfield->height;
13120 vp_playfield->x += vp_playfield->margin_left;
13121 vp_playfield->y += vp_playfield->margin_top;
13123 // adjust individual playfield borders if only default border is specified
13125 if (vp_playfield->border_left == -1)
13126 vp_playfield->border_left = vp_playfield->border_size;
13127 if (vp_playfield->border_right == -1)
13128 vp_playfield->border_right = vp_playfield->border_size;
13129 if (vp_playfield->border_top == -1)
13130 vp_playfield->border_top = vp_playfield->border_size;
13131 if (vp_playfield->border_bottom == -1)
13132 vp_playfield->border_bottom = vp_playfield->border_size;
13134 // set dynamic playfield borders if borders are specified as undefined
13135 // (but only if window size was dynamic and playfield size was static)
13137 if (dynamic_window_width && !dynamic_playfield_width)
13139 if (vp_playfield->border_left == -1)
13141 vp_playfield->border_left = (vp_playfield->x -
13142 vp_playfield->margin_left);
13143 vp_playfield->x -= vp_playfield->border_left;
13144 vp_playfield->width += vp_playfield->border_left;
13147 if (vp_playfield->border_right == -1)
13149 vp_playfield->border_right = (vp_window->width -
13151 vp_playfield->width -
13152 vp_playfield->margin_right);
13153 vp_playfield->width += vp_playfield->border_right;
13157 if (dynamic_window_height && !dynamic_playfield_height)
13159 if (vp_playfield->border_top == -1)
13161 vp_playfield->border_top = (vp_playfield->y -
13162 vp_playfield->margin_top);
13163 vp_playfield->y -= vp_playfield->border_top;
13164 vp_playfield->height += vp_playfield->border_top;
13167 if (vp_playfield->border_bottom == -1)
13169 vp_playfield->border_bottom = (vp_window->height -
13171 vp_playfield->height -
13172 vp_playfield->margin_bottom);
13173 vp_playfield->height += vp_playfield->border_bottom;
13177 // adjust playfield size to be a multiple of a defined alignment tile size
13179 int align_size = vp_playfield->align_size;
13180 int playfield_xtiles = vp_playfield->width / align_size;
13181 int playfield_ytiles = vp_playfield->height / align_size;
13182 int playfield_width_corrected = playfield_xtiles * align_size;
13183 int playfield_height_corrected = playfield_ytiles * align_size;
13184 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13185 i == GFX_SPECIAL_ARG_EDITOR);
13187 if (is_playfield_mode &&
13188 dynamic_playfield_width &&
13189 vp_playfield->width != playfield_width_corrected)
13191 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13193 vp_playfield->width = playfield_width_corrected;
13195 if (vp_playfield->align == ALIGN_LEFT)
13197 vp_playfield->border_left += playfield_xdiff;
13199 else if (vp_playfield->align == ALIGN_RIGHT)
13201 vp_playfield->border_right += playfield_xdiff;
13203 else if (vp_playfield->align == ALIGN_CENTER)
13205 int border_left_diff = playfield_xdiff / 2;
13206 int border_right_diff = playfield_xdiff - border_left_diff;
13208 vp_playfield->border_left += border_left_diff;
13209 vp_playfield->border_right += border_right_diff;
13213 if (is_playfield_mode &&
13214 dynamic_playfield_height &&
13215 vp_playfield->height != playfield_height_corrected)
13217 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13219 vp_playfield->height = playfield_height_corrected;
13221 if (vp_playfield->valign == VALIGN_TOP)
13223 vp_playfield->border_top += playfield_ydiff;
13225 else if (vp_playfield->align == VALIGN_BOTTOM)
13227 vp_playfield->border_right += playfield_ydiff;
13229 else if (vp_playfield->align == VALIGN_MIDDLE)
13231 int border_top_diff = playfield_ydiff / 2;
13232 int border_bottom_diff = playfield_ydiff - border_top_diff;
13234 vp_playfield->border_top += border_top_diff;
13235 vp_playfield->border_bottom += border_bottom_diff;
13239 // adjust door positions according to specified alignment
13241 for (j = 0; j < 2; j++)
13243 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13245 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13246 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13247 else if (vp_door->align == ALIGN_CENTER)
13248 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13249 else if (vp_door->align == ALIGN_RIGHT)
13250 vp_door->x += vp_window->width - vp_door->width;
13252 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13253 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13254 else if (vp_door->valign == VALIGN_MIDDLE)
13255 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13256 else if (vp_door->valign == VALIGN_BOTTOM)
13257 vp_door->y += vp_window->height - vp_door->height;
13262 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13266 struct XYTileSize *dst, *src;
13269 editor_buttons_xy[] =
13272 &editor.button.element_left, &editor.palette.element_left,
13273 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13276 &editor.button.element_middle, &editor.palette.element_middle,
13277 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13280 &editor.button.element_right, &editor.palette.element_right,
13281 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13288 // set default position for element buttons to element graphics
13289 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13291 if ((*editor_buttons_xy[i].dst).x == -1 &&
13292 (*editor_buttons_xy[i].dst).y == -1)
13294 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13296 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13298 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13302 // adjust editor palette rows and columns if specified to be dynamic
13304 if (editor.palette.cols == -1)
13306 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13307 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13308 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13310 editor.palette.cols = (vp_width - sc_width) / bt_width;
13312 if (editor.palette.x == -1)
13314 int palette_width = editor.palette.cols * bt_width + sc_width;
13316 editor.palette.x = (vp_width - palette_width) / 2;
13320 if (editor.palette.rows == -1)
13322 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13323 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13324 int tx_height = getFontHeight(FONT_TEXT_2);
13326 editor.palette.rows = (vp_height - tx_height) / bt_height;
13328 if (editor.palette.y == -1)
13330 int palette_height = editor.palette.rows * bt_height + tx_height;
13332 editor.palette.y = (vp_height - palette_height) / 2;
13337 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13338 boolean initialize)
13340 // special case: check if network and preview player positions are redefined,
13341 // to compare this later against the main menu level preview being redefined
13342 struct TokenIntPtrInfo menu_config_players[] =
13344 { "main.network_players.x", &menu.main.network_players.redefined },
13345 { "main.network_players.y", &menu.main.network_players.redefined },
13346 { "main.preview_players.x", &menu.main.preview_players.redefined },
13347 { "main.preview_players.y", &menu.main.preview_players.redefined },
13348 { "preview.x", &preview.redefined },
13349 { "preview.y", &preview.redefined }
13355 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13356 *menu_config_players[i].value = FALSE;
13360 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13361 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13362 *menu_config_players[i].value = TRUE;
13366 static void InitMenuDesignSettings_PreviewPlayers(void)
13368 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13371 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13373 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13376 static void LoadMenuDesignSettingsFromFilename(char *filename)
13378 static struct TitleFadingInfo tfi;
13379 static struct TitleMessageInfo tmi;
13380 static struct TokenInfo title_tokens[] =
13382 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13383 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13384 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13385 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13386 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13390 static struct TokenInfo titlemessage_tokens[] =
13392 { TYPE_INTEGER, &tmi.x, ".x" },
13393 { TYPE_INTEGER, &tmi.y, ".y" },
13394 { TYPE_INTEGER, &tmi.width, ".width" },
13395 { TYPE_INTEGER, &tmi.height, ".height" },
13396 { TYPE_INTEGER, &tmi.chars, ".chars" },
13397 { TYPE_INTEGER, &tmi.lines, ".lines" },
13398 { TYPE_INTEGER, &tmi.align, ".align" },
13399 { TYPE_INTEGER, &tmi.valign, ".valign" },
13400 { TYPE_INTEGER, &tmi.font, ".font" },
13401 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13402 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13403 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13404 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13405 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13406 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13407 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13408 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13409 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13415 struct TitleFadingInfo *info;
13420 // initialize first titles from "enter screen" definitions, if defined
13421 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13422 { &title_first_default, "menu.enter_screen.TITLE" },
13424 // initialize title screens from "next screen" definitions, if defined
13425 { &title_initial_default, "menu.next_screen.TITLE" },
13426 { &title_default, "menu.next_screen.TITLE" },
13432 struct TitleMessageInfo *array;
13435 titlemessage_arrays[] =
13437 // initialize first titles from "enter screen" definitions, if defined
13438 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13439 { titlescreen_first, "menu.enter_screen.TITLE" },
13440 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13441 { titlemessage_first, "menu.enter_screen.TITLE" },
13443 // initialize titles from "next screen" definitions, if defined
13444 { titlescreen_initial, "menu.next_screen.TITLE" },
13445 { titlescreen, "menu.next_screen.TITLE" },
13446 { titlemessage_initial, "menu.next_screen.TITLE" },
13447 { titlemessage, "menu.next_screen.TITLE" },
13449 // overwrite titles with title definitions, if defined
13450 { titlescreen_initial_first, "[title_initial]" },
13451 { titlescreen_first, "[title]" },
13452 { titlemessage_initial_first, "[title_initial]" },
13453 { titlemessage_first, "[title]" },
13455 { titlescreen_initial, "[title_initial]" },
13456 { titlescreen, "[title]" },
13457 { titlemessage_initial, "[title_initial]" },
13458 { titlemessage, "[title]" },
13460 // overwrite titles with title screen/message definitions, if defined
13461 { titlescreen_initial_first, "[titlescreen_initial]" },
13462 { titlescreen_first, "[titlescreen]" },
13463 { titlemessage_initial_first, "[titlemessage_initial]" },
13464 { titlemessage_first, "[titlemessage]" },
13466 { titlescreen_initial, "[titlescreen_initial]" },
13467 { titlescreen, "[titlescreen]" },
13468 { titlemessage_initial, "[titlemessage_initial]" },
13469 { titlemessage, "[titlemessage]" },
13473 SetupFileHash *setup_file_hash;
13476 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13479 // the following initializes hierarchical values from dynamic configuration
13481 // special case: initialize with default values that may be overwritten
13482 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13483 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13485 struct TokenIntPtrInfo menu_config[] =
13487 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13488 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13489 { "menu.list_size", &menu.list_size[i] }
13492 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13494 char *token = menu_config[j].token;
13495 char *value = getHashEntry(setup_file_hash, token);
13498 *menu_config[j].value = get_integer_from_string(value);
13502 // special case: initialize with default values that may be overwritten
13503 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13504 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13506 struct TokenIntPtrInfo menu_config[] =
13508 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13509 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13510 { "menu.list_size.INFO", &menu.list_size_info[i] },
13511 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13512 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13515 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13517 char *token = menu_config[j].token;
13518 char *value = getHashEntry(setup_file_hash, token);
13521 *menu_config[j].value = get_integer_from_string(value);
13525 // special case: initialize with default values that may be overwritten
13526 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13527 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13529 struct TokenIntPtrInfo menu_config[] =
13531 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13532 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13535 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13537 char *token = menu_config[j].token;
13538 char *value = getHashEntry(setup_file_hash, token);
13541 *menu_config[j].value = get_integer_from_string(value);
13545 // special case: initialize with default values that may be overwritten
13546 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13547 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13549 struct TokenIntPtrInfo menu_config[] =
13551 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13552 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13553 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13554 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13555 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13556 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13557 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13558 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13559 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13560 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13563 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13565 char *token = menu_config[j].token;
13566 char *value = getHashEntry(setup_file_hash, token);
13569 *menu_config[j].value = get_integer_from_string(value);
13573 // special case: initialize with default values that may be overwritten
13574 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13575 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13577 struct TokenIntPtrInfo menu_config[] =
13579 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13580 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13581 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13582 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13583 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13584 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13585 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13586 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13587 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13590 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13592 char *token = menu_config[j].token;
13593 char *value = getHashEntry(setup_file_hash, token);
13596 *menu_config[j].value = get_token_parameter_value(token, value);
13600 // special case: initialize with default values that may be overwritten
13601 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13602 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13606 char *token_prefix;
13607 struct RectWithBorder *struct_ptr;
13611 { "viewport.window", &viewport.window[i] },
13612 { "viewport.playfield", &viewport.playfield[i] },
13613 { "viewport.door_1", &viewport.door_1[i] },
13614 { "viewport.door_2", &viewport.door_2[i] }
13617 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13619 struct TokenIntPtrInfo vp_config[] =
13621 { ".x", &vp_struct[j].struct_ptr->x },
13622 { ".y", &vp_struct[j].struct_ptr->y },
13623 { ".width", &vp_struct[j].struct_ptr->width },
13624 { ".height", &vp_struct[j].struct_ptr->height },
13625 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13626 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13627 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13628 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13629 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13630 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13631 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13632 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13633 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13634 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13635 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13636 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13637 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13638 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13639 { ".align", &vp_struct[j].struct_ptr->align },
13640 { ".valign", &vp_struct[j].struct_ptr->valign }
13643 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13645 char *token = getStringCat2(vp_struct[j].token_prefix,
13646 vp_config[k].token);
13647 char *value = getHashEntry(setup_file_hash, token);
13650 *vp_config[k].value = get_token_parameter_value(token, value);
13657 // special case: initialize with default values that may be overwritten
13658 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13659 for (i = 0; title_info[i].info != NULL; i++)
13661 struct TitleFadingInfo *info = title_info[i].info;
13662 char *base_token = title_info[i].text;
13664 for (j = 0; title_tokens[j].type != -1; j++)
13666 char *token = getStringCat2(base_token, title_tokens[j].text);
13667 char *value = getHashEntry(setup_file_hash, token);
13671 int parameter_value = get_token_parameter_value(token, value);
13675 *(int *)title_tokens[j].value = (int)parameter_value;
13684 // special case: initialize with default values that may be overwritten
13685 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13686 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13688 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13689 char *base_token = titlemessage_arrays[i].text;
13691 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13693 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13694 char *value = getHashEntry(setup_file_hash, token);
13698 int parameter_value = get_token_parameter_value(token, value);
13700 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13704 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13705 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13707 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13717 // read (and overwrite with) values that may be specified in config file
13718 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13720 // special case: check if network and preview player positions are redefined
13721 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13723 freeSetupFileHash(setup_file_hash);
13726 void LoadMenuDesignSettings(void)
13728 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13730 InitMenuDesignSettings_Static();
13731 InitMenuDesignSettings_SpecialPreProcessing();
13732 InitMenuDesignSettings_PreviewPlayers();
13734 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13736 // first look for special settings configured in level series config
13737 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13739 if (fileExists(filename_base))
13740 LoadMenuDesignSettingsFromFilename(filename_base);
13743 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13745 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13746 LoadMenuDesignSettingsFromFilename(filename_local);
13748 InitMenuDesignSettings_SpecialPostProcessing();
13751 void LoadMenuDesignSettings_AfterGraphics(void)
13753 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13756 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13757 boolean ignore_defaults)
13761 for (i = 0; sound_config_vars[i].token != NULL; i++)
13763 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13765 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13766 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13770 *sound_config_vars[i].value =
13771 get_token_parameter_value(sound_config_vars[i].token, value);
13775 void InitSoundSettings_Static(void)
13777 // always start with reliable default values from static default config
13778 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13781 static void LoadSoundSettingsFromFilename(char *filename)
13783 SetupFileHash *setup_file_hash;
13785 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13788 // read (and overwrite with) values that may be specified in config file
13789 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13791 freeSetupFileHash(setup_file_hash);
13794 void LoadSoundSettings(void)
13796 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13798 InitSoundSettings_Static();
13800 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13802 // first look for special settings configured in level series config
13803 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13805 if (fileExists(filename_base))
13806 LoadSoundSettingsFromFilename(filename_base);
13809 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13811 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13812 LoadSoundSettingsFromFilename(filename_local);
13815 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13817 char *filename = getEditorSetupFilename();
13818 SetupFileList *setup_file_list, *list;
13819 SetupFileHash *element_hash;
13820 int num_unknown_tokens = 0;
13823 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13826 element_hash = newSetupFileHash();
13828 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13829 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13831 // determined size may be larger than needed (due to unknown elements)
13833 for (list = setup_file_list; list != NULL; list = list->next)
13836 // add space for up to 3 more elements for padding that may be needed
13837 *num_elements += 3;
13839 // free memory for old list of elements, if needed
13840 checked_free(*elements);
13842 // allocate memory for new list of elements
13843 *elements = checked_malloc(*num_elements * sizeof(int));
13846 for (list = setup_file_list; list != NULL; list = list->next)
13848 char *value = getHashEntry(element_hash, list->token);
13850 if (value == NULL) // try to find obsolete token mapping
13852 char *mapped_token = get_mapped_token(list->token);
13854 if (mapped_token != NULL)
13856 value = getHashEntry(element_hash, mapped_token);
13858 free(mapped_token);
13864 (*elements)[(*num_elements)++] = atoi(value);
13868 if (num_unknown_tokens == 0)
13871 Warn("unknown token(s) found in config file:");
13872 Warn("- config file: '%s'", filename);
13874 num_unknown_tokens++;
13877 Warn("- token: '%s'", list->token);
13881 if (num_unknown_tokens > 0)
13884 while (*num_elements % 4) // pad with empty elements, if needed
13885 (*elements)[(*num_elements)++] = EL_EMPTY;
13887 freeSetupFileList(setup_file_list);
13888 freeSetupFileHash(element_hash);
13891 for (i = 0; i < *num_elements; i++)
13892 Debug("editor", "element '%s' [%d]\n",
13893 element_info[(*elements)[i]].token_name, (*elements)[i]);
13897 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13900 SetupFileHash *setup_file_hash = NULL;
13901 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13902 char *filename_music, *filename_prefix, *filename_info;
13908 token_to_value_ptr[] =
13910 { "title_header", &tmp_music_file_info.title_header },
13911 { "artist_header", &tmp_music_file_info.artist_header },
13912 { "album_header", &tmp_music_file_info.album_header },
13913 { "year_header", &tmp_music_file_info.year_header },
13914 { "played_header", &tmp_music_file_info.played_header },
13916 { "title", &tmp_music_file_info.title },
13917 { "artist", &tmp_music_file_info.artist },
13918 { "album", &tmp_music_file_info.album },
13919 { "year", &tmp_music_file_info.year },
13920 { "played", &tmp_music_file_info.played },
13926 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13927 getCustomMusicFilename(basename));
13929 if (filename_music == NULL)
13932 // ---------- try to replace file extension ----------
13934 filename_prefix = getStringCopy(filename_music);
13935 if (strrchr(filename_prefix, '.') != NULL)
13936 *strrchr(filename_prefix, '.') = '\0';
13937 filename_info = getStringCat2(filename_prefix, ".txt");
13939 if (fileExists(filename_info))
13940 setup_file_hash = loadSetupFileHash(filename_info);
13942 free(filename_prefix);
13943 free(filename_info);
13945 if (setup_file_hash == NULL)
13947 // ---------- try to add file extension ----------
13949 filename_prefix = getStringCopy(filename_music);
13950 filename_info = getStringCat2(filename_prefix, ".txt");
13952 if (fileExists(filename_info))
13953 setup_file_hash = loadSetupFileHash(filename_info);
13955 free(filename_prefix);
13956 free(filename_info);
13959 if (setup_file_hash == NULL)
13962 // ---------- music file info found ----------
13964 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13966 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13968 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13970 *token_to_value_ptr[i].value_ptr =
13971 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13974 tmp_music_file_info.basename = getStringCopy(basename);
13975 tmp_music_file_info.music = music;
13976 tmp_music_file_info.is_sound = is_sound;
13978 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13979 *new_music_file_info = tmp_music_file_info;
13981 return new_music_file_info;
13984 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13986 return get_music_file_info_ext(basename, music, FALSE);
13989 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13991 return get_music_file_info_ext(basename, sound, TRUE);
13994 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13995 char *basename, boolean is_sound)
13997 for (; list != NULL; list = list->next)
13998 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14004 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14006 return music_info_listed_ext(list, basename, FALSE);
14009 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14011 return music_info_listed_ext(list, basename, TRUE);
14014 void LoadMusicInfo(void)
14016 int num_music_noconf = getMusicListSize_NoConf();
14017 int num_music = getMusicListSize();
14018 int num_sounds = getSoundListSize();
14019 struct FileInfo *music, *sound;
14020 struct MusicFileInfo *next, **new;
14024 while (music_file_info != NULL)
14026 next = music_file_info->next;
14028 checked_free(music_file_info->basename);
14030 checked_free(music_file_info->title_header);
14031 checked_free(music_file_info->artist_header);
14032 checked_free(music_file_info->album_header);
14033 checked_free(music_file_info->year_header);
14034 checked_free(music_file_info->played_header);
14036 checked_free(music_file_info->title);
14037 checked_free(music_file_info->artist);
14038 checked_free(music_file_info->album);
14039 checked_free(music_file_info->year);
14040 checked_free(music_file_info->played);
14042 free(music_file_info);
14044 music_file_info = next;
14047 new = &music_file_info;
14049 // get (configured or unconfigured) music file info for all levels
14050 for (i = leveldir_current->first_level;
14051 i <= leveldir_current->last_level; i++)
14055 if (levelset.music[i] != MUS_UNDEFINED)
14057 // get music file info for configured level music
14058 music_nr = levelset.music[i];
14060 else if (num_music_noconf > 0)
14062 // get music file info for unconfigured level music
14063 int level_pos = i - leveldir_current->first_level;
14065 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14072 char *basename = getMusicInfoEntryFilename(music_nr);
14074 if (basename == NULL)
14077 if (!music_info_listed(music_file_info, basename))
14079 *new = get_music_file_info(basename, music_nr);
14082 new = &(*new)->next;
14086 // get music file info for all remaining configured music files
14087 for (i = 0; i < num_music; i++)
14089 music = getMusicListEntry(i);
14091 if (music->filename == NULL)
14094 if (strEqual(music->filename, UNDEFINED_FILENAME))
14097 // a configured file may be not recognized as music
14098 if (!FileIsMusic(music->filename))
14101 if (!music_info_listed(music_file_info, music->filename))
14103 *new = get_music_file_info(music->filename, i);
14106 new = &(*new)->next;
14110 // get sound file info for all configured sound files
14111 for (i = 0; i < num_sounds; i++)
14113 sound = getSoundListEntry(i);
14115 if (sound->filename == NULL)
14118 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14121 // a configured file may be not recognized as sound
14122 if (!FileIsSound(sound->filename))
14125 if (!sound_info_listed(music_file_info, sound->filename))
14127 *new = get_sound_file_info(sound->filename, i);
14129 new = &(*new)->next;
14133 // add pointers to previous list nodes
14135 struct MusicFileInfo *node = music_file_info;
14137 while (node != NULL)
14140 node->next->prev = node;
14146 static void add_helpanim_entry(int element, int action, int direction,
14147 int delay, int *num_list_entries)
14149 struct HelpAnimInfo *new_list_entry;
14150 (*num_list_entries)++;
14153 checked_realloc(helpanim_info,
14154 *num_list_entries * sizeof(struct HelpAnimInfo));
14155 new_list_entry = &helpanim_info[*num_list_entries - 1];
14157 new_list_entry->element = element;
14158 new_list_entry->action = action;
14159 new_list_entry->direction = direction;
14160 new_list_entry->delay = delay;
14163 static void print_unknown_token(char *filename, char *token, int token_nr)
14168 Warn("unknown token(s) found in config file:");
14169 Warn("- config file: '%s'", filename);
14172 Warn("- token: '%s'", token);
14175 static void print_unknown_token_end(int token_nr)
14181 void LoadHelpAnimInfo(void)
14183 char *filename = getHelpAnimFilename();
14184 SetupFileList *setup_file_list = NULL, *list;
14185 SetupFileHash *element_hash, *action_hash, *direction_hash;
14186 int num_list_entries = 0;
14187 int num_unknown_tokens = 0;
14190 if (fileExists(filename))
14191 setup_file_list = loadSetupFileList(filename);
14193 if (setup_file_list == NULL)
14195 // use reliable default values from static configuration
14196 SetupFileList *insert_ptr;
14198 insert_ptr = setup_file_list =
14199 newSetupFileList(helpanim_config[0].token,
14200 helpanim_config[0].value);
14202 for (i = 1; helpanim_config[i].token; i++)
14203 insert_ptr = addListEntry(insert_ptr,
14204 helpanim_config[i].token,
14205 helpanim_config[i].value);
14208 element_hash = newSetupFileHash();
14209 action_hash = newSetupFileHash();
14210 direction_hash = newSetupFileHash();
14212 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14213 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14215 for (i = 0; i < NUM_ACTIONS; i++)
14216 setHashEntry(action_hash, element_action_info[i].suffix,
14217 i_to_a(element_action_info[i].value));
14219 // do not store direction index (bit) here, but direction value!
14220 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14221 setHashEntry(direction_hash, element_direction_info[i].suffix,
14222 i_to_a(1 << element_direction_info[i].value));
14224 for (list = setup_file_list; list != NULL; list = list->next)
14226 char *element_token, *action_token, *direction_token;
14227 char *element_value, *action_value, *direction_value;
14228 int delay = atoi(list->value);
14230 if (strEqual(list->token, "end"))
14232 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14237 /* first try to break element into element/action/direction parts;
14238 if this does not work, also accept combined "element[.act][.dir]"
14239 elements (like "dynamite.active"), which are unique elements */
14241 if (strchr(list->token, '.') == NULL) // token contains no '.'
14243 element_value = getHashEntry(element_hash, list->token);
14244 if (element_value != NULL) // element found
14245 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14246 &num_list_entries);
14249 // no further suffixes found -- this is not an element
14250 print_unknown_token(filename, list->token, num_unknown_tokens++);
14256 // token has format "<prefix>.<something>"
14258 action_token = strchr(list->token, '.'); // suffix may be action ...
14259 direction_token = action_token; // ... or direction
14261 element_token = getStringCopy(list->token);
14262 *strchr(element_token, '.') = '\0';
14264 element_value = getHashEntry(element_hash, element_token);
14266 if (element_value == NULL) // this is no element
14268 element_value = getHashEntry(element_hash, list->token);
14269 if (element_value != NULL) // combined element found
14270 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14271 &num_list_entries);
14273 print_unknown_token(filename, list->token, num_unknown_tokens++);
14275 free(element_token);
14280 action_value = getHashEntry(action_hash, action_token);
14282 if (action_value != NULL) // action found
14284 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14285 &num_list_entries);
14287 free(element_token);
14292 direction_value = getHashEntry(direction_hash, direction_token);
14294 if (direction_value != NULL) // direction found
14296 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14297 &num_list_entries);
14299 free(element_token);
14304 if (strchr(action_token + 1, '.') == NULL)
14306 // no further suffixes found -- this is not an action nor direction
14308 element_value = getHashEntry(element_hash, list->token);
14309 if (element_value != NULL) // combined element found
14310 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14311 &num_list_entries);
14313 print_unknown_token(filename, list->token, num_unknown_tokens++);
14315 free(element_token);
14320 // token has format "<prefix>.<suffix>.<something>"
14322 direction_token = strchr(action_token + 1, '.');
14324 action_token = getStringCopy(action_token);
14325 *strchr(action_token + 1, '.') = '\0';
14327 action_value = getHashEntry(action_hash, action_token);
14329 if (action_value == NULL) // this is no action
14331 element_value = getHashEntry(element_hash, list->token);
14332 if (element_value != NULL) // combined element found
14333 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14334 &num_list_entries);
14336 print_unknown_token(filename, list->token, num_unknown_tokens++);
14338 free(element_token);
14339 free(action_token);
14344 direction_value = getHashEntry(direction_hash, direction_token);
14346 if (direction_value != NULL) // direction found
14348 add_helpanim_entry(atoi(element_value), atoi(action_value),
14349 atoi(direction_value), delay, &num_list_entries);
14351 free(element_token);
14352 free(action_token);
14357 // this is no direction
14359 element_value = getHashEntry(element_hash, list->token);
14360 if (element_value != NULL) // combined element found
14361 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14362 &num_list_entries);
14364 print_unknown_token(filename, list->token, num_unknown_tokens++);
14366 free(element_token);
14367 free(action_token);
14370 print_unknown_token_end(num_unknown_tokens);
14372 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14373 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14375 freeSetupFileList(setup_file_list);
14376 freeSetupFileHash(element_hash);
14377 freeSetupFileHash(action_hash);
14378 freeSetupFileHash(direction_hash);
14381 for (i = 0; i < num_list_entries; i++)
14382 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14383 EL_NAME(helpanim_info[i].element),
14384 helpanim_info[i].element,
14385 helpanim_info[i].action,
14386 helpanim_info[i].direction,
14387 helpanim_info[i].delay);
14391 void LoadHelpTextInfo(void)
14393 char *filename = getHelpTextFilename();
14396 if (helptext_info != NULL)
14398 freeSetupFileHash(helptext_info);
14399 helptext_info = NULL;
14402 if (fileExists(filename))
14403 helptext_info = loadSetupFileHash(filename);
14405 if (helptext_info == NULL)
14407 // use reliable default values from static configuration
14408 helptext_info = newSetupFileHash();
14410 for (i = 0; helptext_config[i].token; i++)
14411 setHashEntry(helptext_info,
14412 helptext_config[i].token,
14413 helptext_config[i].value);
14417 BEGIN_HASH_ITERATION(helptext_info, itr)
14419 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14420 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14422 END_HASH_ITERATION(hash, itr)
14427 // ----------------------------------------------------------------------------
14429 // ----------------------------------------------------------------------------
14431 #define MAX_NUM_CONVERT_LEVELS 1000
14433 void ConvertLevels(void)
14435 static LevelDirTree *convert_leveldir = NULL;
14436 static int convert_level_nr = -1;
14437 static int num_levels_handled = 0;
14438 static int num_levels_converted = 0;
14439 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14442 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14443 global.convert_leveldir);
14445 if (convert_leveldir == NULL)
14446 Fail("no such level identifier: '%s'", global.convert_leveldir);
14448 leveldir_current = convert_leveldir;
14450 if (global.convert_level_nr != -1)
14452 convert_leveldir->first_level = global.convert_level_nr;
14453 convert_leveldir->last_level = global.convert_level_nr;
14456 convert_level_nr = convert_leveldir->first_level;
14458 PrintLine("=", 79);
14459 Print("Converting levels\n");
14460 PrintLine("-", 79);
14461 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14462 Print("Level series name: '%s'\n", convert_leveldir->name);
14463 Print("Level series author: '%s'\n", convert_leveldir->author);
14464 Print("Number of levels: %d\n", convert_leveldir->levels);
14465 PrintLine("=", 79);
14468 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14469 levels_failed[i] = FALSE;
14471 while (convert_level_nr <= convert_leveldir->last_level)
14473 char *level_filename;
14476 level_nr = convert_level_nr++;
14478 Print("Level %03d: ", level_nr);
14480 LoadLevel(level_nr);
14481 if (level.no_level_file || level.no_valid_file)
14483 Print("(no level)\n");
14487 Print("converting level ... ");
14490 // special case: conversion of some EMC levels as requested by ACME
14491 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14494 level_filename = getDefaultLevelFilename(level_nr);
14495 new_level = !fileExists(level_filename);
14499 SaveLevel(level_nr);
14501 num_levels_converted++;
14503 Print("converted.\n");
14507 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14508 levels_failed[level_nr] = TRUE;
14510 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14513 num_levels_handled++;
14517 PrintLine("=", 79);
14518 Print("Number of levels handled: %d\n", num_levels_handled);
14519 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14520 (num_levels_handled ?
14521 num_levels_converted * 100 / num_levels_handled : 0));
14522 PrintLine("-", 79);
14523 Print("Summary (for automatic parsing by scripts):\n");
14524 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14525 convert_leveldir->identifier, num_levels_converted,
14526 num_levels_handled,
14527 (num_levels_handled ?
14528 num_levels_converted * 100 / num_levels_handled : 0));
14530 if (num_levels_handled != num_levels_converted)
14532 Print(", FAILED:");
14533 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14534 if (levels_failed[i])
14539 PrintLine("=", 79);
14541 CloseAllAndExit(0);
14545 // ----------------------------------------------------------------------------
14546 // create and save images for use in level sketches (raw BMP format)
14547 // ----------------------------------------------------------------------------
14549 void CreateLevelSketchImages(void)
14555 InitElementPropertiesGfxElement();
14557 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14558 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14560 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14562 int element = getMappedElement(i);
14563 char basename1[16];
14564 char basename2[16];
14568 sprintf(basename1, "%04d.bmp", i);
14569 sprintf(basename2, "%04ds.bmp", i);
14571 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14572 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14574 DrawSizedElement(0, 0, element, TILESIZE);
14575 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14577 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14578 Fail("cannot save level sketch image file '%s'", filename1);
14580 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14581 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14583 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14584 Fail("cannot save level sketch image file '%s'", filename2);
14589 // create corresponding SQL statements (for normal and small images)
14592 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14593 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14596 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14597 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14599 // optional: create content for forum level sketch demonstration post
14601 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14604 FreeBitmap(bitmap1);
14605 FreeBitmap(bitmap2);
14608 fprintf(stderr, "\n");
14610 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14612 CloseAllAndExit(0);
14616 // ----------------------------------------------------------------------------
14617 // create and save images for element collecting animations (raw BMP format)
14618 // ----------------------------------------------------------------------------
14620 static boolean createCollectImage(int element)
14622 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14625 void CreateCollectElementImages(void)
14629 int anim_frames = num_steps - 1;
14630 int tile_size = TILESIZE;
14631 int anim_width = tile_size * anim_frames;
14632 int anim_height = tile_size;
14633 int num_collect_images = 0;
14634 int pos_collect_images = 0;
14636 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14637 if (createCollectImage(i))
14638 num_collect_images++;
14640 Info("Creating %d element collecting animation images ...",
14641 num_collect_images);
14643 int dst_width = anim_width * 2;
14644 int dst_height = anim_height * num_collect_images / 2;
14645 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14646 char *basename_bmp = "RocksCollect.bmp";
14647 char *basename_png = "RocksCollect.png";
14648 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14649 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14650 int len_filename_bmp = strlen(filename_bmp);
14651 int len_filename_png = strlen(filename_png);
14652 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14653 char cmd_convert[max_command_len];
14655 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14659 // force using RGBA surface for destination bitmap
14660 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14661 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14663 dst_bitmap->surface =
14664 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14666 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14668 if (!createCollectImage(i))
14671 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14672 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14673 int graphic = el2img(i);
14674 char *token_name = element_info[i].token_name;
14675 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14676 Bitmap *src_bitmap;
14679 Info("- creating collecting image for '%s' ...", token_name);
14681 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14683 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14684 tile_size, tile_size, 0, 0);
14686 // force using RGBA surface for temporary bitmap (using transparent black)
14687 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14688 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14690 tmp_bitmap->surface =
14691 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14693 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14695 for (j = 0; j < anim_frames; j++)
14697 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14698 int frame_size = frame_size_final * num_steps;
14699 int offset = (tile_size - frame_size_final) / 2;
14700 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14702 while (frame_size > frame_size_final)
14706 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14708 FreeBitmap(frame_bitmap);
14710 frame_bitmap = half_bitmap;
14713 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14714 frame_size_final, frame_size_final,
14715 dst_x + j * tile_size + offset, dst_y + offset);
14717 FreeBitmap(frame_bitmap);
14720 tmp_bitmap->surface_masked = NULL;
14722 FreeBitmap(tmp_bitmap);
14724 pos_collect_images++;
14727 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14728 Fail("cannot save element collecting image file '%s'", filename_bmp);
14730 FreeBitmap(dst_bitmap);
14732 Info("Converting image file from BMP to PNG ...");
14734 if (system(cmd_convert) != 0)
14735 Fail("converting image file failed");
14737 unlink(filename_bmp);
14741 CloseAllAndExit(0);
14745 // ----------------------------------------------------------------------------
14746 // create and save images for custom and group elements (raw BMP format)
14747 // ----------------------------------------------------------------------------
14749 void CreateCustomElementImages(char *directory)
14751 char *src_basename = "RocksCE-template.ilbm";
14752 char *dst_basename = "RocksCE.bmp";
14753 char *src_filename = getPath2(directory, src_basename);
14754 char *dst_filename = getPath2(directory, dst_basename);
14755 Bitmap *src_bitmap;
14757 int yoffset_ce = 0;
14758 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14761 InitVideoDefaults();
14763 ReCreateBitmap(&backbuffer, video.width, video.height);
14765 src_bitmap = LoadImage(src_filename);
14767 bitmap = CreateBitmap(TILEX * 16 * 2,
14768 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14771 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14778 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14779 TILEX * x, TILEY * y + yoffset_ce);
14781 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14783 TILEX * x + TILEX * 16,
14784 TILEY * y + yoffset_ce);
14786 for (j = 2; j >= 0; j--)
14790 BlitBitmap(src_bitmap, bitmap,
14791 TILEX + c * 7, 0, 6, 10,
14792 TILEX * x + 6 + j * 7,
14793 TILEY * y + 11 + yoffset_ce);
14795 BlitBitmap(src_bitmap, bitmap,
14796 TILEX + c * 8, TILEY, 6, 10,
14797 TILEX * 16 + TILEX * x + 6 + j * 8,
14798 TILEY * y + 10 + yoffset_ce);
14804 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14811 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14812 TILEX * x, TILEY * y + yoffset_ge);
14814 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14816 TILEX * x + TILEX * 16,
14817 TILEY * y + yoffset_ge);
14819 for (j = 1; j >= 0; j--)
14823 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14824 TILEX * x + 6 + j * 10,
14825 TILEY * y + 11 + yoffset_ge);
14827 BlitBitmap(src_bitmap, bitmap,
14828 TILEX + c * 8, TILEY + 12, 6, 10,
14829 TILEX * 16 + TILEX * x + 10 + j * 8,
14830 TILEY * y + 10 + yoffset_ge);
14836 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14837 Fail("cannot save CE graphics file '%s'", dst_filename);
14839 FreeBitmap(bitmap);
14841 CloseAllAndExit(0);