1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_INTEGER, CONF_VALUE_8_BIT(23),
311 &li.bd_cave_random_seed_c64, 0
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
605 // (some values for BD style amoeba following below)
608 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
609 &li.bd_diagonal_movements, FALSE
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
614 &li.bd_topmost_player_active, TRUE
618 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
619 &li.bd_pushing_prob, 25
623 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
624 &li.bd_pushing_prob_with_sweet, 100
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
629 &li.bd_push_mega_rock_with_sweet, FALSE
633 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
634 &li.bd_snap_element, EL_EMPTY
639 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
640 &li.score[SC_DIAMOND_EXTRA], 20
644 EL_BD_MAGIC_WALL, -1,
645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
646 &li.bd_magic_wall_wait_hatching, FALSE
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
651 &li.bd_magic_wall_stops_amoeba, TRUE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
656 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
659 EL_BD_MAGIC_WALL, -1,
660 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
661 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
664 EL_BD_MAGIC_WALL, -1,
665 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
666 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
669 EL_BD_MAGIC_WALL, -1,
670 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
671 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
674 EL_BD_MAGIC_WALL, -1,
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
676 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
679 EL_BD_MAGIC_WALL, -1,
680 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
681 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
684 EL_BD_MAGIC_WALL, -1,
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
686 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
691 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
692 &li.bd_clock_extra_time, 30
696 EL_BD_VOODOO_DOLL, -1,
697 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
698 &li.bd_voodoo_collects_diamonds, FALSE
701 EL_BD_VOODOO_DOLL, -1,
702 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
703 &li.bd_voodoo_hurt_kills_player, FALSE
706 EL_BD_VOODOO_DOLL, -1,
707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
708 &li.bd_voodoo_dies_by_rock, FALSE
711 EL_BD_VOODOO_DOLL, -1,
712 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
713 &li.bd_voodoo_vanish_by_explosion, TRUE
716 EL_BD_VOODOO_DOLL, -1,
717 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
718 &li.bd_voodoo_penalty_time, 30
723 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
724 &li.bd_slime_is_predictable, TRUE
728 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
729 &li.bd_slime_permeability_rate, 100
733 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
734 &li.bd_slime_permeability_bits_c64, 0
738 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
739 &li.bd_slime_random_seed_c64, -1
743 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
744 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
748 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
749 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
753 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
754 &li.bd_slime_eats_element_2, EL_BD_ROCK
758 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
759 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
763 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
764 &li.bd_slime_eats_element_3, EL_BD_NUT
768 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
769 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
774 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
775 &li.bd_acid_eats_element, EL_BD_SAND
779 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
780 &li.bd_acid_spread_rate, 3
784 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
785 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
790 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
791 &li.bd_biter_move_delay, 0
795 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
796 &li.bd_biter_eats_element, EL_BD_DIAMOND
801 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
802 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
806 EL_BD_EXPANDABLE_WALL_ANY, -1,
807 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
808 &li.bd_change_expanding_wall, FALSE
811 EL_BD_EXPANDABLE_WALL_ANY, -1,
812 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
813 &li.bd_expanding_wall_looks_like, EL_BD_WALL
817 EL_BD_REPLICATOR, -1,
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
819 &li.bd_replicators_active, TRUE
822 EL_BD_REPLICATOR, -1,
823 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
824 &li.bd_replicator_create_delay, 4
828 EL_BD_CONVEYOR_LEFT, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
830 &li.bd_conveyor_belts_active, TRUE
833 EL_BD_CONVEYOR_LEFT, -1,
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
835 &li.bd_conveyor_belts_changed, FALSE
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
841 &li.bd_water_cannot_flow_down, FALSE
846 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
847 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
851 EL_BD_PNEUMATIC_HAMMER, -1,
852 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
853 &li.bd_hammer_walls_break_delay, 5
856 EL_BD_PNEUMATIC_HAMMER, -1,
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
858 &li.bd_hammer_walls_reappear, FALSE
861 EL_BD_PNEUMATIC_HAMMER, -1,
862 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
863 &li.bd_hammer_walls_reappear_delay, 100
868 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
869 &li.bd_num_skeletons_needed_for_pot, 5
873 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
874 &li.bd_skeleton_worth_num_diamonds, 0
878 EL_BD_CREATURE_SWITCH, -1,
879 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
880 &li.bd_creatures_start_backwards, FALSE
883 EL_BD_CREATURE_SWITCH, -1,
884 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
885 &li.bd_creatures_turn_on_hatching, FALSE
888 EL_BD_CREATURE_SWITCH, -1,
889 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
890 &li.bd_creatures_auto_turn_delay, 0
894 EL_BD_GRAVITY_SWITCH, -1,
895 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
896 &li.bd_gravity_direction, GD_MV_DOWN
899 EL_BD_GRAVITY_SWITCH, -1,
900 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
901 &li.bd_gravity_switch_active, FALSE
904 EL_BD_GRAVITY_SWITCH, -1,
905 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
906 &li.bd_gravity_switch_delay, 10
909 EL_BD_GRAVITY_SWITCH, -1,
910 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
911 &li.bd_gravity_affects_all, TRUE
916 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
917 &li.bd_sand_looks_like, EL_BD_SAND
920 // (the following values are related to various game elements)
924 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
925 &li.score[SC_EMERALD], 10
930 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
931 &li.score[SC_DIAMOND], 10
936 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
937 &li.score[SC_BUG], 10
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_SPACESHIP], 10
948 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
949 &li.score[SC_PACMAN], 10
954 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
955 &li.score[SC_NUT], 10
960 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
961 &li.score[SC_DYNAMITE], 10
966 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
967 &li.score[SC_KEY], 10
972 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
973 &li.score[SC_PEARL], 10
978 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
979 &li.score[SC_CRYSTAL], 10
982 // (amoeba values used by R'n'D game engine only)
985 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
986 &li.amoeba_content, EL_DIAMOND
990 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
996 &li.grow_into_diggable, TRUE
998 // (amoeba values used by BD game engine only)
1001 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1002 &li.bd_amoeba_wait_for_hatching, FALSE
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1007 &li.bd_amoeba_start_immediately, TRUE
1011 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1012 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1016 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1017 &li.bd_amoeba_threshold_too_big, 200
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1022 &li.bd_amoeba_slow_growth_time, 200
1026 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1027 &li.bd_amoeba_slow_growth_rate, 3
1031 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1032 &li.bd_amoeba_fast_growth_rate, 25
1036 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1037 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1041 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1042 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1048 &li.bd_amoeba_2_threshold_too_big, 200
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1053 &li.bd_amoeba_2_slow_growth_time, 200
1057 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1058 &li.bd_amoeba_2_slow_growth_rate, 3
1062 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1063 &li.bd_amoeba_2_fast_growth_rate, 25
1067 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1068 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1072 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1073 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1077 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1078 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1082 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1083 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1088 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1089 &li.yamyam_content, EL_ROCK, NULL,
1090 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1095 &li.score[SC_YAMYAM], 10
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1101 &li.score[SC_ROBOT], 10
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1118 &li.time_magic_wall, 10
1122 EL_GAME_OF_LIFE, -1,
1123 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1124 &li.game_of_life[0], 2
1127 EL_GAME_OF_LIFE, -1,
1128 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1129 &li.game_of_life[1], 3
1132 EL_GAME_OF_LIFE, -1,
1133 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1134 &li.game_of_life[2], 3
1137 EL_GAME_OF_LIFE, -1,
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1139 &li.game_of_life[3], 3
1142 EL_GAME_OF_LIFE, -1,
1143 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1144 &li.use_life_bugs, FALSE
1149 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1154 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1159 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1164 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1169 EL_TIMEGATE_SWITCH, -1,
1170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1171 &li.time_timegate, 10
1175 EL_LIGHT_SWITCH_ACTIVE, -1,
1176 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1181 EL_SHIELD_NORMAL, -1,
1182 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1183 &li.shield_normal_time, 10
1186 EL_SHIELD_NORMAL, -1,
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1188 &li.score[SC_SHIELD], 10
1192 EL_SHIELD_DEADLY, -1,
1193 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1194 &li.shield_deadly_time, 10
1197 EL_SHIELD_DEADLY, -1,
1198 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1199 &li.score[SC_SHIELD], 10
1204 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1209 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1210 &li.extra_time_score, 10
1214 EL_TIME_ORB_FULL, -1,
1215 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1216 &li.time_orb_time, 10
1219 EL_TIME_ORB_FULL, -1,
1220 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1221 &li.use_time_orb_bug, FALSE
1226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1227 &li.use_spring_bug, FALSE
1232 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1233 &li.android_move_time, 10
1237 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1238 &li.android_clone_time, 10
1241 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1242 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1243 &li.android_clone_element[0], EL_EMPTY, NULL,
1244 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1248 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1249 &li.android_clone_element[0], EL_EMPTY, NULL,
1250 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1255 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1256 &li.lenses_score, 10
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1265 EL_EMC_MAGNIFIER, -1,
1266 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1267 &li.magnify_score, 10
1270 EL_EMC_MAGNIFIER, -1,
1271 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1272 &li.magnify_time, 10
1276 EL_EMC_MAGIC_BALL, -1,
1277 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1281 EL_EMC_MAGIC_BALL, -1,
1282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1283 &li.ball_random, FALSE
1286 EL_EMC_MAGIC_BALL, -1,
1287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1288 &li.ball_active_initial, FALSE
1291 EL_EMC_MAGIC_BALL, -1,
1292 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1293 &li.ball_content, EL_EMPTY, NULL,
1294 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1298 EL_SOKOBAN_FIELD_EMPTY, -1,
1299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1300 &li.sb_fields_needed, TRUE
1304 EL_SOKOBAN_OBJECT, -1,
1305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1306 &li.sb_objects_needed, TRUE
1311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1312 &li.mm_laser_red, FALSE
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1317 &li.mm_laser_green, FALSE
1321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1322 &li.mm_laser_blue, TRUE
1327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1328 &li.df_laser_red, TRUE
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1333 &li.df_laser_green, TRUE
1337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1338 &li.df_laser_blue, FALSE
1342 EL_MM_FUSE_ACTIVE, -1,
1343 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1344 &li.mm_time_fuse, 25
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1349 &li.mm_time_bomb, 75
1353 EL_MM_GRAY_BALL, -1,
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1355 &li.mm_time_ball, 75
1358 EL_MM_GRAY_BALL, -1,
1359 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1360 &li.mm_ball_choice_mode, ANIM_RANDOM
1363 EL_MM_GRAY_BALL, -1,
1364 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1365 &li.mm_ball_content, EL_EMPTY, NULL,
1366 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1369 EL_MM_GRAY_BALL, -1,
1370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1371 &li.rotate_mm_ball_content, TRUE
1374 EL_MM_GRAY_BALL, -1,
1375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1376 &li.explode_mm_ball, FALSE
1380 EL_MM_STEEL_BLOCK, -1,
1381 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1382 &li.mm_time_block, 75
1385 EL_MM_LIGHTBALL, -1,
1386 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1387 &li.score[SC_ELEM_BONUS], 10
1397 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1401 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1402 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1406 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1407 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1412 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1413 &xx_envelope.autowrap, FALSE
1417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1418 &xx_envelope.centered, FALSE
1423 TYPE_STRING, CONF_VALUE_BYTES(1),
1424 &xx_envelope.text, -1, NULL,
1425 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1426 &xx_default_string_empty[0]
1436 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1440 TYPE_STRING, CONF_VALUE_BYTES(1),
1441 &xx_ei.description[0], -1,
1442 &yy_ei.description[0],
1443 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1444 &xx_default_description[0]
1449 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1450 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1451 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1453 #if ENABLE_RESERVED_CODE
1454 // (reserved for later use)
1457 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1458 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1459 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1466 &xx_ei.use_gfx_element, FALSE,
1467 &yy_ei.use_gfx_element
1471 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1472 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1473 &yy_ei.gfx_element_initial
1478 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1479 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1480 &yy_ei.access_direction
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1486 &xx_ei.collect_score_initial, 10,
1487 &yy_ei.collect_score_initial
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1492 &xx_ei.collect_count_initial, 1,
1493 &yy_ei.collect_count_initial
1498 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1499 &xx_ei.ce_value_fixed_initial, 0,
1500 &yy_ei.ce_value_fixed_initial
1504 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1505 &xx_ei.ce_value_random_initial, 0,
1506 &yy_ei.ce_value_random_initial
1510 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1511 &xx_ei.use_last_ce_value, FALSE,
1512 &yy_ei.use_last_ce_value
1517 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1518 &xx_ei.push_delay_fixed, 8,
1519 &yy_ei.push_delay_fixed
1523 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1524 &xx_ei.push_delay_random, 8,
1525 &yy_ei.push_delay_random
1529 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1530 &xx_ei.drop_delay_fixed, 0,
1531 &yy_ei.drop_delay_fixed
1535 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1536 &xx_ei.drop_delay_random, 0,
1537 &yy_ei.drop_delay_random
1541 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1542 &xx_ei.move_delay_fixed, 0,
1543 &yy_ei.move_delay_fixed
1547 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1548 &xx_ei.move_delay_random, 0,
1549 &yy_ei.move_delay_random
1553 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1554 &xx_ei.step_delay_fixed, 0,
1555 &yy_ei.step_delay_fixed
1559 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1560 &xx_ei.step_delay_random, 0,
1561 &yy_ei.step_delay_random
1566 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1567 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1572 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1573 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1574 &yy_ei.move_direction_initial
1578 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1579 &xx_ei.move_stepsize, TILEX / 8,
1580 &yy_ei.move_stepsize
1585 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1586 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1587 &yy_ei.move_enter_element
1591 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1592 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1593 &yy_ei.move_leave_element
1597 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1598 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1599 &yy_ei.move_leave_type
1604 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1605 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1606 &yy_ei.slippery_type
1611 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1612 &xx_ei.explosion_type, EXPLODES_3X3,
1613 &yy_ei.explosion_type
1617 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1618 &xx_ei.explosion_delay, 16,
1619 &yy_ei.explosion_delay
1623 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1624 &xx_ei.ignition_delay, 8,
1625 &yy_ei.ignition_delay
1630 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1631 &xx_ei.content, EL_EMPTY_SPACE,
1633 &xx_num_contents, 1, 1
1636 // ---------- "num_change_pages" must be the last entry ---------------------
1639 -1, SAVE_CONF_ALWAYS,
1640 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1641 &xx_ei.num_change_pages, 1,
1642 &yy_ei.num_change_pages
1653 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1655 // ---------- "current_change_page" must be the first entry -----------------
1658 -1, SAVE_CONF_ALWAYS,
1659 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1660 &xx_current_change_page, -1
1663 // ---------- (the remaining entries can be in any order) -------------------
1667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1668 &xx_change.can_change, FALSE
1673 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1674 &xx_event_bits[0], 0
1678 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1679 &xx_event_bits[1], 0
1684 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1685 &xx_change.trigger_player, CH_PLAYER_ANY
1689 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1690 &xx_change.trigger_side, CH_SIDE_ANY
1694 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1695 &xx_change.trigger_page, CH_PAGE_ANY
1700 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1701 &xx_change.target_element, EL_EMPTY_SPACE
1706 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1707 &xx_change.delay_fixed, 0
1711 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1712 &xx_change.delay_random, 0
1716 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1717 &xx_change.delay_frames, FRAMES_PER_SECOND
1722 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1723 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1728 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1729 &xx_change.explode, FALSE
1733 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1734 &xx_change.use_target_content, FALSE
1738 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1739 &xx_change.only_if_complete, FALSE
1743 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1744 &xx_change.use_random_replace, FALSE
1748 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1749 &xx_change.random_percentage, 100
1753 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1754 &xx_change.replace_when, CP_WHEN_EMPTY
1759 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1760 &xx_change.has_action, FALSE
1764 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1765 &xx_change.action_type, CA_NO_ACTION
1769 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1770 &xx_change.action_mode, CA_MODE_UNDEFINED
1774 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1775 &xx_change.action_arg, CA_ARG_UNDEFINED
1780 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1781 &xx_change.action_element, EL_EMPTY_SPACE
1786 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1787 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1788 &xx_num_contents, 1, 1
1798 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1802 TYPE_STRING, CONF_VALUE_BYTES(1),
1803 &xx_ei.description[0], -1, NULL,
1804 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1805 &xx_default_description[0]
1810 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1811 &xx_ei.use_gfx_element, FALSE
1815 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1816 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1821 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1822 &xx_group.choice_mode, ANIM_RANDOM
1827 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1828 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1829 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1839 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1844 &xx_ei.use_gfx_element, FALSE
1848 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1849 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1859 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1864 &li.block_snap_field, TRUE
1868 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1869 &li.continuous_snapping, TRUE
1873 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1874 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1878 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1879 &li.use_start_element[0], FALSE
1883 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1884 &li.start_element[0], EL_PLAYER_1
1888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1889 &li.use_artwork_element[0], FALSE
1893 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1894 &li.artwork_element[0], EL_PLAYER_1
1898 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1899 &li.use_explosion_element[0], FALSE
1903 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1904 &li.explosion_element[0], EL_PLAYER_1
1919 filetype_id_list[] =
1921 { LEVEL_FILE_TYPE_RND, "RND" },
1922 { LEVEL_FILE_TYPE_BD, "BD" },
1923 { LEVEL_FILE_TYPE_EM, "EM" },
1924 { LEVEL_FILE_TYPE_SP, "SP" },
1925 { LEVEL_FILE_TYPE_DX, "DX" },
1926 { LEVEL_FILE_TYPE_SB, "SB" },
1927 { LEVEL_FILE_TYPE_DC, "DC" },
1928 { LEVEL_FILE_TYPE_MM, "MM" },
1929 { LEVEL_FILE_TYPE_MM, "DF" },
1934 // ============================================================================
1935 // level file functions
1936 // ============================================================================
1938 static boolean check_special_flags(char *flag)
1940 if (strEqual(options.special_flags, flag) ||
1941 strEqual(leveldir_current->special_flags, flag))
1947 static struct DateInfo getCurrentDate(void)
1949 time_t epoch_seconds = time(NULL);
1950 struct tm *now = localtime(&epoch_seconds);
1951 struct DateInfo date;
1953 date.year = now->tm_year + 1900;
1954 date.month = now->tm_mon + 1;
1955 date.day = now->tm_mday;
1957 date.src = DATE_SRC_CLOCK;
1962 static void resetEventFlags(struct ElementChangeInfo *change)
1966 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1967 change->has_event[i] = FALSE;
1970 static void resetEventBits(void)
1974 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1975 xx_event_bits[i] = 0;
1978 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1982 /* important: only change event flag if corresponding event bit is set
1983 (this is because all xx_event_bits[] values are loaded separately,
1984 and all xx_event_bits[] values are set back to zero before loading
1985 another value xx_event_bits[x] (each value representing 32 flags)) */
1987 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1988 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1989 change->has_event[i] = TRUE;
1992 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1996 /* in contrast to the above function setEventFlagsFromEventBits(), it
1997 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1998 depending on the corresponding change->has_event[i] values here, as
1999 all xx_event_bits[] values are reset in resetEventBits() before */
2001 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2002 if (change->has_event[i])
2003 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2006 static char *getDefaultElementDescription(struct ElementInfo *ei)
2008 static char description[MAX_ELEMENT_NAME_LEN + 1];
2009 char *default_description = (ei->custom_description != NULL ?
2010 ei->custom_description :
2011 ei->editor_description);
2014 // always start with reliable default values
2015 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2016 description[i] = '\0';
2018 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2019 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2021 return &description[0];
2024 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2026 char *default_description = getDefaultElementDescription(ei);
2029 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2030 ei->description[i] = default_description[i];
2033 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2037 for (i = 0; conf[i].data_type != -1; i++)
2039 int default_value = conf[i].default_value;
2040 int data_type = conf[i].data_type;
2041 int conf_type = conf[i].conf_type;
2042 int byte_mask = conf_type & CONF_MASK_BYTES;
2044 if (byte_mask == CONF_MASK_MULTI_BYTES)
2046 int default_num_entities = conf[i].default_num_entities;
2047 int max_num_entities = conf[i].max_num_entities;
2049 *(int *)(conf[i].num_entities) = default_num_entities;
2051 if (data_type == TYPE_STRING)
2053 char *default_string = conf[i].default_string;
2054 char *string = (char *)(conf[i].value);
2056 strncpy(string, default_string, max_num_entities);
2058 else if (data_type == TYPE_ELEMENT_LIST)
2060 int *element_array = (int *)(conf[i].value);
2063 for (j = 0; j < max_num_entities; j++)
2064 element_array[j] = default_value;
2066 else if (data_type == TYPE_CONTENT_LIST)
2068 struct Content *content = (struct Content *)(conf[i].value);
2071 for (c = 0; c < max_num_entities; c++)
2072 for (y = 0; y < 3; y++)
2073 for (x = 0; x < 3; x++)
2074 content[c].e[x][y] = default_value;
2077 else // constant size configuration data (1, 2 or 4 bytes)
2079 if (data_type == TYPE_BOOLEAN)
2080 *(boolean *)(conf[i].value) = default_value;
2082 *(int *) (conf[i].value) = default_value;
2087 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2091 for (i = 0; conf[i].data_type != -1; i++)
2093 int data_type = conf[i].data_type;
2094 int conf_type = conf[i].conf_type;
2095 int byte_mask = conf_type & CONF_MASK_BYTES;
2097 if (byte_mask == CONF_MASK_MULTI_BYTES)
2099 int max_num_entities = conf[i].max_num_entities;
2101 if (data_type == TYPE_STRING)
2103 char *string = (char *)(conf[i].value);
2104 char *string_copy = (char *)(conf[i].value_copy);
2106 strncpy(string_copy, string, max_num_entities);
2108 else if (data_type == TYPE_ELEMENT_LIST)
2110 int *element_array = (int *)(conf[i].value);
2111 int *element_array_copy = (int *)(conf[i].value_copy);
2114 for (j = 0; j < max_num_entities; j++)
2115 element_array_copy[j] = element_array[j];
2117 else if (data_type == TYPE_CONTENT_LIST)
2119 struct Content *content = (struct Content *)(conf[i].value);
2120 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2123 for (c = 0; c < max_num_entities; c++)
2124 for (y = 0; y < 3; y++)
2125 for (x = 0; x < 3; x++)
2126 content_copy[c].e[x][y] = content[c].e[x][y];
2129 else // constant size configuration data (1, 2 or 4 bytes)
2131 if (data_type == TYPE_BOOLEAN)
2132 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2134 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2139 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2143 xx_ei = *ei_from; // copy element data into temporary buffer
2144 yy_ei = *ei_to; // copy element data into temporary buffer
2146 copyConfigFromConfigList(chunk_config_CUSX_base);
2151 // ---------- reinitialize and copy change pages ----------
2153 ei_to->num_change_pages = ei_from->num_change_pages;
2154 ei_to->current_change_page = ei_from->current_change_page;
2156 setElementChangePages(ei_to, ei_to->num_change_pages);
2158 for (i = 0; i < ei_to->num_change_pages; i++)
2159 ei_to->change_page[i] = ei_from->change_page[i];
2161 // ---------- copy group element info ----------
2162 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2163 *ei_to->group = *ei_from->group;
2165 // mark this custom element as modified
2166 ei_to->modified_settings = TRUE;
2169 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2171 int change_page_size = sizeof(struct ElementChangeInfo);
2173 ei->num_change_pages = MAX(1, change_pages);
2176 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2178 if (ei->current_change_page >= ei->num_change_pages)
2179 ei->current_change_page = ei->num_change_pages - 1;
2181 ei->change = &ei->change_page[ei->current_change_page];
2184 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2186 xx_change = *change; // copy change data into temporary buffer
2188 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2190 *change = xx_change;
2192 resetEventFlags(change);
2194 change->direct_action = 0;
2195 change->other_action = 0;
2197 change->pre_change_function = NULL;
2198 change->change_function = NULL;
2199 change->post_change_function = NULL;
2202 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2206 li = *level; // copy level data into temporary buffer
2207 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2208 *level = li; // copy temporary buffer back to level data
2210 setLevelInfoToDefaults_BD();
2211 setLevelInfoToDefaults_EM();
2212 setLevelInfoToDefaults_SP();
2213 setLevelInfoToDefaults_MM();
2215 level->native_bd_level = &native_bd_level;
2216 level->native_em_level = &native_em_level;
2217 level->native_sp_level = &native_sp_level;
2218 level->native_mm_level = &native_mm_level;
2220 level->file_version = FILE_VERSION_ACTUAL;
2221 level->game_version = GAME_VERSION_ACTUAL;
2223 level->creation_date = getCurrentDate();
2225 level->encoding_16bit_field = TRUE;
2226 level->encoding_16bit_yamyam = TRUE;
2227 level->encoding_16bit_amoeba = TRUE;
2229 // clear level name and level author string buffers
2230 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2231 level->name[i] = '\0';
2232 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2233 level->author[i] = '\0';
2235 // set level name and level author to default values
2236 strcpy(level->name, NAMELESS_LEVEL_NAME);
2237 strcpy(level->author, ANONYMOUS_NAME);
2239 // set level playfield to playable default level with player and exit
2240 for (x = 0; x < MAX_LEV_FIELDX; x++)
2241 for (y = 0; y < MAX_LEV_FIELDY; y++)
2242 level->field[x][y] = EL_SAND;
2244 level->field[0][0] = EL_PLAYER_1;
2245 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2247 BorderElement = EL_STEELWALL;
2249 // detect custom elements when loading them
2250 level->file_has_custom_elements = FALSE;
2252 // set all bug compatibility flags to "false" => do not emulate this bug
2253 level->use_action_after_change_bug = FALSE;
2255 if (leveldir_current)
2257 // try to determine better author name than 'anonymous'
2258 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2260 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2261 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2265 switch (LEVELCLASS(leveldir_current))
2267 case LEVELCLASS_TUTORIAL:
2268 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2271 case LEVELCLASS_CONTRIB:
2272 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2273 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2276 case LEVELCLASS_PRIVATE:
2277 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2278 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2282 // keep default value
2289 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2291 static boolean clipboard_elements_initialized = FALSE;
2294 InitElementPropertiesStatic();
2296 li = *level; // copy level data into temporary buffer
2297 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2298 *level = li; // copy temporary buffer back to level data
2300 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2303 struct ElementInfo *ei = &element_info[element];
2305 if (element == EL_MM_GRAY_BALL)
2307 struct LevelInfo_MM *level_mm = level->native_mm_level;
2310 for (j = 0; j < level->num_mm_ball_contents; j++)
2311 level->mm_ball_content[j] =
2312 map_element_MM_to_RND(level_mm->ball_content[j]);
2315 // never initialize clipboard elements after the very first time
2316 // (to be able to use clipboard elements between several levels)
2317 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2320 if (IS_ENVELOPE(element))
2322 int envelope_nr = element - EL_ENVELOPE_1;
2324 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2326 level->envelope[envelope_nr] = xx_envelope;
2329 if (IS_CUSTOM_ELEMENT(element) ||
2330 IS_GROUP_ELEMENT(element) ||
2331 IS_INTERNAL_ELEMENT(element))
2333 xx_ei = *ei; // copy element data into temporary buffer
2335 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2340 setElementChangePages(ei, 1);
2341 setElementChangeInfoToDefaults(ei->change);
2343 if (IS_CUSTOM_ELEMENT(element) ||
2344 IS_GROUP_ELEMENT(element))
2346 setElementDescriptionToDefault(ei);
2348 ei->modified_settings = FALSE;
2351 if (IS_CUSTOM_ELEMENT(element) ||
2352 IS_INTERNAL_ELEMENT(element))
2354 // internal values used in level editor
2356 ei->access_type = 0;
2357 ei->access_layer = 0;
2358 ei->access_protected = 0;
2359 ei->walk_to_action = 0;
2360 ei->smash_targets = 0;
2363 ei->can_explode_by_fire = FALSE;
2364 ei->can_explode_smashed = FALSE;
2365 ei->can_explode_impact = FALSE;
2367 ei->current_change_page = 0;
2370 if (IS_GROUP_ELEMENT(element) ||
2371 IS_INTERNAL_ELEMENT(element))
2373 struct ElementGroupInfo *group;
2375 // initialize memory for list of elements in group
2376 if (ei->group == NULL)
2377 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2381 xx_group = *group; // copy group data into temporary buffer
2383 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2388 if (IS_EMPTY_ELEMENT(element) ||
2389 IS_INTERNAL_ELEMENT(element))
2391 xx_ei = *ei; // copy element data into temporary buffer
2393 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2399 clipboard_elements_initialized = TRUE;
2402 static void setLevelInfoToDefaults(struct LevelInfo *level,
2403 boolean level_info_only,
2404 boolean reset_file_status)
2406 setLevelInfoToDefaults_Level(level);
2408 if (!level_info_only)
2409 setLevelInfoToDefaults_Elements(level);
2411 if (reset_file_status)
2413 level->no_valid_file = FALSE;
2414 level->no_level_file = FALSE;
2417 level->changed = FALSE;
2420 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2422 level_file_info->nr = 0;
2423 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2424 level_file_info->packed = FALSE;
2426 setString(&level_file_info->basename, NULL);
2427 setString(&level_file_info->filename, NULL);
2430 int getMappedElement_SB(int, boolean);
2432 static void ActivateLevelTemplate(void)
2436 if (check_special_flags("load_xsb_to_ces"))
2438 // fill smaller playfields with padding "beyond border wall" elements
2439 if (level.fieldx < level_template.fieldx ||
2440 level.fieldy < level_template.fieldy)
2442 short field[level.fieldx][level.fieldy];
2443 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2444 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2445 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2446 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2448 // copy old playfield (which is smaller than the visible area)
2449 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2450 field[x][y] = level.field[x][y];
2452 // fill new, larger playfield with "beyond border wall" elements
2453 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2454 level.field[x][y] = getMappedElement_SB('_', TRUE);
2456 // copy the old playfield to the middle of the new playfield
2457 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2458 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2460 level.fieldx = new_fieldx;
2461 level.fieldy = new_fieldy;
2465 // Currently there is no special action needed to activate the template
2466 // data, because 'element_info' property settings overwrite the original
2467 // level data, while all other variables do not change.
2469 // Exception: 'from_level_template' elements in the original level playfield
2470 // are overwritten with the corresponding elements at the same position in
2471 // playfield from the level template.
2473 for (x = 0; x < level.fieldx; x++)
2474 for (y = 0; y < level.fieldy; y++)
2475 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2476 level.field[x][y] = level_template.field[x][y];
2478 if (check_special_flags("load_xsb_to_ces"))
2480 struct LevelInfo level_backup = level;
2482 // overwrite all individual level settings from template level settings
2483 level = level_template;
2485 // restore level file info
2486 level.file_info = level_backup.file_info;
2488 // restore playfield size
2489 level.fieldx = level_backup.fieldx;
2490 level.fieldy = level_backup.fieldy;
2492 // restore playfield content
2493 for (x = 0; x < level.fieldx; x++)
2494 for (y = 0; y < level.fieldy; y++)
2495 level.field[x][y] = level_backup.field[x][y];
2497 // restore name and author from individual level
2498 strcpy(level.name, level_backup.name);
2499 strcpy(level.author, level_backup.author);
2501 // restore flag "use_custom_template"
2502 level.use_custom_template = level_backup.use_custom_template;
2506 static boolean checkForPackageFromBasename_BD(char *basename)
2508 // check for native BD level file extensions
2509 if (!strSuffixLower(basename, ".bd") &&
2510 !strSuffixLower(basename, ".bdr") &&
2511 !strSuffixLower(basename, ".brc") &&
2512 !strSuffixLower(basename, ".gds"))
2515 // check for standard single-level BD files (like "001.bd")
2516 if (strSuffixLower(basename, ".bd") &&
2517 strlen(basename) == 6 &&
2518 basename[0] >= '0' && basename[0] <= '9' &&
2519 basename[1] >= '0' && basename[1] <= '9' &&
2520 basename[2] >= '0' && basename[2] <= '9')
2523 // this is a level package in native BD file format
2527 static char *getLevelFilenameFromBasename(char *basename)
2529 static char *filename = NULL;
2531 checked_free(filename);
2533 filename = getPath2(getCurrentLevelDir(), basename);
2538 static int getFileTypeFromBasename(char *basename)
2540 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2542 static char *filename = NULL;
2543 struct stat file_status;
2545 // ---------- try to determine file type from filename ----------
2547 // check for typical filename of a Supaplex level package file
2548 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2549 return LEVEL_FILE_TYPE_SP;
2551 // check for typical filename of a Diamond Caves II level package file
2552 if (strSuffixLower(basename, ".dc") ||
2553 strSuffixLower(basename, ".dc2"))
2554 return LEVEL_FILE_TYPE_DC;
2556 // check for typical filename of a Sokoban level package file
2557 if (strSuffixLower(basename, ".xsb") &&
2558 strchr(basename, '%') == NULL)
2559 return LEVEL_FILE_TYPE_SB;
2561 // check for typical filename of a Boulder Dash (GDash) level package file
2562 if (checkForPackageFromBasename_BD(basename))
2563 return LEVEL_FILE_TYPE_BD;
2565 // ---------- try to determine file type from filesize ----------
2567 checked_free(filename);
2568 filename = getPath2(getCurrentLevelDir(), basename);
2570 if (stat(filename, &file_status) == 0)
2572 // check for typical filesize of a Supaplex level package file
2573 if (file_status.st_size == 170496)
2574 return LEVEL_FILE_TYPE_SP;
2577 return LEVEL_FILE_TYPE_UNKNOWN;
2580 static int getFileTypeFromMagicBytes(char *filename, int type)
2584 if ((file = openFile(filename, MODE_READ)))
2586 char chunk_name[CHUNK_ID_LEN + 1];
2588 getFileChunkBE(file, chunk_name, NULL);
2590 if (strEqual(chunk_name, "MMII") ||
2591 strEqual(chunk_name, "MIRR"))
2592 type = LEVEL_FILE_TYPE_MM;
2600 static boolean checkForPackageFromBasename(char *basename)
2602 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2603 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2605 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2608 static char *getSingleLevelBasenameExt(int nr, char *extension)
2610 static char basename[MAX_FILENAME_LEN];
2613 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2615 sprintf(basename, "%03d.%s", nr, extension);
2620 static char *getSingleLevelBasename(int nr)
2622 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2625 static char *getPackedLevelBasename(int type)
2627 static char basename[MAX_FILENAME_LEN];
2628 char *directory = getCurrentLevelDir();
2630 DirectoryEntry *dir_entry;
2632 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2634 if ((dir = openDirectory(directory)) == NULL)
2636 Warn("cannot read current level directory '%s'", directory);
2641 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2643 char *entry_basename = dir_entry->basename;
2644 int entry_type = getFileTypeFromBasename(entry_basename);
2646 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2648 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2651 strcpy(basename, entry_basename);
2658 closeDirectory(dir);
2663 static char *getSingleLevelFilename(int nr)
2665 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2668 #if ENABLE_UNUSED_CODE
2669 static char *getPackedLevelFilename(int type)
2671 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2675 char *getDefaultLevelFilename(int nr)
2677 return getSingleLevelFilename(nr);
2680 #if ENABLE_UNUSED_CODE
2681 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2685 lfi->packed = FALSE;
2687 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2688 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2692 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2693 int type, char *format, ...)
2695 static char basename[MAX_FILENAME_LEN];
2698 va_start(ap, format);
2699 vsprintf(basename, format, ap);
2703 lfi->packed = FALSE;
2705 setString(&lfi->basename, basename);
2706 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2709 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2715 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2716 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2719 static int getFiletypeFromID(char *filetype_id)
2721 char *filetype_id_lower;
2722 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2725 if (filetype_id == NULL)
2726 return LEVEL_FILE_TYPE_UNKNOWN;
2728 filetype_id_lower = getStringToLower(filetype_id);
2730 for (i = 0; filetype_id_list[i].id != NULL; i++)
2732 char *id_lower = getStringToLower(filetype_id_list[i].id);
2734 if (strEqual(filetype_id_lower, id_lower))
2735 filetype = filetype_id_list[i].filetype;
2739 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2743 free(filetype_id_lower);
2748 char *getLocalLevelTemplateFilename(void)
2750 return getDefaultLevelFilename(-1);
2753 char *getGlobalLevelTemplateFilename(void)
2755 // global variable "leveldir_current" must be modified in the loop below
2756 LevelDirTree *leveldir_current_last = leveldir_current;
2757 char *filename = NULL;
2759 // check for template level in path from current to topmost tree node
2761 while (leveldir_current != NULL)
2763 filename = getDefaultLevelFilename(-1);
2765 if (fileExists(filename))
2768 leveldir_current = leveldir_current->node_parent;
2771 // restore global variable "leveldir_current" modified in above loop
2772 leveldir_current = leveldir_current_last;
2777 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2781 // special case: level number is negative => check for level template file
2784 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2785 getSingleLevelBasename(-1));
2787 // replace local level template filename with global template filename
2788 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2790 // no fallback if template file not existing
2794 // special case: check for file name/pattern specified in "levelinfo.conf"
2795 if (leveldir_current->level_filename != NULL)
2797 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2799 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2800 leveldir_current->level_filename, nr);
2802 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2804 if (fileExists(lfi->filename))
2807 else if (leveldir_current->level_filetype != NULL)
2809 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2811 // check for specified native level file with standard file name
2812 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2813 "%03d.%s", nr, LEVELFILE_EXTENSION);
2814 if (fileExists(lfi->filename))
2818 // check for native Rocks'n'Diamonds level file
2819 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2820 "%03d.%s", nr, LEVELFILE_EXTENSION);
2821 if (fileExists(lfi->filename))
2824 // check for native Boulder Dash level file
2825 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2826 if (fileExists(lfi->filename))
2829 // check for Emerald Mine level file (V1)
2830 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2831 'a' + (nr / 10) % 26, '0' + nr % 10);
2832 if (fileExists(lfi->filename))
2834 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2835 'A' + (nr / 10) % 26, '0' + nr % 10);
2836 if (fileExists(lfi->filename))
2839 // check for Emerald Mine level file (V2 to V5)
2840 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2841 if (fileExists(lfi->filename))
2844 // check for Emerald Mine level file (V6 / single mode)
2845 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2846 if (fileExists(lfi->filename))
2848 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2849 if (fileExists(lfi->filename))
2852 // check for Emerald Mine level file (V6 / teamwork mode)
2853 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2854 if (fileExists(lfi->filename))
2856 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2857 if (fileExists(lfi->filename))
2860 // check for various packed level file formats
2861 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2862 if (fileExists(lfi->filename))
2865 // no known level file found -- use default values (and fail later)
2866 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2867 "%03d.%s", nr, LEVELFILE_EXTENSION);
2870 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2872 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2873 lfi->type = getFileTypeFromBasename(lfi->basename);
2875 if (lfi->type == LEVEL_FILE_TYPE_RND)
2876 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2879 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2881 // always start with reliable default values
2882 setFileInfoToDefaults(level_file_info);
2884 level_file_info->nr = nr; // set requested level number
2886 determineLevelFileInfo_Filename(level_file_info);
2887 determineLevelFileInfo_Filetype(level_file_info);
2890 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2891 struct LevelFileInfo *lfi_to)
2893 lfi_to->nr = lfi_from->nr;
2894 lfi_to->type = lfi_from->type;
2895 lfi_to->packed = lfi_from->packed;
2897 setString(&lfi_to->basename, lfi_from->basename);
2898 setString(&lfi_to->filename, lfi_from->filename);
2901 // ----------------------------------------------------------------------------
2902 // functions for loading R'n'D level
2903 // ----------------------------------------------------------------------------
2905 int getMappedElement(int element)
2907 // remap some (historic, now obsolete) elements
2911 case EL_PLAYER_OBSOLETE:
2912 element = EL_PLAYER_1;
2915 case EL_KEY_OBSOLETE:
2919 case EL_EM_KEY_1_FILE_OBSOLETE:
2920 element = EL_EM_KEY_1;
2923 case EL_EM_KEY_2_FILE_OBSOLETE:
2924 element = EL_EM_KEY_2;
2927 case EL_EM_KEY_3_FILE_OBSOLETE:
2928 element = EL_EM_KEY_3;
2931 case EL_EM_KEY_4_FILE_OBSOLETE:
2932 element = EL_EM_KEY_4;
2935 case EL_ENVELOPE_OBSOLETE:
2936 element = EL_ENVELOPE_1;
2944 if (element >= NUM_FILE_ELEMENTS)
2946 Warn("invalid level element %d", element);
2948 element = EL_UNKNOWN;
2956 static int getMappedElementByVersion(int element, int game_version)
2958 // remap some elements due to certain game version
2960 if (game_version <= VERSION_IDENT(2,2,0,0))
2962 // map game font elements
2963 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2964 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2965 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2966 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2969 if (game_version < VERSION_IDENT(3,0,0,0))
2971 // map Supaplex gravity tube elements
2972 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2973 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2974 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2975 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2982 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2984 level->file_version = getFileVersion(file);
2985 level->game_version = getFileVersion(file);
2990 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2992 level->creation_date.year = getFile16BitBE(file);
2993 level->creation_date.month = getFile8Bit(file);
2994 level->creation_date.day = getFile8Bit(file);
2996 level->creation_date.src = DATE_SRC_LEVELFILE;
3001 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3003 int initial_player_stepsize;
3004 int initial_player_gravity;
3007 level->fieldx = getFile8Bit(file);
3008 level->fieldy = getFile8Bit(file);
3010 level->time = getFile16BitBE(file);
3011 level->gems_needed = getFile16BitBE(file);
3013 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3014 level->name[i] = getFile8Bit(file);
3015 level->name[MAX_LEVEL_NAME_LEN] = 0;
3017 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3018 level->score[i] = getFile8Bit(file);
3020 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3021 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3022 for (y = 0; y < 3; y++)
3023 for (x = 0; x < 3; x++)
3024 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3026 level->amoeba_speed = getFile8Bit(file);
3027 level->time_magic_wall = getFile8Bit(file);
3028 level->time_wheel = getFile8Bit(file);
3029 level->amoeba_content = getMappedElement(getFile8Bit(file));
3031 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3034 for (i = 0; i < MAX_PLAYERS; i++)
3035 level->initial_player_stepsize[i] = initial_player_stepsize;
3037 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3039 for (i = 0; i < MAX_PLAYERS; i++)
3040 level->initial_player_gravity[i] = initial_player_gravity;
3042 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3043 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3045 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3047 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3048 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3049 level->can_move_into_acid_bits = getFile32BitBE(file);
3050 level->dont_collide_with_bits = getFile8Bit(file);
3052 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3053 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3055 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3056 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3057 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3059 level->game_engine_type = getFile8Bit(file);
3061 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3066 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3070 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3071 level->name[i] = getFile8Bit(file);
3072 level->name[MAX_LEVEL_NAME_LEN] = 0;
3077 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3081 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3082 level->author[i] = getFile8Bit(file);
3083 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3088 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3091 int chunk_size_expected = level->fieldx * level->fieldy;
3093 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3094 stored with 16-bit encoding (and should be twice as big then).
3095 Even worse, playfield data was stored 16-bit when only yamyam content
3096 contained 16-bit elements and vice versa. */
3098 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3099 chunk_size_expected *= 2;
3101 if (chunk_size_expected != chunk_size)
3103 ReadUnusedBytesFromFile(file, chunk_size);
3104 return chunk_size_expected;
3107 for (y = 0; y < level->fieldy; y++)
3108 for (x = 0; x < level->fieldx; x++)
3109 level->field[x][y] =
3110 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3115 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3118 int header_size = 4;
3119 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3120 int chunk_size_expected = header_size + content_size;
3122 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3123 stored with 16-bit encoding (and should be twice as big then).
3124 Even worse, playfield data was stored 16-bit when only yamyam content
3125 contained 16-bit elements and vice versa. */
3127 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3128 chunk_size_expected += content_size;
3130 if (chunk_size_expected != chunk_size)
3132 ReadUnusedBytesFromFile(file, chunk_size);
3133 return chunk_size_expected;
3137 level->num_yamyam_contents = getFile8Bit(file);
3141 // correct invalid number of content fields -- should never happen
3142 if (level->num_yamyam_contents < 1 ||
3143 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3144 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3146 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3147 for (y = 0; y < 3; y++)
3148 for (x = 0; x < 3; x++)
3149 level->yamyam_content[i].e[x][y] =
3150 getMappedElement(level->encoding_16bit_field ?
3151 getFile16BitBE(file) : getFile8Bit(file));
3155 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3160 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3162 element = getMappedElement(getFile16BitBE(file));
3163 num_contents = getFile8Bit(file);
3165 getFile8Bit(file); // content x size (unused)
3166 getFile8Bit(file); // content y size (unused)
3168 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3170 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3171 for (y = 0; y < 3; y++)
3172 for (x = 0; x < 3; x++)
3173 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3175 // correct invalid number of content fields -- should never happen
3176 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3177 num_contents = STD_ELEMENT_CONTENTS;
3179 if (element == EL_YAMYAM)
3181 level->num_yamyam_contents = num_contents;
3183 for (i = 0; i < num_contents; i++)
3184 for (y = 0; y < 3; y++)
3185 for (x = 0; x < 3; x++)
3186 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3188 else if (element == EL_BD_AMOEBA)
3190 level->amoeba_content = content_array[0][0][0];
3194 Warn("cannot load content for element '%d'", element);
3200 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3206 int chunk_size_expected;
3208 element = getMappedElement(getFile16BitBE(file));
3209 if (!IS_ENVELOPE(element))
3210 element = EL_ENVELOPE_1;
3212 envelope_nr = element - EL_ENVELOPE_1;
3214 envelope_len = getFile16BitBE(file);
3216 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3217 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3219 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3221 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3222 if (chunk_size_expected != chunk_size)
3224 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3225 return chunk_size_expected;
3228 for (i = 0; i < envelope_len; i++)
3229 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3234 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3236 int num_changed_custom_elements = getFile16BitBE(file);
3237 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3240 if (chunk_size_expected != chunk_size)
3242 ReadUnusedBytesFromFile(file, chunk_size - 2);
3243 return chunk_size_expected;
3246 for (i = 0; i < num_changed_custom_elements; i++)
3248 int element = getMappedElement(getFile16BitBE(file));
3249 int properties = getFile32BitBE(file);
3251 if (IS_CUSTOM_ELEMENT(element))
3252 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3254 Warn("invalid custom element number %d", element);
3256 // older game versions that wrote level files with CUS1 chunks used
3257 // different default push delay values (not yet stored in level file)
3258 element_info[element].push_delay_fixed = 2;
3259 element_info[element].push_delay_random = 8;
3262 level->file_has_custom_elements = TRUE;
3267 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3269 int num_changed_custom_elements = getFile16BitBE(file);
3270 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3273 if (chunk_size_expected != chunk_size)
3275 ReadUnusedBytesFromFile(file, chunk_size - 2);
3276 return chunk_size_expected;
3279 for (i = 0; i < num_changed_custom_elements; i++)
3281 int element = getMappedElement(getFile16BitBE(file));
3282 int custom_target_element = getMappedElement(getFile16BitBE(file));
3284 if (IS_CUSTOM_ELEMENT(element))
3285 element_info[element].change->target_element = custom_target_element;
3287 Warn("invalid custom element number %d", element);
3290 level->file_has_custom_elements = TRUE;
3295 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3297 int num_changed_custom_elements = getFile16BitBE(file);
3298 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3301 if (chunk_size_expected != chunk_size)
3303 ReadUnusedBytesFromFile(file, chunk_size - 2);
3304 return chunk_size_expected;
3307 for (i = 0; i < num_changed_custom_elements; i++)
3309 int element = getMappedElement(getFile16BitBE(file));
3310 struct ElementInfo *ei = &element_info[element];
3311 unsigned int event_bits;
3313 if (!IS_CUSTOM_ELEMENT(element))
3315 Warn("invalid custom element number %d", element);
3317 element = EL_INTERNAL_DUMMY;
3320 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3321 ei->description[j] = getFile8Bit(file);
3322 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3324 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3326 // some free bytes for future properties and padding
3327 ReadUnusedBytesFromFile(file, 7);
3329 ei->use_gfx_element = getFile8Bit(file);
3330 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3332 ei->collect_score_initial = getFile8Bit(file);
3333 ei->collect_count_initial = getFile8Bit(file);
3335 ei->push_delay_fixed = getFile16BitBE(file);
3336 ei->push_delay_random = getFile16BitBE(file);
3337 ei->move_delay_fixed = getFile16BitBE(file);
3338 ei->move_delay_random = getFile16BitBE(file);
3340 ei->move_pattern = getFile16BitBE(file);
3341 ei->move_direction_initial = getFile8Bit(file);
3342 ei->move_stepsize = getFile8Bit(file);
3344 for (y = 0; y < 3; y++)
3345 for (x = 0; x < 3; x++)
3346 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3348 // bits 0 - 31 of "has_event[]"
3349 event_bits = getFile32BitBE(file);
3350 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3351 if (event_bits & (1u << j))
3352 ei->change->has_event[j] = TRUE;
3354 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3356 ei->change->delay_fixed = getFile16BitBE(file);
3357 ei->change->delay_random = getFile16BitBE(file);
3358 ei->change->delay_frames = getFile16BitBE(file);
3360 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3362 ei->change->explode = getFile8Bit(file);
3363 ei->change->use_target_content = getFile8Bit(file);
3364 ei->change->only_if_complete = getFile8Bit(file);
3365 ei->change->use_random_replace = getFile8Bit(file);
3367 ei->change->random_percentage = getFile8Bit(file);
3368 ei->change->replace_when = getFile8Bit(file);
3370 for (y = 0; y < 3; y++)
3371 for (x = 0; x < 3; x++)
3372 ei->change->target_content.e[x][y] =
3373 getMappedElement(getFile16BitBE(file));
3375 ei->slippery_type = getFile8Bit(file);
3377 // some free bytes for future properties and padding
3378 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3380 // mark that this custom element has been modified
3381 ei->modified_settings = TRUE;
3384 level->file_has_custom_elements = TRUE;
3389 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3391 struct ElementInfo *ei;
3392 int chunk_size_expected;
3396 // ---------- custom element base property values (96 bytes) ----------------
3398 element = getMappedElement(getFile16BitBE(file));
3400 if (!IS_CUSTOM_ELEMENT(element))
3402 Warn("invalid custom element number %d", element);
3404 ReadUnusedBytesFromFile(file, chunk_size - 2);
3409 ei = &element_info[element];
3411 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3412 ei->description[i] = getFile8Bit(file);
3413 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3415 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3417 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3419 ei->num_change_pages = getFile8Bit(file);
3421 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3422 if (chunk_size_expected != chunk_size)
3424 ReadUnusedBytesFromFile(file, chunk_size - 43);
3425 return chunk_size_expected;
3428 ei->ce_value_fixed_initial = getFile16BitBE(file);
3429 ei->ce_value_random_initial = getFile16BitBE(file);
3430 ei->use_last_ce_value = getFile8Bit(file);
3432 ei->use_gfx_element = getFile8Bit(file);
3433 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3435 ei->collect_score_initial = getFile8Bit(file);
3436 ei->collect_count_initial = getFile8Bit(file);
3438 ei->drop_delay_fixed = getFile8Bit(file);
3439 ei->push_delay_fixed = getFile8Bit(file);
3440 ei->drop_delay_random = getFile8Bit(file);
3441 ei->push_delay_random = getFile8Bit(file);
3442 ei->move_delay_fixed = getFile16BitBE(file);
3443 ei->move_delay_random = getFile16BitBE(file);
3445 // bits 0 - 15 of "move_pattern" ...
3446 ei->move_pattern = getFile16BitBE(file);
3447 ei->move_direction_initial = getFile8Bit(file);
3448 ei->move_stepsize = getFile8Bit(file);
3450 ei->slippery_type = getFile8Bit(file);
3452 for (y = 0; y < 3; y++)
3453 for (x = 0; x < 3; x++)
3454 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3456 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3457 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3458 ei->move_leave_type = getFile8Bit(file);
3460 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3461 ei->move_pattern |= (getFile16BitBE(file) << 16);
3463 ei->access_direction = getFile8Bit(file);
3465 ei->explosion_delay = getFile8Bit(file);
3466 ei->ignition_delay = getFile8Bit(file);
3467 ei->explosion_type = getFile8Bit(file);
3469 // some free bytes for future custom property values and padding
3470 ReadUnusedBytesFromFile(file, 1);
3472 // ---------- change page property values (48 bytes) ------------------------
3474 setElementChangePages(ei, ei->num_change_pages);
3476 for (i = 0; i < ei->num_change_pages; i++)
3478 struct ElementChangeInfo *change = &ei->change_page[i];
3479 unsigned int event_bits;
3481 // always start with reliable default values
3482 setElementChangeInfoToDefaults(change);
3484 // bits 0 - 31 of "has_event[]" ...
3485 event_bits = getFile32BitBE(file);
3486 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3487 if (event_bits & (1u << j))
3488 change->has_event[j] = TRUE;
3490 change->target_element = getMappedElement(getFile16BitBE(file));
3492 change->delay_fixed = getFile16BitBE(file);
3493 change->delay_random = getFile16BitBE(file);
3494 change->delay_frames = getFile16BitBE(file);
3496 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3498 change->explode = getFile8Bit(file);
3499 change->use_target_content = getFile8Bit(file);
3500 change->only_if_complete = getFile8Bit(file);
3501 change->use_random_replace = getFile8Bit(file);
3503 change->random_percentage = getFile8Bit(file);
3504 change->replace_when = getFile8Bit(file);
3506 for (y = 0; y < 3; y++)
3507 for (x = 0; x < 3; x++)
3508 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3510 change->can_change = getFile8Bit(file);
3512 change->trigger_side = getFile8Bit(file);
3514 change->trigger_player = getFile8Bit(file);
3515 change->trigger_page = getFile8Bit(file);
3517 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3518 CH_PAGE_ANY : (1 << change->trigger_page));
3520 change->has_action = getFile8Bit(file);
3521 change->action_type = getFile8Bit(file);
3522 change->action_mode = getFile8Bit(file);
3523 change->action_arg = getFile16BitBE(file);
3525 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3526 event_bits = getFile8Bit(file);
3527 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3528 if (event_bits & (1u << (j - 32)))
3529 change->has_event[j] = TRUE;
3532 // mark this custom element as modified
3533 ei->modified_settings = TRUE;
3535 level->file_has_custom_elements = TRUE;
3540 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3542 struct ElementInfo *ei;
3543 struct ElementGroupInfo *group;
3547 element = getMappedElement(getFile16BitBE(file));
3549 if (!IS_GROUP_ELEMENT(element))
3551 Warn("invalid group element number %d", element);
3553 ReadUnusedBytesFromFile(file, chunk_size - 2);
3558 ei = &element_info[element];
3560 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3561 ei->description[i] = getFile8Bit(file);
3562 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3564 group = element_info[element].group;
3566 group->num_elements = getFile8Bit(file);
3568 ei->use_gfx_element = getFile8Bit(file);
3569 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3571 group->choice_mode = getFile8Bit(file);
3573 // some free bytes for future values and padding
3574 ReadUnusedBytesFromFile(file, 3);
3576 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3577 group->element[i] = getMappedElement(getFile16BitBE(file));
3579 // mark this group element as modified
3580 element_info[element].modified_settings = TRUE;
3582 level->file_has_custom_elements = TRUE;
3587 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3588 int element, int real_element)
3590 int micro_chunk_size = 0;
3591 int conf_type = getFile8Bit(file);
3592 int byte_mask = conf_type & CONF_MASK_BYTES;
3593 boolean element_found = FALSE;
3596 micro_chunk_size += 1;
3598 if (byte_mask == CONF_MASK_MULTI_BYTES)
3600 int num_bytes = getFile16BitBE(file);
3601 byte *buffer = checked_malloc(num_bytes);
3603 ReadBytesFromFile(file, buffer, num_bytes);
3605 for (i = 0; conf[i].data_type != -1; i++)
3607 if (conf[i].element == element &&
3608 conf[i].conf_type == conf_type)
3610 int data_type = conf[i].data_type;
3611 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3612 int max_num_entities = conf[i].max_num_entities;
3614 if (num_entities > max_num_entities)
3616 Warn("truncating number of entities for element %d from %d to %d",
3617 element, num_entities, max_num_entities);
3619 num_entities = max_num_entities;
3622 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3623 data_type == TYPE_CONTENT_LIST))
3625 // for element and content lists, zero entities are not allowed
3626 Warn("found empty list of entities for element %d", element);
3628 // do not set "num_entities" here to prevent reading behind buffer
3630 *(int *)(conf[i].num_entities) = 1; // at least one is required
3634 *(int *)(conf[i].num_entities) = num_entities;
3637 element_found = TRUE;
3639 if (data_type == TYPE_STRING)
3641 char *string = (char *)(conf[i].value);
3644 for (j = 0; j < max_num_entities; j++)
3645 string[j] = (j < num_entities ? buffer[j] : '\0');
3647 else if (data_type == TYPE_ELEMENT_LIST)
3649 int *element_array = (int *)(conf[i].value);
3652 for (j = 0; j < num_entities; j++)
3654 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3656 else if (data_type == TYPE_CONTENT_LIST)
3658 struct Content *content= (struct Content *)(conf[i].value);
3661 for (c = 0; c < num_entities; c++)
3662 for (y = 0; y < 3; y++)
3663 for (x = 0; x < 3; x++)
3664 content[c].e[x][y] =
3665 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3668 element_found = FALSE;
3674 checked_free(buffer);
3676 micro_chunk_size += 2 + num_bytes;
3678 else // constant size configuration data (1, 2 or 4 bytes)
3680 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3681 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3682 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3684 for (i = 0; conf[i].data_type != -1; i++)
3686 if (conf[i].element == element &&
3687 conf[i].conf_type == conf_type)
3689 int data_type = conf[i].data_type;
3691 if (data_type == TYPE_ELEMENT)
3692 value = getMappedElement(value);
3694 if (data_type == TYPE_BOOLEAN)
3695 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3697 *(int *) (conf[i].value) = value;
3699 element_found = TRUE;
3705 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3710 char *error_conf_chunk_bytes =
3711 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3712 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3713 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3714 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3715 int error_element = real_element;
3717 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3718 error_conf_chunk_bytes, error_conf_chunk_token,
3719 error_element, EL_NAME(error_element));
3722 return micro_chunk_size;
3725 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3727 int real_chunk_size = 0;
3729 li = *level; // copy level data into temporary buffer
3731 while (!checkEndOfFile(file))
3733 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3735 if (real_chunk_size >= chunk_size)
3739 *level = li; // copy temporary buffer back to level data
3741 return real_chunk_size;
3744 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3746 int real_chunk_size = 0;
3748 li = *level; // copy level data into temporary buffer
3750 while (!checkEndOfFile(file))
3752 int element = getMappedElement(getFile16BitBE(file));
3754 real_chunk_size += 2;
3755 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3757 if (real_chunk_size >= chunk_size)
3761 *level = li; // copy temporary buffer back to level data
3763 return real_chunk_size;
3766 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3768 int real_chunk_size = 0;
3770 li = *level; // copy level data into temporary buffer
3772 while (!checkEndOfFile(file))
3774 int element = getMappedElement(getFile16BitBE(file));
3776 real_chunk_size += 2;
3777 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3779 if (real_chunk_size >= chunk_size)
3783 *level = li; // copy temporary buffer back to level data
3785 return real_chunk_size;
3788 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3790 int element = getMappedElement(getFile16BitBE(file));
3791 int envelope_nr = element - EL_ENVELOPE_1;
3792 int real_chunk_size = 2;
3794 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3796 while (!checkEndOfFile(file))
3798 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3801 if (real_chunk_size >= chunk_size)
3805 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3807 return real_chunk_size;
3810 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3812 int element = getMappedElement(getFile16BitBE(file));
3813 int real_chunk_size = 2;
3814 struct ElementInfo *ei = &element_info[element];
3817 xx_ei = *ei; // copy element data into temporary buffer
3819 xx_ei.num_change_pages = -1;
3821 while (!checkEndOfFile(file))
3823 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3825 if (xx_ei.num_change_pages != -1)
3828 if (real_chunk_size >= chunk_size)
3834 if (ei->num_change_pages == -1)
3836 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3839 ei->num_change_pages = 1;
3841 setElementChangePages(ei, 1);
3842 setElementChangeInfoToDefaults(ei->change);
3844 return real_chunk_size;
3847 // initialize number of change pages stored for this custom element
3848 setElementChangePages(ei, ei->num_change_pages);
3849 for (i = 0; i < ei->num_change_pages; i++)
3850 setElementChangeInfoToDefaults(&ei->change_page[i]);
3852 // start with reading properties for the first change page
3853 xx_current_change_page = 0;
3855 while (!checkEndOfFile(file))
3857 // level file might contain invalid change page number
3858 if (xx_current_change_page >= ei->num_change_pages)
3861 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3863 xx_change = *change; // copy change data into temporary buffer
3865 resetEventBits(); // reset bits; change page might have changed
3867 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3870 *change = xx_change;
3872 setEventFlagsFromEventBits(change);
3874 if (real_chunk_size >= chunk_size)
3878 level->file_has_custom_elements = TRUE;
3880 return real_chunk_size;
3883 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3885 int element = getMappedElement(getFile16BitBE(file));
3886 int real_chunk_size = 2;
3887 struct ElementInfo *ei = &element_info[element];
3888 struct ElementGroupInfo *group = ei->group;
3893 xx_ei = *ei; // copy element data into temporary buffer
3894 xx_group = *group; // copy group data into temporary buffer
3896 while (!checkEndOfFile(file))
3898 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3901 if (real_chunk_size >= chunk_size)
3908 level->file_has_custom_elements = TRUE;
3910 return real_chunk_size;
3913 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3915 int element = getMappedElement(getFile16BitBE(file));
3916 int real_chunk_size = 2;
3917 struct ElementInfo *ei = &element_info[element];
3919 xx_ei = *ei; // copy element data into temporary buffer
3921 while (!checkEndOfFile(file))
3923 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3926 if (real_chunk_size >= chunk_size)
3932 level->file_has_custom_elements = TRUE;
3934 return real_chunk_size;
3937 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3938 struct LevelFileInfo *level_file_info,
3939 boolean level_info_only)
3941 char *filename = level_file_info->filename;
3942 char cookie[MAX_LINE_LEN];
3943 char chunk_name[CHUNK_ID_LEN + 1];
3947 if (!(file = openFile(filename, MODE_READ)))
3949 level->no_valid_file = TRUE;
3950 level->no_level_file = TRUE;
3952 if (level_info_only)
3955 Warn("cannot read level '%s' -- using empty level", filename);
3957 if (!setup.editor.use_template_for_new_levels)
3960 // if level file not found, try to initialize level data from template
3961 filename = getGlobalLevelTemplateFilename();
3963 if (!(file = openFile(filename, MODE_READ)))
3966 // default: for empty levels, use level template for custom elements
3967 level->use_custom_template = TRUE;
3969 level->no_valid_file = FALSE;
3972 getFileChunkBE(file, chunk_name, NULL);
3973 if (strEqual(chunk_name, "RND1"))
3975 getFile32BitBE(file); // not used
3977 getFileChunkBE(file, chunk_name, NULL);
3978 if (!strEqual(chunk_name, "CAVE"))
3980 level->no_valid_file = TRUE;
3982 Warn("unknown format of level file '%s'", filename);
3989 else // check for pre-2.0 file format with cookie string
3991 strcpy(cookie, chunk_name);
3992 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3994 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3995 cookie[strlen(cookie) - 1] = '\0';
3997 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3999 level->no_valid_file = TRUE;
4001 Warn("unknown format of level file '%s'", filename);
4008 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4010 level->no_valid_file = TRUE;
4012 Warn("unsupported version of level file '%s'", filename);
4019 // pre-2.0 level files have no game version, so use file version here
4020 level->game_version = level->file_version;
4023 if (level->file_version < FILE_VERSION_1_2)
4025 // level files from versions before 1.2.0 without chunk structure
4026 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4027 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4035 int (*loader)(File *, int, struct LevelInfo *);
4039 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4040 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4041 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4042 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4043 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4044 { "INFO", -1, LoadLevel_INFO },
4045 { "BODY", -1, LoadLevel_BODY },
4046 { "CONT", -1, LoadLevel_CONT },
4047 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4048 { "CNT3", -1, LoadLevel_CNT3 },
4049 { "CUS1", -1, LoadLevel_CUS1 },
4050 { "CUS2", -1, LoadLevel_CUS2 },
4051 { "CUS3", -1, LoadLevel_CUS3 },
4052 { "CUS4", -1, LoadLevel_CUS4 },
4053 { "GRP1", -1, LoadLevel_GRP1 },
4054 { "CONF", -1, LoadLevel_CONF },
4055 { "ELEM", -1, LoadLevel_ELEM },
4056 { "NOTE", -1, LoadLevel_NOTE },
4057 { "CUSX", -1, LoadLevel_CUSX },
4058 { "GRPX", -1, LoadLevel_GRPX },
4059 { "EMPX", -1, LoadLevel_EMPX },
4064 while (getFileChunkBE(file, chunk_name, &chunk_size))
4068 while (chunk_info[i].name != NULL &&
4069 !strEqual(chunk_name, chunk_info[i].name))
4072 if (chunk_info[i].name == NULL)
4074 Warn("unknown chunk '%s' in level file '%s'",
4075 chunk_name, filename);
4077 ReadUnusedBytesFromFile(file, chunk_size);
4079 else if (chunk_info[i].size != -1 &&
4080 chunk_info[i].size != chunk_size)
4082 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4083 chunk_size, chunk_name, filename);
4085 ReadUnusedBytesFromFile(file, chunk_size);
4089 // call function to load this level chunk
4090 int chunk_size_expected =
4091 (chunk_info[i].loader)(file, chunk_size, level);
4093 if (chunk_size_expected < 0)
4095 Warn("error reading chunk '%s' in level file '%s'",
4096 chunk_name, filename);
4101 // the size of some chunks cannot be checked before reading other
4102 // chunks first (like "HEAD" and "BODY") that contain some header
4103 // information, so check them here
4104 if (chunk_size_expected != chunk_size)
4106 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4107 chunk_size, chunk_name, filename);
4119 // ----------------------------------------------------------------------------
4120 // functions for loading BD level
4121 // ----------------------------------------------------------------------------
4123 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4124 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4126 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4128 struct LevelInfo_BD *level_bd = level->native_bd_level;
4129 GdCave *cave = NULL; // will be changed below
4130 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4131 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4134 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4136 // cave and map newly allocated when set to defaults above
4137 cave = level_bd->cave;
4140 cave->intermission = level->bd_intermission;
4143 cave->level_time[0] = level->time;
4144 cave->level_diamonds[0] = level->gems_needed;
4147 cave->scheduling = level->bd_scheduling_type;
4148 cave->pal_timing = level->bd_pal_timing;
4149 cave->level_speed[0] = level->bd_cycle_delay_ms;
4150 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4151 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4152 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4155 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4156 cave->diamond_value = level->score[SC_EMERALD];
4157 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4159 // compatibility settings
4160 cave->lineshift = level->bd_line_shifting_borders;
4161 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4162 cave->short_explosions = level->bd_short_explosions;
4164 // player properties
4165 cave->diagonal_movements = level->bd_diagonal_movements;
4166 cave->active_is_first_found = level->bd_topmost_player_active;
4167 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4168 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4169 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4170 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4172 // element properties
4173 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4174 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4175 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4176 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4177 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4178 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4179 cave->level_magic_wall_time[0] = level->time_magic_wall;
4180 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4181 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4183 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4184 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4185 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4186 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4187 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4188 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4189 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4191 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4192 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4193 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4194 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4195 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4196 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4197 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4198 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4199 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4200 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4201 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4203 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4204 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4205 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4206 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4207 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4208 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4210 cave->slime_predictable = level->bd_slime_is_predictable;
4211 cave->slime_correct_random = level->bd_slime_correct_random;
4212 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4213 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4214 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4215 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4216 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4217 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4218 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4219 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4220 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4221 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4223 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4224 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4225 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4227 cave->biter_delay_frame = level->bd_biter_move_delay;
4228 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4230 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4232 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4234 cave->replicators_active = level->bd_replicators_active;
4235 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4237 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4238 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4240 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4242 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4244 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4245 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4246 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4248 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4249 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4251 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4252 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4254 cave->creatures_backwards = level->bd_creatures_start_backwards;
4255 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4256 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4258 cave->gravity = level->bd_gravity_direction;
4259 cave->gravity_switch_active = level->bd_gravity_switch_active;
4260 cave->gravity_change_time = level->bd_gravity_switch_delay;
4261 cave->gravity_affects_all = level->bd_gravity_affects_all;
4264 strncpy(cave->name, level->name, sizeof(GdString));
4265 cave->name[sizeof(GdString) - 1] = '\0';
4267 // playfield elements
4268 for (x = 0; x < cave->w; x++)
4269 for (y = 0; y < cave->h; y++)
4270 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4273 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4275 struct LevelInfo_BD *level_bd = level->native_bd_level;
4276 GdCave *cave = level_bd->cave;
4277 int bd_level_nr = level_bd->level_nr;
4280 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4281 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4284 level->bd_intermission = cave->intermission;
4287 level->time = cave->level_time[bd_level_nr];
4288 level->gems_needed = cave->level_diamonds[bd_level_nr];
4291 level->bd_scheduling_type = cave->scheduling;
4292 level->bd_pal_timing = cave->pal_timing;
4293 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4294 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4295 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4296 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4299 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4300 level->score[SC_EMERALD] = cave->diamond_value;
4301 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4303 // compatibility settings
4304 level->bd_line_shifting_borders = cave->lineshift;
4305 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4306 level->bd_short_explosions = cave->short_explosions;
4308 // player properties
4309 level->bd_diagonal_movements = cave->diagonal_movements;
4310 level->bd_topmost_player_active = cave->active_is_first_found;
4311 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4312 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4313 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4314 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4316 // element properties
4317 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4318 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4319 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4320 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4321 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4322 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4323 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4324 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4325 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4327 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4328 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4329 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4330 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4331 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4332 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4333 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4335 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4336 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4337 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4338 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4339 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4340 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4341 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4342 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4343 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4344 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4345 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4347 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4348 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4349 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4350 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4351 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4352 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4354 level->bd_slime_is_predictable = cave->slime_predictable;
4355 level->bd_slime_correct_random = cave->slime_correct_random;
4356 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4357 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4358 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4359 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4360 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4361 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4362 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4363 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4364 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4365 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4367 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4368 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4369 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4371 level->bd_biter_move_delay = cave->biter_delay_frame;
4372 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4374 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4376 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4378 level->bd_replicators_active = cave->replicators_active;
4379 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4381 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4382 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4384 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4386 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4388 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4389 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4390 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4392 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4393 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4395 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4396 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4398 level->bd_creatures_start_backwards = cave->creatures_backwards;
4399 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4400 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4402 level->bd_gravity_direction = cave->gravity;
4403 level->bd_gravity_switch_active = cave->gravity_switch_active;
4404 level->bd_gravity_switch_delay = cave->gravity_change_time;
4405 level->bd_gravity_affects_all = cave->gravity_affects_all;
4408 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4410 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4411 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4413 // playfield elements
4414 for (x = 0; x < level->fieldx; x++)
4415 for (y = 0; y < level->fieldy; y++)
4416 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4418 checked_free(cave_name);
4421 static void setTapeInfoToDefaults(void);
4423 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4425 struct LevelInfo_BD *level_bd = level->native_bd_level;
4426 GdCave *cave = level_bd->cave;
4427 GdReplay *replay = level_bd->replay;
4433 // always start with reliable default values
4434 setTapeInfoToDefaults();
4436 tape.level_nr = level_nr; // (currently not used)
4437 tape.random_seed = replay->seed;
4439 TapeSetDateFromIsoDateString(replay->date);
4442 tape.pos[tape.counter].delay = 0;
4444 tape.bd_replay = TRUE;
4446 // all time calculations only used to display approximate tape time
4447 int cave_speed = cave->speed;
4448 int milliseconds_game = 0;
4449 int milliseconds_elapsed = 20;
4451 for (i = 0; i < replay->movements->len; i++)
4453 int replay_action = replay->movements->data[i];
4454 int tape_action = map_action_BD_to_RND(replay_action);
4455 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4456 boolean success = 0;
4460 success = TapeAddAction(action);
4462 milliseconds_game += milliseconds_elapsed;
4464 if (milliseconds_game >= cave_speed)
4466 milliseconds_game -= cave_speed;
4473 tape.pos[tape.counter].delay = 0;
4474 tape.pos[tape.counter].action[0] = 0;
4478 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4484 TapeHaltRecording();
4488 // ----------------------------------------------------------------------------
4489 // functions for loading EM level
4490 // ----------------------------------------------------------------------------
4492 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4494 static int ball_xy[8][2] =
4505 struct LevelInfo_EM *level_em = level->native_em_level;
4506 struct CAVE *cav = level_em->cav;
4509 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4510 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4512 cav->time_seconds = level->time;
4513 cav->gems_needed = level->gems_needed;
4515 cav->emerald_score = level->score[SC_EMERALD];
4516 cav->diamond_score = level->score[SC_DIAMOND];
4517 cav->alien_score = level->score[SC_ROBOT];
4518 cav->tank_score = level->score[SC_SPACESHIP];
4519 cav->bug_score = level->score[SC_BUG];
4520 cav->eater_score = level->score[SC_YAMYAM];
4521 cav->nut_score = level->score[SC_NUT];
4522 cav->dynamite_score = level->score[SC_DYNAMITE];
4523 cav->key_score = level->score[SC_KEY];
4524 cav->exit_score = level->score[SC_TIME_BONUS];
4526 cav->num_eater_arrays = level->num_yamyam_contents;
4528 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4529 for (y = 0; y < 3; y++)
4530 for (x = 0; x < 3; x++)
4531 cav->eater_array[i][y * 3 + x] =
4532 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4534 cav->amoeba_time = level->amoeba_speed;
4535 cav->wonderwall_time = level->time_magic_wall;
4536 cav->wheel_time = level->time_wheel;
4538 cav->android_move_time = level->android_move_time;
4539 cav->android_clone_time = level->android_clone_time;
4540 cav->ball_random = level->ball_random;
4541 cav->ball_active = level->ball_active_initial;
4542 cav->ball_time = level->ball_time;
4543 cav->num_ball_arrays = level->num_ball_contents;
4545 cav->lenses_score = level->lenses_score;
4546 cav->magnify_score = level->magnify_score;
4547 cav->slurp_score = level->slurp_score;
4549 cav->lenses_time = level->lenses_time;
4550 cav->magnify_time = level->magnify_time;
4552 cav->wind_time = 9999;
4553 cav->wind_direction =
4554 map_direction_RND_to_EM(level->wind_direction_initial);
4556 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4557 for (j = 0; j < 8; j++)
4558 cav->ball_array[i][j] =
4559 map_element_RND_to_EM_cave(level->ball_content[i].
4560 e[ball_xy[j][0]][ball_xy[j][1]]);
4562 map_android_clone_elements_RND_to_EM(level);
4564 // first fill the complete playfield with the empty space element
4565 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4566 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4567 cav->cave[x][y] = Cblank;
4569 // then copy the real level contents from level file into the playfield
4570 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4572 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4574 if (level->field[x][y] == EL_AMOEBA_DEAD)
4575 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4577 cav->cave[x][y] = new_element;
4580 for (i = 0; i < MAX_PLAYERS; i++)
4582 cav->player_x[i] = -1;
4583 cav->player_y[i] = -1;
4586 // initialize player positions and delete players from the playfield
4587 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4589 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4591 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4593 cav->player_x[player_nr] = x;
4594 cav->player_y[player_nr] = y;
4596 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4601 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4603 static int ball_xy[8][2] =
4614 struct LevelInfo_EM *level_em = level->native_em_level;
4615 struct CAVE *cav = level_em->cav;
4618 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4619 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4621 level->time = cav->time_seconds;
4622 level->gems_needed = cav->gems_needed;
4624 sprintf(level->name, "Level %d", level->file_info.nr);
4626 level->score[SC_EMERALD] = cav->emerald_score;
4627 level->score[SC_DIAMOND] = cav->diamond_score;
4628 level->score[SC_ROBOT] = cav->alien_score;
4629 level->score[SC_SPACESHIP] = cav->tank_score;
4630 level->score[SC_BUG] = cav->bug_score;
4631 level->score[SC_YAMYAM] = cav->eater_score;
4632 level->score[SC_NUT] = cav->nut_score;
4633 level->score[SC_DYNAMITE] = cav->dynamite_score;
4634 level->score[SC_KEY] = cav->key_score;
4635 level->score[SC_TIME_BONUS] = cav->exit_score;
4637 level->num_yamyam_contents = cav->num_eater_arrays;
4639 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4640 for (y = 0; y < 3; y++)
4641 for (x = 0; x < 3; x++)
4642 level->yamyam_content[i].e[x][y] =
4643 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4645 level->amoeba_speed = cav->amoeba_time;
4646 level->time_magic_wall = cav->wonderwall_time;
4647 level->time_wheel = cav->wheel_time;
4649 level->android_move_time = cav->android_move_time;
4650 level->android_clone_time = cav->android_clone_time;
4651 level->ball_random = cav->ball_random;
4652 level->ball_active_initial = cav->ball_active;
4653 level->ball_time = cav->ball_time;
4654 level->num_ball_contents = cav->num_ball_arrays;
4656 level->lenses_score = cav->lenses_score;
4657 level->magnify_score = cav->magnify_score;
4658 level->slurp_score = cav->slurp_score;
4660 level->lenses_time = cav->lenses_time;
4661 level->magnify_time = cav->magnify_time;
4663 level->wind_direction_initial =
4664 map_direction_EM_to_RND(cav->wind_direction);
4666 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4667 for (j = 0; j < 8; j++)
4668 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4669 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4671 map_android_clone_elements_EM_to_RND(level);
4673 // convert the playfield (some elements need special treatment)
4674 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4676 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4678 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4679 new_element = EL_AMOEBA_DEAD;
4681 level->field[x][y] = new_element;
4684 for (i = 0; i < MAX_PLAYERS; i++)
4686 // in case of all players set to the same field, use the first player
4687 int nr = MAX_PLAYERS - i - 1;
4688 int jx = cav->player_x[nr];
4689 int jy = cav->player_y[nr];
4691 if (jx != -1 && jy != -1)
4692 level->field[jx][jy] = EL_PLAYER_1 + nr;
4695 // time score is counted for each 10 seconds left in Emerald Mine levels
4696 level->time_score_base = 10;
4700 // ----------------------------------------------------------------------------
4701 // functions for loading SP level
4702 // ----------------------------------------------------------------------------
4704 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4706 struct LevelInfo_SP *level_sp = level->native_sp_level;
4707 LevelInfoType *header = &level_sp->header;
4710 level_sp->width = level->fieldx;
4711 level_sp->height = level->fieldy;
4713 for (x = 0; x < level->fieldx; x++)
4714 for (y = 0; y < level->fieldy; y++)
4715 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4717 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4719 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4720 header->LevelTitle[i] = level->name[i];
4721 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4723 header->InfotronsNeeded = level->gems_needed;
4725 header->SpecialPortCount = 0;
4727 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4729 boolean gravity_port_found = FALSE;
4730 boolean gravity_port_valid = FALSE;
4731 int gravity_port_flag;
4732 int gravity_port_base_element;
4733 int element = level->field[x][y];
4735 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4736 element <= EL_SP_GRAVITY_ON_PORT_UP)
4738 gravity_port_found = TRUE;
4739 gravity_port_valid = TRUE;
4740 gravity_port_flag = 1;
4741 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4743 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4744 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4746 gravity_port_found = TRUE;
4747 gravity_port_valid = TRUE;
4748 gravity_port_flag = 0;
4749 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4751 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4752 element <= EL_SP_GRAVITY_PORT_UP)
4754 // change R'n'D style gravity inverting special port to normal port
4755 // (there are no gravity inverting ports in native Supaplex engine)
4757 gravity_port_found = TRUE;
4758 gravity_port_valid = FALSE;
4759 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4762 if (gravity_port_found)
4764 if (gravity_port_valid &&
4765 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4767 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4769 port->PortLocation = (y * level->fieldx + x) * 2;
4770 port->Gravity = gravity_port_flag;
4772 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4774 header->SpecialPortCount++;
4778 // change special gravity port to normal port
4780 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4783 level_sp->playfield[x][y] = element - EL_SP_START;
4788 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4790 struct LevelInfo_SP *level_sp = level->native_sp_level;
4791 LevelInfoType *header = &level_sp->header;
4792 boolean num_invalid_elements = 0;
4795 level->fieldx = level_sp->width;
4796 level->fieldy = level_sp->height;
4798 for (x = 0; x < level->fieldx; x++)
4800 for (y = 0; y < level->fieldy; y++)
4802 int element_old = level_sp->playfield[x][y];
4803 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4805 if (element_new == EL_UNKNOWN)
4807 num_invalid_elements++;
4809 Debug("level:native:SP", "invalid element %d at position %d, %d",
4813 level->field[x][y] = element_new;
4817 if (num_invalid_elements > 0)
4818 Warn("found %d invalid elements%s", num_invalid_elements,
4819 (!options.debug ? " (use '--debug' for more details)" : ""));
4821 for (i = 0; i < MAX_PLAYERS; i++)
4822 level->initial_player_gravity[i] =
4823 (header->InitialGravity == 1 ? TRUE : FALSE);
4825 // skip leading spaces
4826 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4827 if (header->LevelTitle[i] != ' ')
4831 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4832 level->name[j] = header->LevelTitle[i];
4833 level->name[j] = '\0';
4835 // cut trailing spaces
4837 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4838 level->name[j - 1] = '\0';
4840 level->gems_needed = header->InfotronsNeeded;
4842 for (i = 0; i < header->SpecialPortCount; i++)
4844 SpecialPortType *port = &header->SpecialPort[i];
4845 int port_location = port->PortLocation;
4846 int gravity = port->Gravity;
4847 int port_x, port_y, port_element;
4849 port_x = (port_location / 2) % level->fieldx;
4850 port_y = (port_location / 2) / level->fieldx;
4852 if (port_x < 0 || port_x >= level->fieldx ||
4853 port_y < 0 || port_y >= level->fieldy)
4855 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4860 port_element = level->field[port_x][port_y];
4862 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4863 port_element > EL_SP_GRAVITY_PORT_UP)
4865 Warn("no special port at position (%d, %d)", port_x, port_y);
4870 // change previous (wrong) gravity inverting special port to either
4871 // gravity enabling special port or gravity disabling special port
4872 level->field[port_x][port_y] +=
4873 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4874 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4877 // change special gravity ports without database entries to normal ports
4878 for (x = 0; x < level->fieldx; x++)
4879 for (y = 0; y < level->fieldy; y++)
4880 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4881 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4882 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4884 level->time = 0; // no time limit
4885 level->amoeba_speed = 0;
4886 level->time_magic_wall = 0;
4887 level->time_wheel = 0;
4888 level->amoeba_content = EL_EMPTY;
4890 // original Supaplex does not use score values -- rate by playing time
4891 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4892 level->score[i] = 0;
4894 level->rate_time_over_score = TRUE;
4896 // there are no yamyams in supaplex levels
4897 for (i = 0; i < level->num_yamyam_contents; i++)
4898 for (x = 0; x < 3; x++)
4899 for (y = 0; y < 3; y++)
4900 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4903 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4905 struct LevelInfo_SP *level_sp = level->native_sp_level;
4906 struct DemoInfo_SP *demo = &level_sp->demo;
4909 // always start with reliable default values
4910 demo->is_available = FALSE;
4913 if (TAPE_IS_EMPTY(tape))
4916 demo->level_nr = tape.level_nr; // (currently not used)
4918 level_sp->header.DemoRandomSeed = tape.random_seed;
4922 for (i = 0; i < tape.length; i++)
4924 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4925 int demo_repeat = tape.pos[i].delay;
4926 int demo_entries = (demo_repeat + 15) / 16;
4928 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4930 Warn("tape truncated: size exceeds maximum SP demo size %d",
4936 for (j = 0; j < demo_repeat / 16; j++)
4937 demo->data[demo->length++] = 0xf0 | demo_action;
4939 if (demo_repeat % 16)
4940 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4943 demo->is_available = TRUE;
4946 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4948 struct LevelInfo_SP *level_sp = level->native_sp_level;
4949 struct DemoInfo_SP *demo = &level_sp->demo;
4950 char *filename = level->file_info.filename;
4953 // always start with reliable default values
4954 setTapeInfoToDefaults();
4956 if (!demo->is_available)
4959 tape.level_nr = demo->level_nr; // (currently not used)
4960 tape.random_seed = level_sp->header.DemoRandomSeed;
4962 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4965 tape.pos[tape.counter].delay = 0;
4967 for (i = 0; i < demo->length; i++)
4969 int demo_action = demo->data[i] & 0x0f;
4970 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4971 int tape_action = map_key_SP_to_RND(demo_action);
4972 int tape_repeat = demo_repeat + 1;
4973 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4974 boolean success = 0;
4977 for (j = 0; j < tape_repeat; j++)
4978 success = TapeAddAction(action);
4982 Warn("SP demo truncated: size exceeds maximum tape size %d",
4989 TapeHaltRecording();
4993 // ----------------------------------------------------------------------------
4994 // functions for loading MM level
4995 // ----------------------------------------------------------------------------
4997 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4999 struct LevelInfo_MM *level_mm = level->native_mm_level;
5002 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5003 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5005 level_mm->time = level->time;
5006 level_mm->kettles_needed = level->gems_needed;
5007 level_mm->auto_count_kettles = level->auto_count_gems;
5009 level_mm->mm_laser_red = level->mm_laser_red;
5010 level_mm->mm_laser_green = level->mm_laser_green;
5011 level_mm->mm_laser_blue = level->mm_laser_blue;
5013 level_mm->df_laser_red = level->df_laser_red;
5014 level_mm->df_laser_green = level->df_laser_green;
5015 level_mm->df_laser_blue = level->df_laser_blue;
5017 strcpy(level_mm->name, level->name);
5018 strcpy(level_mm->author, level->author);
5020 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
5021 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
5022 level_mm->score[SC_KEY] = level->score[SC_KEY];
5023 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5024 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5026 level_mm->amoeba_speed = level->amoeba_speed;
5027 level_mm->time_fuse = level->mm_time_fuse;
5028 level_mm->time_bomb = level->mm_time_bomb;
5029 level_mm->time_ball = level->mm_time_ball;
5030 level_mm->time_block = level->mm_time_block;
5032 level_mm->num_ball_contents = level->num_mm_ball_contents;
5033 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5034 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5035 level_mm->explode_ball = level->explode_mm_ball;
5037 for (i = 0; i < level->num_mm_ball_contents; i++)
5038 level_mm->ball_content[i] =
5039 map_element_RND_to_MM(level->mm_ball_content[i]);
5041 for (x = 0; x < level->fieldx; x++)
5042 for (y = 0; y < level->fieldy; y++)
5044 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5047 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5049 struct LevelInfo_MM *level_mm = level->native_mm_level;
5052 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5053 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5055 level->time = level_mm->time;
5056 level->gems_needed = level_mm->kettles_needed;
5057 level->auto_count_gems = level_mm->auto_count_kettles;
5059 level->mm_laser_red = level_mm->mm_laser_red;
5060 level->mm_laser_green = level_mm->mm_laser_green;
5061 level->mm_laser_blue = level_mm->mm_laser_blue;
5063 level->df_laser_red = level_mm->df_laser_red;
5064 level->df_laser_green = level_mm->df_laser_green;
5065 level->df_laser_blue = level_mm->df_laser_blue;
5067 strcpy(level->name, level_mm->name);
5069 // only overwrite author from 'levelinfo.conf' if author defined in level
5070 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5071 strcpy(level->author, level_mm->author);
5073 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5074 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5075 level->score[SC_KEY] = level_mm->score[SC_KEY];
5076 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5077 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5079 level->amoeba_speed = level_mm->amoeba_speed;
5080 level->mm_time_fuse = level_mm->time_fuse;
5081 level->mm_time_bomb = level_mm->time_bomb;
5082 level->mm_time_ball = level_mm->time_ball;
5083 level->mm_time_block = level_mm->time_block;
5085 level->num_mm_ball_contents = level_mm->num_ball_contents;
5086 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5087 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5088 level->explode_mm_ball = level_mm->explode_ball;
5090 for (i = 0; i < level->num_mm_ball_contents; i++)
5091 level->mm_ball_content[i] =
5092 map_element_MM_to_RND(level_mm->ball_content[i]);
5094 for (x = 0; x < level->fieldx; x++)
5095 for (y = 0; y < level->fieldy; y++)
5096 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5100 // ----------------------------------------------------------------------------
5101 // functions for loading DC level
5102 // ----------------------------------------------------------------------------
5104 #define DC_LEVEL_HEADER_SIZE 344
5106 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5109 static int last_data_encoded;
5113 int diff_hi, diff_lo;
5114 int data_hi, data_lo;
5115 unsigned short data_decoded;
5119 last_data_encoded = 0;
5126 diff = data_encoded - last_data_encoded;
5127 diff_hi = diff & ~0xff;
5128 diff_lo = diff & 0xff;
5132 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5133 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5134 data_hi = data_hi & 0xff00;
5136 data_decoded = data_hi | data_lo;
5138 last_data_encoded = data_encoded;
5140 offset1 = (offset1 + 1) % 31;
5141 offset2 = offset2 & 0xff;
5143 return data_decoded;
5146 static int getMappedElement_DC(int element)
5154 // 0x0117 - 0x036e: (?)
5157 // 0x042d - 0x0684: (?)
5173 element = EL_CRYSTAL;
5176 case 0x0e77: // quicksand (boulder)
5177 element = EL_QUICKSAND_FAST_FULL;
5180 case 0x0e99: // slow quicksand (boulder)
5181 element = EL_QUICKSAND_FULL;
5185 element = EL_EM_EXIT_OPEN;
5189 element = EL_EM_EXIT_CLOSED;
5193 element = EL_EM_STEEL_EXIT_OPEN;
5197 element = EL_EM_STEEL_EXIT_CLOSED;
5200 case 0x0f4f: // dynamite (lit 1)
5201 element = EL_EM_DYNAMITE_ACTIVE;
5204 case 0x0f57: // dynamite (lit 2)
5205 element = EL_EM_DYNAMITE_ACTIVE;
5208 case 0x0f5f: // dynamite (lit 3)
5209 element = EL_EM_DYNAMITE_ACTIVE;
5212 case 0x0f67: // dynamite (lit 4)
5213 element = EL_EM_DYNAMITE_ACTIVE;
5220 element = EL_AMOEBA_WET;
5224 element = EL_AMOEBA_DROP;
5228 element = EL_DC_MAGIC_WALL;
5232 element = EL_SPACESHIP_UP;
5236 element = EL_SPACESHIP_DOWN;
5240 element = EL_SPACESHIP_LEFT;
5244 element = EL_SPACESHIP_RIGHT;
5248 element = EL_BUG_UP;
5252 element = EL_BUG_DOWN;
5256 element = EL_BUG_LEFT;
5260 element = EL_BUG_RIGHT;
5264 element = EL_MOLE_UP;
5268 element = EL_MOLE_DOWN;
5272 element = EL_MOLE_LEFT;
5276 element = EL_MOLE_RIGHT;
5284 element = EL_YAMYAM_UP;
5288 element = EL_SWITCHGATE_OPEN;
5292 element = EL_SWITCHGATE_CLOSED;
5296 element = EL_DC_SWITCHGATE_SWITCH_UP;
5300 element = EL_TIMEGATE_CLOSED;
5303 case 0x144c: // conveyor belt switch (green)
5304 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5307 case 0x144f: // conveyor belt switch (red)
5308 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5311 case 0x1452: // conveyor belt switch (blue)
5312 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5316 element = EL_CONVEYOR_BELT_3_MIDDLE;
5320 element = EL_CONVEYOR_BELT_3_LEFT;
5324 element = EL_CONVEYOR_BELT_3_RIGHT;
5328 element = EL_CONVEYOR_BELT_1_MIDDLE;
5332 element = EL_CONVEYOR_BELT_1_LEFT;
5336 element = EL_CONVEYOR_BELT_1_RIGHT;
5340 element = EL_CONVEYOR_BELT_4_MIDDLE;
5344 element = EL_CONVEYOR_BELT_4_LEFT;
5348 element = EL_CONVEYOR_BELT_4_RIGHT;
5352 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5356 element = EL_EXPANDABLE_WALL_VERTICAL;
5360 element = EL_EXPANDABLE_WALL_ANY;
5363 case 0x14ce: // growing steel wall (left/right)
5364 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5367 case 0x14df: // growing steel wall (up/down)
5368 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5371 case 0x14e8: // growing steel wall (up/down/left/right)
5372 element = EL_EXPANDABLE_STEELWALL_ANY;
5376 element = EL_SHIELD_DEADLY;
5380 element = EL_EXTRA_TIME;
5388 element = EL_EMPTY_SPACE;
5391 case 0x1578: // quicksand (empty)
5392 element = EL_QUICKSAND_FAST_EMPTY;
5395 case 0x1579: // slow quicksand (empty)
5396 element = EL_QUICKSAND_EMPTY;
5406 element = EL_EM_DYNAMITE;
5409 case 0x15a1: // key (red)
5410 element = EL_EM_KEY_1;
5413 case 0x15a2: // key (yellow)
5414 element = EL_EM_KEY_2;
5417 case 0x15a3: // key (blue)
5418 element = EL_EM_KEY_4;
5421 case 0x15a4: // key (green)
5422 element = EL_EM_KEY_3;
5425 case 0x15a5: // key (white)
5426 element = EL_DC_KEY_WHITE;
5430 element = EL_WALL_SLIPPERY;
5437 case 0x15a8: // wall (not round)
5441 case 0x15a9: // (blue)
5442 element = EL_CHAR_A;
5445 case 0x15aa: // (blue)
5446 element = EL_CHAR_B;
5449 case 0x15ab: // (blue)
5450 element = EL_CHAR_C;
5453 case 0x15ac: // (blue)
5454 element = EL_CHAR_D;
5457 case 0x15ad: // (blue)
5458 element = EL_CHAR_E;
5461 case 0x15ae: // (blue)
5462 element = EL_CHAR_F;
5465 case 0x15af: // (blue)
5466 element = EL_CHAR_G;
5469 case 0x15b0: // (blue)
5470 element = EL_CHAR_H;
5473 case 0x15b1: // (blue)
5474 element = EL_CHAR_I;
5477 case 0x15b2: // (blue)
5478 element = EL_CHAR_J;
5481 case 0x15b3: // (blue)
5482 element = EL_CHAR_K;
5485 case 0x15b4: // (blue)
5486 element = EL_CHAR_L;
5489 case 0x15b5: // (blue)
5490 element = EL_CHAR_M;
5493 case 0x15b6: // (blue)
5494 element = EL_CHAR_N;
5497 case 0x15b7: // (blue)
5498 element = EL_CHAR_O;
5501 case 0x15b8: // (blue)
5502 element = EL_CHAR_P;
5505 case 0x15b9: // (blue)
5506 element = EL_CHAR_Q;
5509 case 0x15ba: // (blue)
5510 element = EL_CHAR_R;
5513 case 0x15bb: // (blue)
5514 element = EL_CHAR_S;
5517 case 0x15bc: // (blue)
5518 element = EL_CHAR_T;
5521 case 0x15bd: // (blue)
5522 element = EL_CHAR_U;
5525 case 0x15be: // (blue)
5526 element = EL_CHAR_V;
5529 case 0x15bf: // (blue)
5530 element = EL_CHAR_W;
5533 case 0x15c0: // (blue)
5534 element = EL_CHAR_X;
5537 case 0x15c1: // (blue)
5538 element = EL_CHAR_Y;
5541 case 0x15c2: // (blue)
5542 element = EL_CHAR_Z;
5545 case 0x15c3: // (blue)
5546 element = EL_CHAR_AUMLAUT;
5549 case 0x15c4: // (blue)
5550 element = EL_CHAR_OUMLAUT;
5553 case 0x15c5: // (blue)
5554 element = EL_CHAR_UUMLAUT;
5557 case 0x15c6: // (blue)
5558 element = EL_CHAR_0;
5561 case 0x15c7: // (blue)
5562 element = EL_CHAR_1;
5565 case 0x15c8: // (blue)
5566 element = EL_CHAR_2;
5569 case 0x15c9: // (blue)
5570 element = EL_CHAR_3;
5573 case 0x15ca: // (blue)
5574 element = EL_CHAR_4;
5577 case 0x15cb: // (blue)
5578 element = EL_CHAR_5;
5581 case 0x15cc: // (blue)
5582 element = EL_CHAR_6;
5585 case 0x15cd: // (blue)
5586 element = EL_CHAR_7;
5589 case 0x15ce: // (blue)
5590 element = EL_CHAR_8;
5593 case 0x15cf: // (blue)
5594 element = EL_CHAR_9;
5597 case 0x15d0: // (blue)
5598 element = EL_CHAR_PERIOD;
5601 case 0x15d1: // (blue)
5602 element = EL_CHAR_EXCLAM;
5605 case 0x15d2: // (blue)
5606 element = EL_CHAR_COLON;
5609 case 0x15d3: // (blue)
5610 element = EL_CHAR_LESS;
5613 case 0x15d4: // (blue)
5614 element = EL_CHAR_GREATER;
5617 case 0x15d5: // (blue)
5618 element = EL_CHAR_QUESTION;
5621 case 0x15d6: // (blue)
5622 element = EL_CHAR_COPYRIGHT;
5625 case 0x15d7: // (blue)
5626 element = EL_CHAR_UP;
5629 case 0x15d8: // (blue)
5630 element = EL_CHAR_DOWN;
5633 case 0x15d9: // (blue)
5634 element = EL_CHAR_BUTTON;
5637 case 0x15da: // (blue)
5638 element = EL_CHAR_PLUS;
5641 case 0x15db: // (blue)
5642 element = EL_CHAR_MINUS;
5645 case 0x15dc: // (blue)
5646 element = EL_CHAR_APOSTROPHE;
5649 case 0x15dd: // (blue)
5650 element = EL_CHAR_PARENLEFT;
5653 case 0x15de: // (blue)
5654 element = EL_CHAR_PARENRIGHT;
5657 case 0x15df: // (green)
5658 element = EL_CHAR_A;
5661 case 0x15e0: // (green)
5662 element = EL_CHAR_B;
5665 case 0x15e1: // (green)
5666 element = EL_CHAR_C;
5669 case 0x15e2: // (green)
5670 element = EL_CHAR_D;
5673 case 0x15e3: // (green)
5674 element = EL_CHAR_E;
5677 case 0x15e4: // (green)
5678 element = EL_CHAR_F;
5681 case 0x15e5: // (green)
5682 element = EL_CHAR_G;
5685 case 0x15e6: // (green)
5686 element = EL_CHAR_H;
5689 case 0x15e7: // (green)
5690 element = EL_CHAR_I;
5693 case 0x15e8: // (green)
5694 element = EL_CHAR_J;
5697 case 0x15e9: // (green)
5698 element = EL_CHAR_K;
5701 case 0x15ea: // (green)
5702 element = EL_CHAR_L;
5705 case 0x15eb: // (green)
5706 element = EL_CHAR_M;
5709 case 0x15ec: // (green)
5710 element = EL_CHAR_N;
5713 case 0x15ed: // (green)
5714 element = EL_CHAR_O;
5717 case 0x15ee: // (green)
5718 element = EL_CHAR_P;
5721 case 0x15ef: // (green)
5722 element = EL_CHAR_Q;
5725 case 0x15f0: // (green)
5726 element = EL_CHAR_R;
5729 case 0x15f1: // (green)
5730 element = EL_CHAR_S;
5733 case 0x15f2: // (green)
5734 element = EL_CHAR_T;
5737 case 0x15f3: // (green)
5738 element = EL_CHAR_U;
5741 case 0x15f4: // (green)
5742 element = EL_CHAR_V;
5745 case 0x15f5: // (green)
5746 element = EL_CHAR_W;
5749 case 0x15f6: // (green)
5750 element = EL_CHAR_X;
5753 case 0x15f7: // (green)
5754 element = EL_CHAR_Y;
5757 case 0x15f8: // (green)
5758 element = EL_CHAR_Z;
5761 case 0x15f9: // (green)
5762 element = EL_CHAR_AUMLAUT;
5765 case 0x15fa: // (green)
5766 element = EL_CHAR_OUMLAUT;
5769 case 0x15fb: // (green)
5770 element = EL_CHAR_UUMLAUT;
5773 case 0x15fc: // (green)
5774 element = EL_CHAR_0;
5777 case 0x15fd: // (green)
5778 element = EL_CHAR_1;
5781 case 0x15fe: // (green)
5782 element = EL_CHAR_2;
5785 case 0x15ff: // (green)
5786 element = EL_CHAR_3;
5789 case 0x1600: // (green)
5790 element = EL_CHAR_4;
5793 case 0x1601: // (green)
5794 element = EL_CHAR_5;
5797 case 0x1602: // (green)
5798 element = EL_CHAR_6;
5801 case 0x1603: // (green)
5802 element = EL_CHAR_7;
5805 case 0x1604: // (green)
5806 element = EL_CHAR_8;
5809 case 0x1605: // (green)
5810 element = EL_CHAR_9;
5813 case 0x1606: // (green)
5814 element = EL_CHAR_PERIOD;
5817 case 0x1607: // (green)
5818 element = EL_CHAR_EXCLAM;
5821 case 0x1608: // (green)
5822 element = EL_CHAR_COLON;
5825 case 0x1609: // (green)
5826 element = EL_CHAR_LESS;
5829 case 0x160a: // (green)
5830 element = EL_CHAR_GREATER;
5833 case 0x160b: // (green)
5834 element = EL_CHAR_QUESTION;
5837 case 0x160c: // (green)
5838 element = EL_CHAR_COPYRIGHT;
5841 case 0x160d: // (green)
5842 element = EL_CHAR_UP;
5845 case 0x160e: // (green)
5846 element = EL_CHAR_DOWN;
5849 case 0x160f: // (green)
5850 element = EL_CHAR_BUTTON;
5853 case 0x1610: // (green)
5854 element = EL_CHAR_PLUS;
5857 case 0x1611: // (green)
5858 element = EL_CHAR_MINUS;
5861 case 0x1612: // (green)
5862 element = EL_CHAR_APOSTROPHE;
5865 case 0x1613: // (green)
5866 element = EL_CHAR_PARENLEFT;
5869 case 0x1614: // (green)
5870 element = EL_CHAR_PARENRIGHT;
5873 case 0x1615: // (blue steel)
5874 element = EL_STEEL_CHAR_A;
5877 case 0x1616: // (blue steel)
5878 element = EL_STEEL_CHAR_B;
5881 case 0x1617: // (blue steel)
5882 element = EL_STEEL_CHAR_C;
5885 case 0x1618: // (blue steel)
5886 element = EL_STEEL_CHAR_D;
5889 case 0x1619: // (blue steel)
5890 element = EL_STEEL_CHAR_E;
5893 case 0x161a: // (blue steel)
5894 element = EL_STEEL_CHAR_F;
5897 case 0x161b: // (blue steel)
5898 element = EL_STEEL_CHAR_G;
5901 case 0x161c: // (blue steel)
5902 element = EL_STEEL_CHAR_H;
5905 case 0x161d: // (blue steel)
5906 element = EL_STEEL_CHAR_I;
5909 case 0x161e: // (blue steel)
5910 element = EL_STEEL_CHAR_J;
5913 case 0x161f: // (blue steel)
5914 element = EL_STEEL_CHAR_K;
5917 case 0x1620: // (blue steel)
5918 element = EL_STEEL_CHAR_L;
5921 case 0x1621: // (blue steel)
5922 element = EL_STEEL_CHAR_M;
5925 case 0x1622: // (blue steel)
5926 element = EL_STEEL_CHAR_N;
5929 case 0x1623: // (blue steel)
5930 element = EL_STEEL_CHAR_O;
5933 case 0x1624: // (blue steel)
5934 element = EL_STEEL_CHAR_P;
5937 case 0x1625: // (blue steel)
5938 element = EL_STEEL_CHAR_Q;
5941 case 0x1626: // (blue steel)
5942 element = EL_STEEL_CHAR_R;
5945 case 0x1627: // (blue steel)
5946 element = EL_STEEL_CHAR_S;
5949 case 0x1628: // (blue steel)
5950 element = EL_STEEL_CHAR_T;
5953 case 0x1629: // (blue steel)
5954 element = EL_STEEL_CHAR_U;
5957 case 0x162a: // (blue steel)
5958 element = EL_STEEL_CHAR_V;
5961 case 0x162b: // (blue steel)
5962 element = EL_STEEL_CHAR_W;
5965 case 0x162c: // (blue steel)
5966 element = EL_STEEL_CHAR_X;
5969 case 0x162d: // (blue steel)
5970 element = EL_STEEL_CHAR_Y;
5973 case 0x162e: // (blue steel)
5974 element = EL_STEEL_CHAR_Z;
5977 case 0x162f: // (blue steel)
5978 element = EL_STEEL_CHAR_AUMLAUT;
5981 case 0x1630: // (blue steel)
5982 element = EL_STEEL_CHAR_OUMLAUT;
5985 case 0x1631: // (blue steel)
5986 element = EL_STEEL_CHAR_UUMLAUT;
5989 case 0x1632: // (blue steel)
5990 element = EL_STEEL_CHAR_0;
5993 case 0x1633: // (blue steel)
5994 element = EL_STEEL_CHAR_1;
5997 case 0x1634: // (blue steel)
5998 element = EL_STEEL_CHAR_2;
6001 case 0x1635: // (blue steel)
6002 element = EL_STEEL_CHAR_3;
6005 case 0x1636: // (blue steel)
6006 element = EL_STEEL_CHAR_4;
6009 case 0x1637: // (blue steel)
6010 element = EL_STEEL_CHAR_5;
6013 case 0x1638: // (blue steel)
6014 element = EL_STEEL_CHAR_6;
6017 case 0x1639: // (blue steel)
6018 element = EL_STEEL_CHAR_7;
6021 case 0x163a: // (blue steel)
6022 element = EL_STEEL_CHAR_8;
6025 case 0x163b: // (blue steel)
6026 element = EL_STEEL_CHAR_9;
6029 case 0x163c: // (blue steel)
6030 element = EL_STEEL_CHAR_PERIOD;
6033 case 0x163d: // (blue steel)
6034 element = EL_STEEL_CHAR_EXCLAM;
6037 case 0x163e: // (blue steel)
6038 element = EL_STEEL_CHAR_COLON;
6041 case 0x163f: // (blue steel)
6042 element = EL_STEEL_CHAR_LESS;
6045 case 0x1640: // (blue steel)
6046 element = EL_STEEL_CHAR_GREATER;
6049 case 0x1641: // (blue steel)
6050 element = EL_STEEL_CHAR_QUESTION;
6053 case 0x1642: // (blue steel)
6054 element = EL_STEEL_CHAR_COPYRIGHT;
6057 case 0x1643: // (blue steel)
6058 element = EL_STEEL_CHAR_UP;
6061 case 0x1644: // (blue steel)
6062 element = EL_STEEL_CHAR_DOWN;
6065 case 0x1645: // (blue steel)
6066 element = EL_STEEL_CHAR_BUTTON;
6069 case 0x1646: // (blue steel)
6070 element = EL_STEEL_CHAR_PLUS;
6073 case 0x1647: // (blue steel)
6074 element = EL_STEEL_CHAR_MINUS;
6077 case 0x1648: // (blue steel)
6078 element = EL_STEEL_CHAR_APOSTROPHE;
6081 case 0x1649: // (blue steel)
6082 element = EL_STEEL_CHAR_PARENLEFT;
6085 case 0x164a: // (blue steel)
6086 element = EL_STEEL_CHAR_PARENRIGHT;
6089 case 0x164b: // (green steel)
6090 element = EL_STEEL_CHAR_A;
6093 case 0x164c: // (green steel)
6094 element = EL_STEEL_CHAR_B;
6097 case 0x164d: // (green steel)
6098 element = EL_STEEL_CHAR_C;
6101 case 0x164e: // (green steel)
6102 element = EL_STEEL_CHAR_D;
6105 case 0x164f: // (green steel)
6106 element = EL_STEEL_CHAR_E;
6109 case 0x1650: // (green steel)
6110 element = EL_STEEL_CHAR_F;
6113 case 0x1651: // (green steel)
6114 element = EL_STEEL_CHAR_G;
6117 case 0x1652: // (green steel)
6118 element = EL_STEEL_CHAR_H;
6121 case 0x1653: // (green steel)
6122 element = EL_STEEL_CHAR_I;
6125 case 0x1654: // (green steel)
6126 element = EL_STEEL_CHAR_J;
6129 case 0x1655: // (green steel)
6130 element = EL_STEEL_CHAR_K;
6133 case 0x1656: // (green steel)
6134 element = EL_STEEL_CHAR_L;
6137 case 0x1657: // (green steel)
6138 element = EL_STEEL_CHAR_M;
6141 case 0x1658: // (green steel)
6142 element = EL_STEEL_CHAR_N;
6145 case 0x1659: // (green steel)
6146 element = EL_STEEL_CHAR_O;
6149 case 0x165a: // (green steel)
6150 element = EL_STEEL_CHAR_P;
6153 case 0x165b: // (green steel)
6154 element = EL_STEEL_CHAR_Q;
6157 case 0x165c: // (green steel)
6158 element = EL_STEEL_CHAR_R;
6161 case 0x165d: // (green steel)
6162 element = EL_STEEL_CHAR_S;
6165 case 0x165e: // (green steel)
6166 element = EL_STEEL_CHAR_T;
6169 case 0x165f: // (green steel)
6170 element = EL_STEEL_CHAR_U;
6173 case 0x1660: // (green steel)
6174 element = EL_STEEL_CHAR_V;
6177 case 0x1661: // (green steel)
6178 element = EL_STEEL_CHAR_W;
6181 case 0x1662: // (green steel)
6182 element = EL_STEEL_CHAR_X;
6185 case 0x1663: // (green steel)
6186 element = EL_STEEL_CHAR_Y;
6189 case 0x1664: // (green steel)
6190 element = EL_STEEL_CHAR_Z;
6193 case 0x1665: // (green steel)
6194 element = EL_STEEL_CHAR_AUMLAUT;
6197 case 0x1666: // (green steel)
6198 element = EL_STEEL_CHAR_OUMLAUT;
6201 case 0x1667: // (green steel)
6202 element = EL_STEEL_CHAR_UUMLAUT;
6205 case 0x1668: // (green steel)
6206 element = EL_STEEL_CHAR_0;
6209 case 0x1669: // (green steel)
6210 element = EL_STEEL_CHAR_1;
6213 case 0x166a: // (green steel)
6214 element = EL_STEEL_CHAR_2;
6217 case 0x166b: // (green steel)
6218 element = EL_STEEL_CHAR_3;
6221 case 0x166c: // (green steel)
6222 element = EL_STEEL_CHAR_4;
6225 case 0x166d: // (green steel)
6226 element = EL_STEEL_CHAR_5;
6229 case 0x166e: // (green steel)
6230 element = EL_STEEL_CHAR_6;
6233 case 0x166f: // (green steel)
6234 element = EL_STEEL_CHAR_7;
6237 case 0x1670: // (green steel)
6238 element = EL_STEEL_CHAR_8;
6241 case 0x1671: // (green steel)
6242 element = EL_STEEL_CHAR_9;
6245 case 0x1672: // (green steel)
6246 element = EL_STEEL_CHAR_PERIOD;
6249 case 0x1673: // (green steel)
6250 element = EL_STEEL_CHAR_EXCLAM;
6253 case 0x1674: // (green steel)
6254 element = EL_STEEL_CHAR_COLON;
6257 case 0x1675: // (green steel)
6258 element = EL_STEEL_CHAR_LESS;
6261 case 0x1676: // (green steel)
6262 element = EL_STEEL_CHAR_GREATER;
6265 case 0x1677: // (green steel)
6266 element = EL_STEEL_CHAR_QUESTION;
6269 case 0x1678: // (green steel)
6270 element = EL_STEEL_CHAR_COPYRIGHT;
6273 case 0x1679: // (green steel)
6274 element = EL_STEEL_CHAR_UP;
6277 case 0x167a: // (green steel)
6278 element = EL_STEEL_CHAR_DOWN;
6281 case 0x167b: // (green steel)
6282 element = EL_STEEL_CHAR_BUTTON;
6285 case 0x167c: // (green steel)
6286 element = EL_STEEL_CHAR_PLUS;
6289 case 0x167d: // (green steel)
6290 element = EL_STEEL_CHAR_MINUS;
6293 case 0x167e: // (green steel)
6294 element = EL_STEEL_CHAR_APOSTROPHE;
6297 case 0x167f: // (green steel)
6298 element = EL_STEEL_CHAR_PARENLEFT;
6301 case 0x1680: // (green steel)
6302 element = EL_STEEL_CHAR_PARENRIGHT;
6305 case 0x1681: // gate (red)
6306 element = EL_EM_GATE_1;
6309 case 0x1682: // secret gate (red)
6310 element = EL_EM_GATE_1_GRAY;
6313 case 0x1683: // gate (yellow)
6314 element = EL_EM_GATE_2;
6317 case 0x1684: // secret gate (yellow)
6318 element = EL_EM_GATE_2_GRAY;
6321 case 0x1685: // gate (blue)
6322 element = EL_EM_GATE_4;
6325 case 0x1686: // secret gate (blue)
6326 element = EL_EM_GATE_4_GRAY;
6329 case 0x1687: // gate (green)
6330 element = EL_EM_GATE_3;
6333 case 0x1688: // secret gate (green)
6334 element = EL_EM_GATE_3_GRAY;
6337 case 0x1689: // gate (white)
6338 element = EL_DC_GATE_WHITE;
6341 case 0x168a: // secret gate (white)
6342 element = EL_DC_GATE_WHITE_GRAY;
6345 case 0x168b: // secret gate (no key)
6346 element = EL_DC_GATE_FAKE_GRAY;
6350 element = EL_ROBOT_WHEEL;
6354 element = EL_DC_TIMEGATE_SWITCH;
6358 element = EL_ACID_POOL_BOTTOM;
6362 element = EL_ACID_POOL_TOPLEFT;
6366 element = EL_ACID_POOL_TOPRIGHT;
6370 element = EL_ACID_POOL_BOTTOMLEFT;
6374 element = EL_ACID_POOL_BOTTOMRIGHT;
6378 element = EL_STEELWALL;
6382 element = EL_STEELWALL_SLIPPERY;
6385 case 0x1695: // steel wall (not round)
6386 element = EL_STEELWALL;
6389 case 0x1696: // steel wall (left)
6390 element = EL_DC_STEELWALL_1_LEFT;
6393 case 0x1697: // steel wall (bottom)
6394 element = EL_DC_STEELWALL_1_BOTTOM;
6397 case 0x1698: // steel wall (right)
6398 element = EL_DC_STEELWALL_1_RIGHT;
6401 case 0x1699: // steel wall (top)
6402 element = EL_DC_STEELWALL_1_TOP;
6405 case 0x169a: // steel wall (left/bottom)
6406 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6409 case 0x169b: // steel wall (right/bottom)
6410 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6413 case 0x169c: // steel wall (right/top)
6414 element = EL_DC_STEELWALL_1_TOPRIGHT;
6417 case 0x169d: // steel wall (left/top)
6418 element = EL_DC_STEELWALL_1_TOPLEFT;
6421 case 0x169e: // steel wall (right/bottom small)
6422 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6425 case 0x169f: // steel wall (left/bottom small)
6426 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6429 case 0x16a0: // steel wall (right/top small)
6430 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6433 case 0x16a1: // steel wall (left/top small)
6434 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6437 case 0x16a2: // steel wall (left/right)
6438 element = EL_DC_STEELWALL_1_VERTICAL;
6441 case 0x16a3: // steel wall (top/bottom)
6442 element = EL_DC_STEELWALL_1_HORIZONTAL;
6445 case 0x16a4: // steel wall 2 (left end)
6446 element = EL_DC_STEELWALL_2_LEFT;
6449 case 0x16a5: // steel wall 2 (right end)
6450 element = EL_DC_STEELWALL_2_RIGHT;
6453 case 0x16a6: // steel wall 2 (top end)
6454 element = EL_DC_STEELWALL_2_TOP;
6457 case 0x16a7: // steel wall 2 (bottom end)
6458 element = EL_DC_STEELWALL_2_BOTTOM;
6461 case 0x16a8: // steel wall 2 (left/right)
6462 element = EL_DC_STEELWALL_2_HORIZONTAL;
6465 case 0x16a9: // steel wall 2 (up/down)
6466 element = EL_DC_STEELWALL_2_VERTICAL;
6469 case 0x16aa: // steel wall 2 (mid)
6470 element = EL_DC_STEELWALL_2_MIDDLE;
6474 element = EL_SIGN_EXCLAMATION;
6478 element = EL_SIGN_RADIOACTIVITY;
6482 element = EL_SIGN_STOP;
6486 element = EL_SIGN_WHEELCHAIR;
6490 element = EL_SIGN_PARKING;
6494 element = EL_SIGN_NO_ENTRY;
6498 element = EL_SIGN_HEART;
6502 element = EL_SIGN_GIVE_WAY;
6506 element = EL_SIGN_ENTRY_FORBIDDEN;
6510 element = EL_SIGN_EMERGENCY_EXIT;
6514 element = EL_SIGN_YIN_YANG;
6518 element = EL_WALL_EMERALD;
6522 element = EL_WALL_DIAMOND;
6526 element = EL_WALL_PEARL;
6530 element = EL_WALL_CRYSTAL;
6534 element = EL_INVISIBLE_WALL;
6538 element = EL_INVISIBLE_STEELWALL;
6542 // EL_INVISIBLE_SAND
6545 element = EL_LIGHT_SWITCH;
6549 element = EL_ENVELOPE_1;
6553 if (element >= 0x0117 && element <= 0x036e) // (?)
6554 element = EL_DIAMOND;
6555 else if (element >= 0x042d && element <= 0x0684) // (?)
6556 element = EL_EMERALD;
6557 else if (element >= 0x157c && element <= 0x158b)
6559 else if (element >= 0x1590 && element <= 0x159f)
6560 element = EL_DC_LANDMINE;
6561 else if (element >= 0x16bc && element <= 0x16cb)
6562 element = EL_INVISIBLE_SAND;
6565 Warn("unknown Diamond Caves element 0x%04x", element);
6567 element = EL_UNKNOWN;
6572 return getMappedElement(element);
6575 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6577 byte header[DC_LEVEL_HEADER_SIZE];
6579 int envelope_header_pos = 62;
6580 int envelope_content_pos = 94;
6581 int level_name_pos = 251;
6582 int level_author_pos = 292;
6583 int envelope_header_len;
6584 int envelope_content_len;
6586 int level_author_len;
6588 int num_yamyam_contents;
6591 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6593 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6595 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6597 header[i * 2 + 0] = header_word >> 8;
6598 header[i * 2 + 1] = header_word & 0xff;
6601 // read some values from level header to check level decoding integrity
6602 fieldx = header[6] | (header[7] << 8);
6603 fieldy = header[8] | (header[9] << 8);
6604 num_yamyam_contents = header[60] | (header[61] << 8);
6606 // do some simple sanity checks to ensure that level was correctly decoded
6607 if (fieldx < 1 || fieldx > 256 ||
6608 fieldy < 1 || fieldy > 256 ||
6609 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6611 level->no_valid_file = TRUE;
6613 Warn("cannot decode level from stream -- using empty level");
6618 // maximum envelope header size is 31 bytes
6619 envelope_header_len = header[envelope_header_pos];
6620 // maximum envelope content size is 110 (156?) bytes
6621 envelope_content_len = header[envelope_content_pos];
6623 // maximum level title size is 40 bytes
6624 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6625 // maximum level author size is 30 (51?) bytes
6626 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6630 for (i = 0; i < envelope_header_len; i++)
6631 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6632 level->envelope[0].text[envelope_size++] =
6633 header[envelope_header_pos + 1 + i];
6635 if (envelope_header_len > 0 && envelope_content_len > 0)
6637 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6638 level->envelope[0].text[envelope_size++] = '\n';
6639 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6640 level->envelope[0].text[envelope_size++] = '\n';
6643 for (i = 0; i < envelope_content_len; i++)
6644 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6645 level->envelope[0].text[envelope_size++] =
6646 header[envelope_content_pos + 1 + i];
6648 level->envelope[0].text[envelope_size] = '\0';
6650 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6651 level->envelope[0].ysize = 10;
6652 level->envelope[0].autowrap = TRUE;
6653 level->envelope[0].centered = TRUE;
6655 for (i = 0; i < level_name_len; i++)
6656 level->name[i] = header[level_name_pos + 1 + i];
6657 level->name[level_name_len] = '\0';
6659 for (i = 0; i < level_author_len; i++)
6660 level->author[i] = header[level_author_pos + 1 + i];
6661 level->author[level_author_len] = '\0';
6663 num_yamyam_contents = header[60] | (header[61] << 8);
6664 level->num_yamyam_contents =
6665 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6667 for (i = 0; i < num_yamyam_contents; i++)
6669 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6671 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6672 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6674 if (i < MAX_ELEMENT_CONTENTS)
6675 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6679 fieldx = header[6] | (header[7] << 8);
6680 fieldy = header[8] | (header[9] << 8);
6681 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6682 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6684 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6686 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6687 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6689 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6690 level->field[x][y] = getMappedElement_DC(element_dc);
6693 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6694 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6695 level->field[x][y] = EL_PLAYER_1;
6697 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6698 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6699 level->field[x][y] = EL_PLAYER_2;
6701 level->gems_needed = header[18] | (header[19] << 8);
6703 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6704 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6705 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6706 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6707 level->score[SC_NUT] = header[28] | (header[29] << 8);
6708 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6709 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6710 level->score[SC_BUG] = header[34] | (header[35] << 8);
6711 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6712 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6713 level->score[SC_KEY] = header[40] | (header[41] << 8);
6714 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6716 level->time = header[44] | (header[45] << 8);
6718 level->amoeba_speed = header[46] | (header[47] << 8);
6719 level->time_light = header[48] | (header[49] << 8);
6720 level->time_timegate = header[50] | (header[51] << 8);
6721 level->time_wheel = header[52] | (header[53] << 8);
6722 level->time_magic_wall = header[54] | (header[55] << 8);
6723 level->extra_time = header[56] | (header[57] << 8);
6724 level->shield_normal_time = header[58] | (header[59] << 8);
6726 // shield and extra time elements do not have a score
6727 level->score[SC_SHIELD] = 0;
6728 level->extra_time_score = 0;
6730 // set time for normal and deadly shields to the same value
6731 level->shield_deadly_time = level->shield_normal_time;
6733 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6734 // can slip down from flat walls, like normal walls and steel walls
6735 level->em_slippery_gems = TRUE;
6737 // time score is counted for each 10 seconds left in Diamond Caves levels
6738 level->time_score_base = 10;
6741 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6742 struct LevelFileInfo *level_file_info,
6743 boolean level_info_only)
6745 char *filename = level_file_info->filename;
6747 int num_magic_bytes = 8;
6748 char magic_bytes[num_magic_bytes + 1];
6749 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6751 if (!(file = openFile(filename, MODE_READ)))
6753 level->no_valid_file = TRUE;
6755 if (!level_info_only)
6756 Warn("cannot read level '%s' -- using empty level", filename);
6761 // fseek(file, 0x0000, SEEK_SET);
6763 if (level_file_info->packed)
6765 // read "magic bytes" from start of file
6766 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6767 magic_bytes[0] = '\0';
6769 // check "magic bytes" for correct file format
6770 if (!strPrefix(magic_bytes, "DC2"))
6772 level->no_valid_file = TRUE;
6774 Warn("unknown DC level file '%s' -- using empty level", filename);
6779 if (strPrefix(magic_bytes, "DC2Win95") ||
6780 strPrefix(magic_bytes, "DC2Win98"))
6782 int position_first_level = 0x00fa;
6783 int extra_bytes = 4;
6786 // advance file stream to first level inside the level package
6787 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6789 // each block of level data is followed by block of non-level data
6790 num_levels_to_skip *= 2;
6792 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6793 while (num_levels_to_skip >= 0)
6795 // advance file stream to next level inside the level package
6796 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6798 level->no_valid_file = TRUE;
6800 Warn("cannot fseek in file '%s' -- using empty level", filename);
6805 // skip apparently unused extra bytes following each level
6806 ReadUnusedBytesFromFile(file, extra_bytes);
6808 // read size of next level in level package
6809 skip_bytes = getFile32BitLE(file);
6811 num_levels_to_skip--;
6816 level->no_valid_file = TRUE;
6818 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6824 LoadLevelFromFileStream_DC(file, level);
6830 // ----------------------------------------------------------------------------
6831 // functions for loading SB level
6832 // ----------------------------------------------------------------------------
6834 int getMappedElement_SB(int element_ascii, boolean use_ces)
6842 sb_element_mapping[] =
6844 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6845 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6846 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6847 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6848 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6849 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6850 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6851 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6858 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6859 if (element_ascii == sb_element_mapping[i].ascii)
6860 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6862 return EL_UNDEFINED;
6865 static void SetLevelSettings_SB(struct LevelInfo *level)
6869 level->use_step_counter = TRUE;
6872 level->score[SC_TIME_BONUS] = 0;
6873 level->time_score_base = 1;
6874 level->rate_time_over_score = TRUE;
6877 level->auto_exit_sokoban = TRUE;
6880 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6881 struct LevelFileInfo *level_file_info,
6882 boolean level_info_only)
6884 char *filename = level_file_info->filename;
6885 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6886 char last_comment[MAX_LINE_LEN];
6887 char level_name[MAX_LINE_LEN];
6890 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6891 boolean read_continued_line = FALSE;
6892 boolean reading_playfield = FALSE;
6893 boolean got_valid_playfield_line = FALSE;
6894 boolean invalid_playfield_char = FALSE;
6895 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6896 int file_level_nr = 0;
6897 int x = 0, y = 0; // initialized to make compilers happy
6899 last_comment[0] = '\0';
6900 level_name[0] = '\0';
6902 if (!(file = openFile(filename, MODE_READ)))
6904 level->no_valid_file = TRUE;
6906 if (!level_info_only)
6907 Warn("cannot read level '%s' -- using empty level", filename);
6912 while (!checkEndOfFile(file))
6914 // level successfully read, but next level may follow here
6915 if (!got_valid_playfield_line && reading_playfield)
6917 // read playfield from single level file -- skip remaining file
6918 if (!level_file_info->packed)
6921 if (file_level_nr >= num_levels_to_skip)
6926 last_comment[0] = '\0';
6927 level_name[0] = '\0';
6929 reading_playfield = FALSE;
6932 got_valid_playfield_line = FALSE;
6934 // read next line of input file
6935 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6938 // cut trailing line break (this can be newline and/or carriage return)
6939 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6940 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6943 // copy raw input line for later use (mainly debugging output)
6944 strcpy(line_raw, line);
6946 if (read_continued_line)
6948 // append new line to existing line, if there is enough space
6949 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6950 strcat(previous_line, line_ptr);
6952 strcpy(line, previous_line); // copy storage buffer to line
6954 read_continued_line = FALSE;
6957 // if the last character is '\', continue at next line
6958 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6960 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6961 strcpy(previous_line, line); // copy line to storage buffer
6963 read_continued_line = TRUE;
6969 if (line[0] == '\0')
6972 // extract comment text from comment line
6975 for (line_ptr = line; *line_ptr; line_ptr++)
6976 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6979 strcpy(last_comment, line_ptr);
6984 // extract level title text from line containing level title
6985 if (line[0] == '\'')
6987 strcpy(level_name, &line[1]);
6989 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6990 level_name[strlen(level_name) - 1] = '\0';
6995 // skip lines containing only spaces (or empty lines)
6996 for (line_ptr = line; *line_ptr; line_ptr++)
6997 if (*line_ptr != ' ')
6999 if (*line_ptr == '\0')
7002 // at this point, we have found a line containing part of a playfield
7004 got_valid_playfield_line = TRUE;
7006 if (!reading_playfield)
7008 reading_playfield = TRUE;
7009 invalid_playfield_char = FALSE;
7011 for (x = 0; x < MAX_LEV_FIELDX; x++)
7012 for (y = 0; y < MAX_LEV_FIELDY; y++)
7013 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7018 // start with topmost tile row
7022 // skip playfield line if larger row than allowed
7023 if (y >= MAX_LEV_FIELDY)
7026 // start with leftmost tile column
7029 // read playfield elements from line
7030 for (line_ptr = line; *line_ptr; line_ptr++)
7032 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7034 // stop parsing playfield line if larger column than allowed
7035 if (x >= MAX_LEV_FIELDX)
7038 if (mapped_sb_element == EL_UNDEFINED)
7040 invalid_playfield_char = TRUE;
7045 level->field[x][y] = mapped_sb_element;
7047 // continue with next tile column
7050 level->fieldx = MAX(x, level->fieldx);
7053 if (invalid_playfield_char)
7055 // if first playfield line, treat invalid lines as comment lines
7057 reading_playfield = FALSE;
7062 // continue with next tile row
7070 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7071 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7073 if (!reading_playfield)
7075 level->no_valid_file = TRUE;
7077 Warn("cannot read level '%s' -- using empty level", filename);
7082 if (*level_name != '\0')
7084 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7085 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7087 else if (*last_comment != '\0')
7089 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7090 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7094 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7097 // set all empty fields beyond the border walls to invisible steel wall
7098 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7100 if ((x == 0 || x == level->fieldx - 1 ||
7101 y == 0 || y == level->fieldy - 1) &&
7102 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7103 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7104 level->field, level->fieldx, level->fieldy);
7107 // set special level settings for Sokoban levels
7108 SetLevelSettings_SB(level);
7110 if (load_xsb_to_ces)
7112 // special global settings can now be set in level template
7113 level->use_custom_template = TRUE;
7118 // -------------------------------------------------------------------------
7119 // functions for handling native levels
7120 // -------------------------------------------------------------------------
7122 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7123 struct LevelFileInfo *level_file_info,
7124 boolean level_info_only)
7128 // determine position of requested level inside level package
7129 if (level_file_info->packed)
7130 pos = level_file_info->nr - leveldir_current->first_level;
7132 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7133 level->no_valid_file = TRUE;
7136 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7137 struct LevelFileInfo *level_file_info,
7138 boolean level_info_only)
7140 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7141 level->no_valid_file = TRUE;
7144 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7145 struct LevelFileInfo *level_file_info,
7146 boolean level_info_only)
7150 // determine position of requested level inside level package
7151 if (level_file_info->packed)
7152 pos = level_file_info->nr - leveldir_current->first_level;
7154 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7155 level->no_valid_file = TRUE;
7158 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7159 struct LevelFileInfo *level_file_info,
7160 boolean level_info_only)
7162 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7163 level->no_valid_file = TRUE;
7166 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7168 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7169 CopyNativeLevel_RND_to_BD(level);
7170 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7171 CopyNativeLevel_RND_to_EM(level);
7172 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7173 CopyNativeLevel_RND_to_SP(level);
7174 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7175 CopyNativeLevel_RND_to_MM(level);
7178 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7180 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7181 CopyNativeLevel_BD_to_RND(level);
7182 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7183 CopyNativeLevel_EM_to_RND(level);
7184 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7185 CopyNativeLevel_SP_to_RND(level);
7186 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7187 CopyNativeLevel_MM_to_RND(level);
7190 void SaveNativeLevel(struct LevelInfo *level)
7192 // saving native level files only supported for some game engines
7193 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7194 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7197 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7198 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7199 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7200 char *filename = getLevelFilenameFromBasename(basename);
7202 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7205 boolean success = FALSE;
7207 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7209 CopyNativeLevel_RND_to_BD(level);
7210 // CopyNativeTape_RND_to_BD(level);
7212 success = SaveNativeLevel_BD(filename);
7214 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7216 CopyNativeLevel_RND_to_SP(level);
7217 CopyNativeTape_RND_to_SP(level);
7219 success = SaveNativeLevel_SP(filename);
7223 Request("Native level file saved!", REQ_CONFIRM);
7225 Request("Failed to save native level file!", REQ_CONFIRM);
7229 // ----------------------------------------------------------------------------
7230 // functions for loading generic level
7231 // ----------------------------------------------------------------------------
7233 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7234 struct LevelFileInfo *level_file_info,
7235 boolean level_info_only)
7237 // always start with reliable default values
7238 setLevelInfoToDefaults(level, level_info_only, TRUE);
7240 switch (level_file_info->type)
7242 case LEVEL_FILE_TYPE_RND:
7243 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7246 case LEVEL_FILE_TYPE_BD:
7247 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7248 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7251 case LEVEL_FILE_TYPE_EM:
7252 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7253 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7256 case LEVEL_FILE_TYPE_SP:
7257 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7258 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7261 case LEVEL_FILE_TYPE_MM:
7262 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7263 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7266 case LEVEL_FILE_TYPE_DC:
7267 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7270 case LEVEL_FILE_TYPE_SB:
7271 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7275 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7279 // if level file is invalid, restore level structure to default values
7280 if (level->no_valid_file)
7281 setLevelInfoToDefaults(level, level_info_only, FALSE);
7283 if (check_special_flags("use_native_bd_game_engine"))
7284 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7286 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7287 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7289 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7290 CopyNativeLevel_Native_to_RND(level);
7293 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7295 static struct LevelFileInfo level_file_info;
7297 // always start with reliable default values
7298 setFileInfoToDefaults(&level_file_info);
7300 level_file_info.nr = 0; // unknown level number
7301 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7303 setString(&level_file_info.filename, filename);
7305 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7308 static void LoadLevel_InitVersion(struct LevelInfo *level)
7312 if (leveldir_current == NULL) // only when dumping level
7315 // all engine modifications also valid for levels which use latest engine
7316 if (level->game_version < VERSION_IDENT(3,2,0,5))
7318 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7319 level->time_score_base = 10;
7322 if (leveldir_current->latest_engine)
7324 // ---------- use latest game engine --------------------------------------
7326 /* For all levels which are forced to use the latest game engine version
7327 (normally all but user contributed, private and undefined levels), set
7328 the game engine version to the actual version; this allows for actual
7329 corrections in the game engine to take effect for existing, converted
7330 levels (from "classic" or other existing games) to make the emulation
7331 of the corresponding game more accurate, while (hopefully) not breaking
7332 existing levels created from other players. */
7334 level->game_version = GAME_VERSION_ACTUAL;
7336 /* Set special EM style gems behaviour: EM style gems slip down from
7337 normal, steel and growing wall. As this is a more fundamental change,
7338 it seems better to set the default behaviour to "off" (as it is more
7339 natural) and make it configurable in the level editor (as a property
7340 of gem style elements). Already existing converted levels (neither
7341 private nor contributed levels) are changed to the new behaviour. */
7343 if (level->file_version < FILE_VERSION_2_0)
7344 level->em_slippery_gems = TRUE;
7349 // ---------- use game engine the level was created with --------------------
7351 /* For all levels which are not forced to use the latest game engine
7352 version (normally user contributed, private and undefined levels),
7353 use the version of the game engine the levels were created for.
7355 Since 2.0.1, the game engine version is now directly stored
7356 in the level file (chunk "VERS"), so there is no need anymore
7357 to set the game version from the file version (except for old,
7358 pre-2.0 levels, where the game version is still taken from the
7359 file format version used to store the level -- see above). */
7361 // player was faster than enemies in 1.0.0 and before
7362 if (level->file_version == FILE_VERSION_1_0)
7363 for (i = 0; i < MAX_PLAYERS; i++)
7364 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7366 // default behaviour for EM style gems was "slippery" only in 2.0.1
7367 if (level->game_version == VERSION_IDENT(2,0,1,0))
7368 level->em_slippery_gems = TRUE;
7370 // springs could be pushed over pits before (pre-release version) 2.2.0
7371 if (level->game_version < VERSION_IDENT(2,2,0,0))
7372 level->use_spring_bug = TRUE;
7374 if (level->game_version < VERSION_IDENT(3,2,0,5))
7376 // time orb caused limited time in endless time levels before 3.2.0-5
7377 level->use_time_orb_bug = TRUE;
7379 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7380 level->block_snap_field = FALSE;
7382 // extra time score was same value as time left score before 3.2.0-5
7383 level->extra_time_score = level->score[SC_TIME_BONUS];
7386 if (level->game_version < VERSION_IDENT(3,2,0,7))
7388 // default behaviour for snapping was "not continuous" before 3.2.0-7
7389 level->continuous_snapping = FALSE;
7392 // only few elements were able to actively move into acid before 3.1.0
7393 // trigger settings did not exist before 3.1.0; set to default "any"
7394 if (level->game_version < VERSION_IDENT(3,1,0,0))
7396 // correct "can move into acid" settings (all zero in old levels)
7398 level->can_move_into_acid_bits = 0; // nothing can move into acid
7399 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7401 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7402 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7403 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7404 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7406 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7407 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7409 // correct trigger settings (stored as zero == "none" in old levels)
7411 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7413 int element = EL_CUSTOM_START + i;
7414 struct ElementInfo *ei = &element_info[element];
7416 for (j = 0; j < ei->num_change_pages; j++)
7418 struct ElementChangeInfo *change = &ei->change_page[j];
7420 change->trigger_player = CH_PLAYER_ANY;
7421 change->trigger_page = CH_PAGE_ANY;
7426 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7428 int element = EL_CUSTOM_256;
7429 struct ElementInfo *ei = &element_info[element];
7430 struct ElementChangeInfo *change = &ei->change_page[0];
7432 /* This is needed to fix a problem that was caused by a bugfix in function
7433 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7434 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7435 not replace walkable elements, but instead just placed the player on it,
7436 without placing the Sokoban field under the player). Unfortunately, this
7437 breaks "Snake Bite" style levels when the snake is halfway through a door
7438 that just closes (the snake head is still alive and can be moved in this
7439 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7440 player (without Sokoban element) which then gets killed as designed). */
7442 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7443 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7444 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7445 change->target_element = EL_PLAYER_1;
7448 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7449 if (level->game_version < VERSION_IDENT(3,2,5,0))
7451 /* This is needed to fix a problem that was caused by a bugfix in function
7452 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7453 corrects the behaviour when a custom element changes to another custom
7454 element with a higher element number that has change actions defined.
7455 Normally, only one change per frame is allowed for custom elements.
7456 Therefore, it is checked if a custom element already changed in the
7457 current frame; if it did, subsequent changes are suppressed.
7458 Unfortunately, this is only checked for element changes, but not for
7459 change actions, which are still executed. As the function above loops
7460 through all custom elements from lower to higher, an element change
7461 resulting in a lower CE number won't be checked again, while a target
7462 element with a higher number will also be checked, and potential change
7463 actions will get executed for this CE, too (which is wrong), while
7464 further changes are ignored (which is correct). As this bugfix breaks
7465 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7466 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7467 behaviour for existing levels and tapes that make use of this bug */
7469 level->use_action_after_change_bug = TRUE;
7472 // not centering level after relocating player was default only in 3.2.3
7473 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7474 level->shifted_relocation = TRUE;
7476 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7477 if (level->game_version < VERSION_IDENT(3,2,6,0))
7478 level->em_explodes_by_fire = TRUE;
7480 // levels were solved by the first player entering an exit up to 4.1.0.0
7481 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7482 level->solved_by_one_player = TRUE;
7484 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7485 if (level->game_version < VERSION_IDENT(4,1,1,1))
7486 level->use_life_bugs = TRUE;
7488 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7489 if (level->game_version < VERSION_IDENT(4,1,1,1))
7490 level->sb_objects_needed = FALSE;
7492 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7493 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7494 level->finish_dig_collect = FALSE;
7496 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7497 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7498 level->keep_walkable_ce = TRUE;
7501 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7503 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7506 // check if this level is (not) a Sokoban level
7507 for (y = 0; y < level->fieldy; y++)
7508 for (x = 0; x < level->fieldx; x++)
7509 if (!IS_SB_ELEMENT(Tile[x][y]))
7510 is_sokoban_level = FALSE;
7512 if (is_sokoban_level)
7514 // set special level settings for Sokoban levels
7515 SetLevelSettings_SB(level);
7519 static void LoadLevel_InitSettings(struct LevelInfo *level)
7521 // adjust level settings for (non-native) Sokoban-style levels
7522 LoadLevel_InitSettings_SB(level);
7524 // rename levels with title "nameless level" or if renaming is forced
7525 if (leveldir_current->empty_level_name != NULL &&
7526 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7527 leveldir_current->force_level_name))
7528 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7529 leveldir_current->empty_level_name, level_nr);
7532 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7536 // map elements that have changed in newer versions
7537 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7538 level->game_version);
7539 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7540 for (x = 0; x < 3; x++)
7541 for (y = 0; y < 3; y++)
7542 level->yamyam_content[i].e[x][y] =
7543 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7544 level->game_version);
7548 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7552 // map custom element change events that have changed in newer versions
7553 // (these following values were accidentally changed in version 3.0.1)
7554 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7555 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7557 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7559 int element = EL_CUSTOM_START + i;
7561 // order of checking and copying events to be mapped is important
7562 // (do not change the start and end value -- they are constant)
7563 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7565 if (HAS_CHANGE_EVENT(element, j - 2))
7567 SET_CHANGE_EVENT(element, j - 2, FALSE);
7568 SET_CHANGE_EVENT(element, j, TRUE);
7572 // order of checking and copying events to be mapped is important
7573 // (do not change the start and end value -- they are constant)
7574 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7576 if (HAS_CHANGE_EVENT(element, j - 1))
7578 SET_CHANGE_EVENT(element, j - 1, FALSE);
7579 SET_CHANGE_EVENT(element, j, TRUE);
7585 // initialize "can_change" field for old levels with only one change page
7586 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7588 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7590 int element = EL_CUSTOM_START + i;
7592 if (CAN_CHANGE(element))
7593 element_info[element].change->can_change = TRUE;
7597 // correct custom element values (for old levels without these options)
7598 if (level->game_version < VERSION_IDENT(3,1,1,0))
7600 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7602 int element = EL_CUSTOM_START + i;
7603 struct ElementInfo *ei = &element_info[element];
7605 if (ei->access_direction == MV_NO_DIRECTION)
7606 ei->access_direction = MV_ALL_DIRECTIONS;
7610 // correct custom element values (fix invalid values for all versions)
7613 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7615 int element = EL_CUSTOM_START + i;
7616 struct ElementInfo *ei = &element_info[element];
7618 for (j = 0; j < ei->num_change_pages; j++)
7620 struct ElementChangeInfo *change = &ei->change_page[j];
7622 if (change->trigger_player == CH_PLAYER_NONE)
7623 change->trigger_player = CH_PLAYER_ANY;
7625 if (change->trigger_side == CH_SIDE_NONE)
7626 change->trigger_side = CH_SIDE_ANY;
7631 // initialize "can_explode" field for old levels which did not store this
7632 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7633 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7635 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7637 int element = EL_CUSTOM_START + i;
7639 if (EXPLODES_1X1_OLD(element))
7640 element_info[element].explosion_type = EXPLODES_1X1;
7642 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7643 EXPLODES_SMASHED(element) ||
7644 EXPLODES_IMPACT(element)));
7648 // correct previously hard-coded move delay values for maze runner style
7649 if (level->game_version < VERSION_IDENT(3,1,1,0))
7651 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7653 int element = EL_CUSTOM_START + i;
7655 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7657 // previously hard-coded and therefore ignored
7658 element_info[element].move_delay_fixed = 9;
7659 element_info[element].move_delay_random = 0;
7664 // set some other uninitialized values of custom elements in older levels
7665 if (level->game_version < VERSION_IDENT(3,1,0,0))
7667 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7669 int element = EL_CUSTOM_START + i;
7671 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7673 element_info[element].explosion_delay = 17;
7674 element_info[element].ignition_delay = 8;
7678 // set mouse click change events to work for left/middle/right mouse button
7679 if (level->game_version < VERSION_IDENT(4,2,3,0))
7681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7683 int element = EL_CUSTOM_START + i;
7684 struct ElementInfo *ei = &element_info[element];
7686 for (j = 0; j < ei->num_change_pages; j++)
7688 struct ElementChangeInfo *change = &ei->change_page[j];
7690 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7691 change->has_event[CE_PRESSED_BY_MOUSE] ||
7692 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7693 change->has_event[CE_MOUSE_PRESSED_ON_X])
7694 change->trigger_side = CH_SIDE_ANY;
7700 static void LoadLevel_InitElements(struct LevelInfo *level)
7702 LoadLevel_InitStandardElements(level);
7704 if (level->file_has_custom_elements)
7705 LoadLevel_InitCustomElements(level);
7707 // initialize element properties for level editor etc.
7708 InitElementPropertiesEngine(level->game_version);
7709 InitElementPropertiesGfxElement();
7712 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7716 // map elements that have changed in newer versions
7717 for (y = 0; y < level->fieldy; y++)
7718 for (x = 0; x < level->fieldx; x++)
7719 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7720 level->game_version);
7722 // clear unused playfield data (nicer if level gets resized in editor)
7723 for (x = 0; x < MAX_LEV_FIELDX; x++)
7724 for (y = 0; y < MAX_LEV_FIELDY; y++)
7725 if (x >= level->fieldx || y >= level->fieldy)
7726 level->field[x][y] = EL_EMPTY;
7728 // copy elements to runtime playfield array
7729 for (x = 0; x < MAX_LEV_FIELDX; x++)
7730 for (y = 0; y < MAX_LEV_FIELDY; y++)
7731 Tile[x][y] = level->field[x][y];
7733 // initialize level size variables for faster access
7734 lev_fieldx = level->fieldx;
7735 lev_fieldy = level->fieldy;
7737 // determine border element for this level
7738 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7739 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7744 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7746 struct LevelFileInfo *level_file_info = &level->file_info;
7748 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7749 CopyNativeLevel_RND_to_Native(level);
7752 static void LoadLevelTemplate_LoadAndInit(void)
7754 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7756 LoadLevel_InitVersion(&level_template);
7757 LoadLevel_InitElements(&level_template);
7758 LoadLevel_InitSettings(&level_template);
7760 ActivateLevelTemplate();
7763 void LoadLevelTemplate(int nr)
7765 if (!fileExists(getGlobalLevelTemplateFilename()))
7767 Warn("no level template found for this level");
7772 setLevelFileInfo(&level_template.file_info, nr);
7774 LoadLevelTemplate_LoadAndInit();
7777 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7779 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7781 LoadLevelTemplate_LoadAndInit();
7784 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7786 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7788 if (level.use_custom_template)
7790 if (network_level != NULL)
7791 LoadNetworkLevelTemplate(network_level);
7793 LoadLevelTemplate(-1);
7796 LoadLevel_InitVersion(&level);
7797 LoadLevel_InitElements(&level);
7798 LoadLevel_InitPlayfield(&level);
7799 LoadLevel_InitSettings(&level);
7801 LoadLevel_InitNativeEngines(&level);
7804 void LoadLevel(int nr)
7806 SetLevelSetInfo(leveldir_current->identifier, nr);
7808 setLevelFileInfo(&level.file_info, nr);
7810 LoadLevel_LoadAndInit(NULL);
7813 void LoadLevelInfoOnly(int nr)
7815 setLevelFileInfo(&level.file_info, nr);
7817 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7820 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7822 SetLevelSetInfo(network_level->leveldir_identifier,
7823 network_level->file_info.nr);
7825 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7827 LoadLevel_LoadAndInit(network_level);
7830 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7834 chunk_size += putFileVersion(file, level->file_version);
7835 chunk_size += putFileVersion(file, level->game_version);
7840 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7844 chunk_size += putFile16BitBE(file, level->creation_date.year);
7845 chunk_size += putFile8Bit(file, level->creation_date.month);
7846 chunk_size += putFile8Bit(file, level->creation_date.day);
7851 #if ENABLE_HISTORIC_CHUNKS
7852 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7856 putFile8Bit(file, level->fieldx);
7857 putFile8Bit(file, level->fieldy);
7859 putFile16BitBE(file, level->time);
7860 putFile16BitBE(file, level->gems_needed);
7862 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7863 putFile8Bit(file, level->name[i]);
7865 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7866 putFile8Bit(file, level->score[i]);
7868 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7869 for (y = 0; y < 3; y++)
7870 for (x = 0; x < 3; x++)
7871 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7872 level->yamyam_content[i].e[x][y]));
7873 putFile8Bit(file, level->amoeba_speed);
7874 putFile8Bit(file, level->time_magic_wall);
7875 putFile8Bit(file, level->time_wheel);
7876 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7877 level->amoeba_content));
7878 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7879 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7880 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7881 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7883 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7885 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7886 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7887 putFile32BitBE(file, level->can_move_into_acid_bits);
7888 putFile8Bit(file, level->dont_collide_with_bits);
7890 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7891 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7893 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7894 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7895 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7897 putFile8Bit(file, level->game_engine_type);
7899 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7903 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7908 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7909 chunk_size += putFile8Bit(file, level->name[i]);
7914 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7919 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7920 chunk_size += putFile8Bit(file, level->author[i]);
7925 #if ENABLE_HISTORIC_CHUNKS
7926 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7931 for (y = 0; y < level->fieldy; y++)
7932 for (x = 0; x < level->fieldx; x++)
7933 if (level->encoding_16bit_field)
7934 chunk_size += putFile16BitBE(file, level->field[x][y]);
7936 chunk_size += putFile8Bit(file, level->field[x][y]);
7942 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7947 for (y = 0; y < level->fieldy; y++)
7948 for (x = 0; x < level->fieldx; x++)
7949 chunk_size += putFile16BitBE(file, level->field[x][y]);
7954 #if ENABLE_HISTORIC_CHUNKS
7955 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7959 putFile8Bit(file, EL_YAMYAM);
7960 putFile8Bit(file, level->num_yamyam_contents);
7961 putFile8Bit(file, 0);
7962 putFile8Bit(file, 0);
7964 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7965 for (y = 0; y < 3; y++)
7966 for (x = 0; x < 3; x++)
7967 if (level->encoding_16bit_field)
7968 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7970 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7974 #if ENABLE_HISTORIC_CHUNKS
7975 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7978 int num_contents, content_xsize, content_ysize;
7979 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7981 if (element == EL_YAMYAM)
7983 num_contents = level->num_yamyam_contents;
7987 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7988 for (y = 0; y < 3; y++)
7989 for (x = 0; x < 3; x++)
7990 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7992 else if (element == EL_BD_AMOEBA)
7998 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7999 for (y = 0; y < 3; y++)
8000 for (x = 0; x < 3; x++)
8001 content_array[i][x][y] = EL_EMPTY;
8002 content_array[0][0][0] = level->amoeba_content;
8006 // chunk header already written -- write empty chunk data
8007 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8009 Warn("cannot save content for element '%d'", element);
8014 putFile16BitBE(file, element);
8015 putFile8Bit(file, num_contents);
8016 putFile8Bit(file, content_xsize);
8017 putFile8Bit(file, content_ysize);
8019 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8021 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8022 for (y = 0; y < 3; y++)
8023 for (x = 0; x < 3; x++)
8024 putFile16BitBE(file, content_array[i][x][y]);
8028 #if ENABLE_HISTORIC_CHUNKS
8029 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8031 int envelope_nr = element - EL_ENVELOPE_1;
8032 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8036 chunk_size += putFile16BitBE(file, element);
8037 chunk_size += putFile16BitBE(file, envelope_len);
8038 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8039 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8041 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8042 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8044 for (i = 0; i < envelope_len; i++)
8045 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8051 #if ENABLE_HISTORIC_CHUNKS
8052 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8053 int num_changed_custom_elements)
8057 putFile16BitBE(file, num_changed_custom_elements);
8059 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8061 int element = EL_CUSTOM_START + i;
8063 struct ElementInfo *ei = &element_info[element];
8065 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8067 if (check < num_changed_custom_elements)
8069 putFile16BitBE(file, element);
8070 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8077 if (check != num_changed_custom_elements) // should not happen
8078 Warn("inconsistent number of custom element properties");
8082 #if ENABLE_HISTORIC_CHUNKS
8083 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8084 int num_changed_custom_elements)
8088 putFile16BitBE(file, num_changed_custom_elements);
8090 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8092 int element = EL_CUSTOM_START + i;
8094 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8096 if (check < num_changed_custom_elements)
8098 putFile16BitBE(file, element);
8099 putFile16BitBE(file, element_info[element].change->target_element);
8106 if (check != num_changed_custom_elements) // should not happen
8107 Warn("inconsistent number of custom target elements");
8111 #if ENABLE_HISTORIC_CHUNKS
8112 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8113 int num_changed_custom_elements)
8115 int i, j, x, y, check = 0;
8117 putFile16BitBE(file, num_changed_custom_elements);
8119 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8121 int element = EL_CUSTOM_START + i;
8122 struct ElementInfo *ei = &element_info[element];
8124 if (ei->modified_settings)
8126 if (check < num_changed_custom_elements)
8128 putFile16BitBE(file, element);
8130 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8131 putFile8Bit(file, ei->description[j]);
8133 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8135 // some free bytes for future properties and padding
8136 WriteUnusedBytesToFile(file, 7);
8138 putFile8Bit(file, ei->use_gfx_element);
8139 putFile16BitBE(file, ei->gfx_element_initial);
8141 putFile8Bit(file, ei->collect_score_initial);
8142 putFile8Bit(file, ei->collect_count_initial);
8144 putFile16BitBE(file, ei->push_delay_fixed);
8145 putFile16BitBE(file, ei->push_delay_random);
8146 putFile16BitBE(file, ei->move_delay_fixed);
8147 putFile16BitBE(file, ei->move_delay_random);
8149 putFile16BitBE(file, ei->move_pattern);
8150 putFile8Bit(file, ei->move_direction_initial);
8151 putFile8Bit(file, ei->move_stepsize);
8153 for (y = 0; y < 3; y++)
8154 for (x = 0; x < 3; x++)
8155 putFile16BitBE(file, ei->content.e[x][y]);
8157 putFile32BitBE(file, ei->change->events);
8159 putFile16BitBE(file, ei->change->target_element);
8161 putFile16BitBE(file, ei->change->delay_fixed);
8162 putFile16BitBE(file, ei->change->delay_random);
8163 putFile16BitBE(file, ei->change->delay_frames);
8165 putFile16BitBE(file, ei->change->initial_trigger_element);
8167 putFile8Bit(file, ei->change->explode);
8168 putFile8Bit(file, ei->change->use_target_content);
8169 putFile8Bit(file, ei->change->only_if_complete);
8170 putFile8Bit(file, ei->change->use_random_replace);
8172 putFile8Bit(file, ei->change->random_percentage);
8173 putFile8Bit(file, ei->change->replace_when);
8175 for (y = 0; y < 3; y++)
8176 for (x = 0; x < 3; x++)
8177 putFile16BitBE(file, ei->change->content.e[x][y]);
8179 putFile8Bit(file, ei->slippery_type);
8181 // some free bytes for future properties and padding
8182 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8189 if (check != num_changed_custom_elements) // should not happen
8190 Warn("inconsistent number of custom element properties");
8194 #if ENABLE_HISTORIC_CHUNKS
8195 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8197 struct ElementInfo *ei = &element_info[element];
8200 // ---------- custom element base property values (96 bytes) ----------------
8202 putFile16BitBE(file, element);
8204 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8205 putFile8Bit(file, ei->description[i]);
8207 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8209 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8211 putFile8Bit(file, ei->num_change_pages);
8213 putFile16BitBE(file, ei->ce_value_fixed_initial);
8214 putFile16BitBE(file, ei->ce_value_random_initial);
8215 putFile8Bit(file, ei->use_last_ce_value);
8217 putFile8Bit(file, ei->use_gfx_element);
8218 putFile16BitBE(file, ei->gfx_element_initial);
8220 putFile8Bit(file, ei->collect_score_initial);
8221 putFile8Bit(file, ei->collect_count_initial);
8223 putFile8Bit(file, ei->drop_delay_fixed);
8224 putFile8Bit(file, ei->push_delay_fixed);
8225 putFile8Bit(file, ei->drop_delay_random);
8226 putFile8Bit(file, ei->push_delay_random);
8227 putFile16BitBE(file, ei->move_delay_fixed);
8228 putFile16BitBE(file, ei->move_delay_random);
8230 // bits 0 - 15 of "move_pattern" ...
8231 putFile16BitBE(file, ei->move_pattern & 0xffff);
8232 putFile8Bit(file, ei->move_direction_initial);
8233 putFile8Bit(file, ei->move_stepsize);
8235 putFile8Bit(file, ei->slippery_type);
8237 for (y = 0; y < 3; y++)
8238 for (x = 0; x < 3; x++)
8239 putFile16BitBE(file, ei->content.e[x][y]);
8241 putFile16BitBE(file, ei->move_enter_element);
8242 putFile16BitBE(file, ei->move_leave_element);
8243 putFile8Bit(file, ei->move_leave_type);
8245 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8246 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8248 putFile8Bit(file, ei->access_direction);
8250 putFile8Bit(file, ei->explosion_delay);
8251 putFile8Bit(file, ei->ignition_delay);
8252 putFile8Bit(file, ei->explosion_type);
8254 // some free bytes for future custom property values and padding
8255 WriteUnusedBytesToFile(file, 1);
8257 // ---------- change page property values (48 bytes) ------------------------
8259 for (i = 0; i < ei->num_change_pages; i++)
8261 struct ElementChangeInfo *change = &ei->change_page[i];
8262 unsigned int event_bits;
8264 // bits 0 - 31 of "has_event[]" ...
8266 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8267 if (change->has_event[j])
8268 event_bits |= (1u << j);
8269 putFile32BitBE(file, event_bits);
8271 putFile16BitBE(file, change->target_element);
8273 putFile16BitBE(file, change->delay_fixed);
8274 putFile16BitBE(file, change->delay_random);
8275 putFile16BitBE(file, change->delay_frames);
8277 putFile16BitBE(file, change->initial_trigger_element);
8279 putFile8Bit(file, change->explode);
8280 putFile8Bit(file, change->use_target_content);
8281 putFile8Bit(file, change->only_if_complete);
8282 putFile8Bit(file, change->use_random_replace);
8284 putFile8Bit(file, change->random_percentage);
8285 putFile8Bit(file, change->replace_when);
8287 for (y = 0; y < 3; y++)
8288 for (x = 0; x < 3; x++)
8289 putFile16BitBE(file, change->target_content.e[x][y]);
8291 putFile8Bit(file, change->can_change);
8293 putFile8Bit(file, change->trigger_side);
8295 putFile8Bit(file, change->trigger_player);
8296 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8297 log_2(change->trigger_page)));
8299 putFile8Bit(file, change->has_action);
8300 putFile8Bit(file, change->action_type);
8301 putFile8Bit(file, change->action_mode);
8302 putFile16BitBE(file, change->action_arg);
8304 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8306 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8307 if (change->has_event[j])
8308 event_bits |= (1u << (j - 32));
8309 putFile8Bit(file, event_bits);
8314 #if ENABLE_HISTORIC_CHUNKS
8315 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8317 struct ElementInfo *ei = &element_info[element];
8318 struct ElementGroupInfo *group = ei->group;
8321 putFile16BitBE(file, element);
8323 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8324 putFile8Bit(file, ei->description[i]);
8326 putFile8Bit(file, group->num_elements);
8328 putFile8Bit(file, ei->use_gfx_element);
8329 putFile16BitBE(file, ei->gfx_element_initial);
8331 putFile8Bit(file, group->choice_mode);
8333 // some free bytes for future values and padding
8334 WriteUnusedBytesToFile(file, 3);
8336 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8337 putFile16BitBE(file, group->element[i]);
8341 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8342 boolean write_element)
8344 int save_type = entry->save_type;
8345 int data_type = entry->data_type;
8346 int conf_type = entry->conf_type;
8347 int byte_mask = conf_type & CONF_MASK_BYTES;
8348 int element = entry->element;
8349 int default_value = entry->default_value;
8351 boolean modified = FALSE;
8353 if (byte_mask != CONF_MASK_MULTI_BYTES)
8355 void *value_ptr = entry->value;
8356 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8359 // check if any settings have been modified before saving them
8360 if (value != default_value)
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 += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8373 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8374 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8377 else if (data_type == TYPE_STRING)
8379 char *default_string = entry->default_string;
8380 char *string = (char *)(entry->value);
8381 int string_length = strlen(string);
8384 // check if any settings have been modified before saving them
8385 if (!strEqual(string, default_string))
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, string_length);
8399 for (i = 0; i < string_length; i++)
8400 num_bytes += putFile8Bit(file, string[i]);
8402 else if (data_type == TYPE_ELEMENT_LIST)
8404 int *element_array = (int *)(entry->value);
8405 int num_elements = *(int *)(entry->num_entities);
8408 // check if any settings have been modified before saving them
8409 for (i = 0; i < num_elements; i++)
8410 if (element_array[i] != default_value)
8413 // do not save if explicitly told or if unmodified default settings
8414 if ((save_type == SAVE_CONF_NEVER) ||
8415 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8419 num_bytes += putFile16BitBE(file, element);
8421 num_bytes += putFile8Bit(file, conf_type);
8422 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8424 for (i = 0; i < num_elements; i++)
8425 num_bytes += putFile16BitBE(file, element_array[i]);
8427 else if (data_type == TYPE_CONTENT_LIST)
8429 struct Content *content = (struct Content *)(entry->value);
8430 int num_contents = *(int *)(entry->num_entities);
8433 // check if any settings have been modified before saving them
8434 for (i = 0; i < num_contents; i++)
8435 for (y = 0; y < 3; y++)
8436 for (x = 0; x < 3; x++)
8437 if (content[i].e[x][y] != default_value)
8440 // do not save if explicitly told or if unmodified default settings
8441 if ((save_type == SAVE_CONF_NEVER) ||
8442 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8446 num_bytes += putFile16BitBE(file, element);
8448 num_bytes += putFile8Bit(file, conf_type);
8449 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8451 for (i = 0; i < num_contents; i++)
8452 for (y = 0; y < 3; y++)
8453 for (x = 0; x < 3; x++)
8454 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8460 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8465 li = *level; // copy level data into temporary buffer
8467 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8468 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8473 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8478 li = *level; // copy level data into temporary buffer
8480 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8481 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8486 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8488 int envelope_nr = element - EL_ENVELOPE_1;
8492 chunk_size += putFile16BitBE(file, element);
8494 // copy envelope data into temporary buffer
8495 xx_envelope = level->envelope[envelope_nr];
8497 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8498 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8503 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8505 struct ElementInfo *ei = &element_info[element];
8509 chunk_size += putFile16BitBE(file, element);
8511 xx_ei = *ei; // copy element data into temporary buffer
8513 // set default description string for this specific element
8514 strcpy(xx_default_description, getDefaultElementDescription(ei));
8516 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8517 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8519 for (i = 0; i < ei->num_change_pages; i++)
8521 struct ElementChangeInfo *change = &ei->change_page[i];
8523 xx_current_change_page = i;
8525 xx_change = *change; // copy change data into temporary buffer
8528 setEventBitsFromEventFlags(change);
8530 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8531 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8538 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8540 struct ElementInfo *ei = &element_info[element];
8541 struct ElementGroupInfo *group = ei->group;
8545 chunk_size += putFile16BitBE(file, element);
8547 xx_ei = *ei; // copy element data into temporary buffer
8548 xx_group = *group; // copy group data into temporary buffer
8550 // set default description string for this specific element
8551 strcpy(xx_default_description, getDefaultElementDescription(ei));
8553 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8554 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8559 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8561 struct ElementInfo *ei = &element_info[element];
8565 chunk_size += putFile16BitBE(file, element);
8567 xx_ei = *ei; // copy element data into temporary buffer
8569 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8570 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8575 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8576 boolean save_as_template)
8582 if (!(file = fopen(filename, MODE_WRITE)))
8584 Warn("cannot save level file '%s'", filename);
8589 level->file_version = FILE_VERSION_ACTUAL;
8590 level->game_version = GAME_VERSION_ACTUAL;
8592 level->creation_date = getCurrentDate();
8594 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8595 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8597 chunk_size = SaveLevel_VERS(NULL, level);
8598 putFileChunkBE(file, "VERS", chunk_size);
8599 SaveLevel_VERS(file, level);
8601 chunk_size = SaveLevel_DATE(NULL, level);
8602 putFileChunkBE(file, "DATE", chunk_size);
8603 SaveLevel_DATE(file, level);
8605 chunk_size = SaveLevel_NAME(NULL, level);
8606 putFileChunkBE(file, "NAME", chunk_size);
8607 SaveLevel_NAME(file, level);
8609 chunk_size = SaveLevel_AUTH(NULL, level);
8610 putFileChunkBE(file, "AUTH", chunk_size);
8611 SaveLevel_AUTH(file, level);
8613 chunk_size = SaveLevel_INFO(NULL, level);
8614 putFileChunkBE(file, "INFO", chunk_size);
8615 SaveLevel_INFO(file, level);
8617 chunk_size = SaveLevel_BODY(NULL, level);
8618 putFileChunkBE(file, "BODY", chunk_size);
8619 SaveLevel_BODY(file, level);
8621 chunk_size = SaveLevel_ELEM(NULL, level);
8622 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8624 putFileChunkBE(file, "ELEM", chunk_size);
8625 SaveLevel_ELEM(file, level);
8628 for (i = 0; i < NUM_ENVELOPES; i++)
8630 int element = EL_ENVELOPE_1 + i;
8632 chunk_size = SaveLevel_NOTE(NULL, level, element);
8633 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8635 putFileChunkBE(file, "NOTE", chunk_size);
8636 SaveLevel_NOTE(file, level, element);
8640 // if not using template level, check for non-default custom/group elements
8641 if (!level->use_custom_template || save_as_template)
8643 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8645 int element = EL_CUSTOM_START + i;
8647 chunk_size = SaveLevel_CUSX(NULL, level, element);
8648 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8650 putFileChunkBE(file, "CUSX", chunk_size);
8651 SaveLevel_CUSX(file, level, element);
8655 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8657 int element = EL_GROUP_START + i;
8659 chunk_size = SaveLevel_GRPX(NULL, level, element);
8660 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8662 putFileChunkBE(file, "GRPX", chunk_size);
8663 SaveLevel_GRPX(file, level, element);
8667 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8669 int element = GET_EMPTY_ELEMENT(i);
8671 chunk_size = SaveLevel_EMPX(NULL, level, element);
8672 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8674 putFileChunkBE(file, "EMPX", chunk_size);
8675 SaveLevel_EMPX(file, level, element);
8682 SetFilePermissions(filename, PERMS_PRIVATE);
8685 void SaveLevel(int nr)
8687 char *filename = getDefaultLevelFilename(nr);
8689 SaveLevelFromFilename(&level, filename, FALSE);
8692 void SaveLevelTemplate(void)
8694 char *filename = getLocalLevelTemplateFilename();
8696 SaveLevelFromFilename(&level, filename, TRUE);
8699 boolean SaveLevelChecked(int nr)
8701 char *filename = getDefaultLevelFilename(nr);
8702 boolean new_level = !fileExists(filename);
8703 boolean level_saved = FALSE;
8705 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8710 Request("Level saved!", REQ_CONFIRM);
8718 void DumpLevel(struct LevelInfo *level)
8720 if (level->no_level_file || level->no_valid_file)
8722 Warn("cannot dump -- no valid level file found");
8728 Print("Level xxx (file version %08d, game version %08d)\n",
8729 level->file_version, level->game_version);
8732 Print("Level author: '%s'\n", level->author);
8733 Print("Level title: '%s'\n", level->name);
8735 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8737 Print("Level time: %d seconds\n", level->time);
8738 Print("Gems needed: %d\n", level->gems_needed);
8740 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8741 Print("Time for wheel: %d seconds\n", level->time_wheel);
8742 Print("Time for light: %d seconds\n", level->time_light);
8743 Print("Time for timegate: %d seconds\n", level->time_timegate);
8745 Print("Amoeba speed: %d\n", level->amoeba_speed);
8748 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8749 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8750 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8751 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8752 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8753 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8759 for (i = 0; i < NUM_ENVELOPES; i++)
8761 char *text = level->envelope[i].text;
8762 int text_len = strlen(text);
8763 boolean has_text = FALSE;
8765 for (j = 0; j < text_len; j++)
8766 if (text[j] != ' ' && text[j] != '\n')
8772 Print("Envelope %d:\n'%s'\n", i + 1, text);
8780 void DumpLevels(void)
8782 static LevelDirTree *dumplevel_leveldir = NULL;
8784 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8785 global.dumplevel_leveldir);
8787 if (dumplevel_leveldir == NULL)
8788 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8790 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8791 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8792 Fail("no such level number: %d", global.dumplevel_level_nr);
8794 leveldir_current = dumplevel_leveldir;
8796 LoadLevel(global.dumplevel_level_nr);
8803 // ============================================================================
8804 // tape file functions
8805 // ============================================================================
8807 static void setTapeInfoToDefaults(void)
8811 // always start with reliable default values (empty tape)
8814 // default values (also for pre-1.2 tapes) with only the first player
8815 tape.player_participates[0] = TRUE;
8816 for (i = 1; i < MAX_PLAYERS; i++)
8817 tape.player_participates[i] = FALSE;
8819 // at least one (default: the first) player participates in every tape
8820 tape.num_participating_players = 1;
8822 tape.property_bits = TAPE_PROPERTY_NONE;
8824 tape.level_nr = level_nr;
8826 tape.changed = FALSE;
8827 tape.solved = FALSE;
8829 tape.recording = FALSE;
8830 tape.playing = FALSE;
8831 tape.pausing = FALSE;
8833 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8834 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8836 tape.no_info_chunk = TRUE;
8837 tape.no_valid_file = FALSE;
8840 static int getTapePosSize(struct TapeInfo *tape)
8842 int tape_pos_size = 0;
8844 if (tape->use_key_actions)
8845 tape_pos_size += tape->num_participating_players;
8847 if (tape->use_mouse_actions)
8848 tape_pos_size += 3; // x and y position and mouse button mask
8850 tape_pos_size += 1; // tape action delay value
8852 return tape_pos_size;
8855 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8857 tape->use_key_actions = FALSE;
8858 tape->use_mouse_actions = FALSE;
8860 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8861 tape->use_key_actions = TRUE;
8863 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8864 tape->use_mouse_actions = TRUE;
8867 static int getTapeActionValue(struct TapeInfo *tape)
8869 return (tape->use_key_actions &&
8870 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8871 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8872 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8873 TAPE_ACTIONS_DEFAULT);
8876 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8878 tape->file_version = getFileVersion(file);
8879 tape->game_version = getFileVersion(file);
8884 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8888 tape->random_seed = getFile32BitBE(file);
8889 tape->date = getFile32BitBE(file);
8890 tape->length = getFile32BitBE(file);
8892 // read header fields that are new since version 1.2
8893 if (tape->file_version >= FILE_VERSION_1_2)
8895 byte store_participating_players = getFile8Bit(file);
8898 // since version 1.2, tapes store which players participate in the tape
8899 tape->num_participating_players = 0;
8900 for (i = 0; i < MAX_PLAYERS; i++)
8902 tape->player_participates[i] = FALSE;
8904 if (store_participating_players & (1 << i))
8906 tape->player_participates[i] = TRUE;
8907 tape->num_participating_players++;
8911 setTapeActionFlags(tape, getFile8Bit(file));
8913 tape->property_bits = getFile8Bit(file);
8914 tape->solved = getFile8Bit(file);
8916 engine_version = getFileVersion(file);
8917 if (engine_version > 0)
8918 tape->engine_version = engine_version;
8920 tape->engine_version = tape->game_version;
8926 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8928 tape->scr_fieldx = getFile8Bit(file);
8929 tape->scr_fieldy = getFile8Bit(file);
8934 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8936 char *level_identifier = NULL;
8937 int level_identifier_size;
8940 tape->no_info_chunk = FALSE;
8942 level_identifier_size = getFile16BitBE(file);
8944 level_identifier = checked_malloc(level_identifier_size);
8946 for (i = 0; i < level_identifier_size; i++)
8947 level_identifier[i] = getFile8Bit(file);
8949 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8950 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8952 checked_free(level_identifier);
8954 tape->level_nr = getFile16BitBE(file);
8956 chunk_size = 2 + level_identifier_size + 2;
8961 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8964 int tape_pos_size = getTapePosSize(tape);
8965 int chunk_size_expected = tape_pos_size * tape->length;
8967 if (chunk_size_expected != chunk_size)
8969 ReadUnusedBytesFromFile(file, chunk_size);
8970 return chunk_size_expected;
8973 for (i = 0; i < tape->length; i++)
8975 if (i >= MAX_TAPE_LEN)
8977 Warn("tape truncated -- size exceeds maximum tape size %d",
8980 // tape too large; read and ignore remaining tape data from this chunk
8981 for (;i < tape->length; i++)
8982 ReadUnusedBytesFromFile(file, tape_pos_size);
8987 if (tape->use_key_actions)
8989 for (j = 0; j < MAX_PLAYERS; j++)
8991 tape->pos[i].action[j] = MV_NONE;
8993 if (tape->player_participates[j])
8994 tape->pos[i].action[j] = getFile8Bit(file);
8998 if (tape->use_mouse_actions)
9000 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
9001 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
9002 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9005 tape->pos[i].delay = getFile8Bit(file);
9007 if (tape->file_version == FILE_VERSION_1_0)
9009 // eliminate possible diagonal moves in old tapes
9010 // this is only for backward compatibility
9012 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9013 byte action = tape->pos[i].action[0];
9014 int k, num_moves = 0;
9016 for (k = 0; k < 4; k++)
9018 if (action & joy_dir[k])
9020 tape->pos[i + num_moves].action[0] = joy_dir[k];
9022 tape->pos[i + num_moves].delay = 0;
9031 tape->length += num_moves;
9034 else if (tape->file_version < FILE_VERSION_2_0)
9036 // convert pre-2.0 tapes to new tape format
9038 if (tape->pos[i].delay > 1)
9041 tape->pos[i + 1] = tape->pos[i];
9042 tape->pos[i + 1].delay = 1;
9045 for (j = 0; j < MAX_PLAYERS; j++)
9046 tape->pos[i].action[j] = MV_NONE;
9047 tape->pos[i].delay--;
9054 if (checkEndOfFile(file))
9058 if (i != tape->length)
9059 chunk_size = tape_pos_size * i;
9064 static void LoadTape_SokobanSolution(char *filename)
9067 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9069 if (!(file = openFile(filename, MODE_READ)))
9071 tape.no_valid_file = TRUE;
9076 while (!checkEndOfFile(file))
9078 unsigned char c = getByteFromFile(file);
9080 if (checkEndOfFile(file))
9087 tape.pos[tape.length].action[0] = MV_UP;
9088 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9094 tape.pos[tape.length].action[0] = MV_DOWN;
9095 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9101 tape.pos[tape.length].action[0] = MV_LEFT;
9102 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9108 tape.pos[tape.length].action[0] = MV_RIGHT;
9109 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9117 // ignore white-space characters
9121 tape.no_valid_file = TRUE;
9123 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9131 if (tape.no_valid_file)
9134 tape.length_frames = GetTapeLengthFrames();
9135 tape.length_seconds = GetTapeLengthSeconds();
9138 void LoadTapeFromFilename(char *filename)
9140 char cookie[MAX_LINE_LEN];
9141 char chunk_name[CHUNK_ID_LEN + 1];
9145 // always start with reliable default values
9146 setTapeInfoToDefaults();
9148 if (strSuffix(filename, ".sln"))
9150 LoadTape_SokobanSolution(filename);
9155 if (!(file = openFile(filename, MODE_READ)))
9157 tape.no_valid_file = TRUE;
9162 getFileChunkBE(file, chunk_name, NULL);
9163 if (strEqual(chunk_name, "RND1"))
9165 getFile32BitBE(file); // not used
9167 getFileChunkBE(file, chunk_name, NULL);
9168 if (!strEqual(chunk_name, "TAPE"))
9170 tape.no_valid_file = TRUE;
9172 Warn("unknown format of tape file '%s'", filename);
9179 else // check for pre-2.0 file format with cookie string
9181 strcpy(cookie, chunk_name);
9182 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9184 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9185 cookie[strlen(cookie) - 1] = '\0';
9187 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9189 tape.no_valid_file = TRUE;
9191 Warn("unknown format of tape file '%s'", filename);
9198 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9200 tape.no_valid_file = TRUE;
9202 Warn("unsupported version of tape file '%s'", filename);
9209 // pre-2.0 tape files have no game version, so use file version here
9210 tape.game_version = tape.file_version;
9213 if (tape.file_version < FILE_VERSION_1_2)
9215 // tape files from versions before 1.2.0 without chunk structure
9216 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9217 LoadTape_BODY(file, 2 * tape.length, &tape);
9225 int (*loader)(File *, int, struct TapeInfo *);
9229 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9230 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9231 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9232 { "INFO", -1, LoadTape_INFO },
9233 { "BODY", -1, LoadTape_BODY },
9237 while (getFileChunkBE(file, chunk_name, &chunk_size))
9241 while (chunk_info[i].name != NULL &&
9242 !strEqual(chunk_name, chunk_info[i].name))
9245 if (chunk_info[i].name == NULL)
9247 Warn("unknown chunk '%s' in tape file '%s'",
9248 chunk_name, filename);
9250 ReadUnusedBytesFromFile(file, chunk_size);
9252 else if (chunk_info[i].size != -1 &&
9253 chunk_info[i].size != chunk_size)
9255 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9256 chunk_size, chunk_name, filename);
9258 ReadUnusedBytesFromFile(file, chunk_size);
9262 // call function to load this tape chunk
9263 int chunk_size_expected =
9264 (chunk_info[i].loader)(file, chunk_size, &tape);
9266 // the size of some chunks cannot be checked before reading other
9267 // chunks first (like "HEAD" and "BODY") that contain some header
9268 // information, so check them here
9269 if (chunk_size_expected != chunk_size)
9271 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9272 chunk_size, chunk_name, filename);
9280 tape.length_frames = GetTapeLengthFrames();
9281 tape.length_seconds = GetTapeLengthSeconds();
9284 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9286 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9288 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9289 tape.engine_version);
9293 void LoadTape(int nr)
9295 char *filename = getTapeFilename(nr);
9297 LoadTapeFromFilename(filename);
9300 void LoadSolutionTape(int nr)
9302 char *filename = getSolutionTapeFilename(nr);
9304 LoadTapeFromFilename(filename);
9306 if (TAPE_IS_EMPTY(tape))
9308 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9309 level.native_bd_level->replay != NULL)
9310 CopyNativeTape_BD_to_RND(&level);
9311 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9312 level.native_sp_level->demo.is_available)
9313 CopyNativeTape_SP_to_RND(&level);
9317 void LoadScoreTape(char *score_tape_basename, int nr)
9319 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9321 LoadTapeFromFilename(filename);
9324 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9326 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9328 LoadTapeFromFilename(filename);
9331 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9333 // chunk required for team mode tapes with non-default screen size
9334 return (tape->num_participating_players > 1 &&
9335 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9336 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9339 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9341 putFileVersion(file, tape->file_version);
9342 putFileVersion(file, tape->game_version);
9345 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9348 byte store_participating_players = 0;
9350 // set bits for participating players for compact storage
9351 for (i = 0; i < MAX_PLAYERS; i++)
9352 if (tape->player_participates[i])
9353 store_participating_players |= (1 << i);
9355 putFile32BitBE(file, tape->random_seed);
9356 putFile32BitBE(file, tape->date);
9357 putFile32BitBE(file, tape->length);
9359 putFile8Bit(file, store_participating_players);
9361 putFile8Bit(file, getTapeActionValue(tape));
9363 putFile8Bit(file, tape->property_bits);
9364 putFile8Bit(file, tape->solved);
9366 putFileVersion(file, tape->engine_version);
9369 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9371 putFile8Bit(file, tape->scr_fieldx);
9372 putFile8Bit(file, tape->scr_fieldy);
9375 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9377 int level_identifier_size = strlen(tape->level_identifier) + 1;
9380 putFile16BitBE(file, level_identifier_size);
9382 for (i = 0; i < level_identifier_size; i++)
9383 putFile8Bit(file, tape->level_identifier[i]);
9385 putFile16BitBE(file, tape->level_nr);
9388 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9392 for (i = 0; i < tape->length; i++)
9394 if (tape->use_key_actions)
9396 for (j = 0; j < MAX_PLAYERS; j++)
9397 if (tape->player_participates[j])
9398 putFile8Bit(file, tape->pos[i].action[j]);
9401 if (tape->use_mouse_actions)
9403 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9404 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9405 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9408 putFile8Bit(file, tape->pos[i].delay);
9412 void SaveTapeToFilename(char *filename)
9416 int info_chunk_size;
9417 int body_chunk_size;
9419 if (!(file = fopen(filename, MODE_WRITE)))
9421 Warn("cannot save level recording file '%s'", filename);
9426 tape_pos_size = getTapePosSize(&tape);
9428 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9429 body_chunk_size = tape_pos_size * tape.length;
9431 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9432 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9434 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9435 SaveTape_VERS(file, &tape);
9437 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9438 SaveTape_HEAD(file, &tape);
9440 if (checkSaveTape_SCRN(&tape))
9442 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9443 SaveTape_SCRN(file, &tape);
9446 putFileChunkBE(file, "INFO", info_chunk_size);
9447 SaveTape_INFO(file, &tape);
9449 putFileChunkBE(file, "BODY", body_chunk_size);
9450 SaveTape_BODY(file, &tape);
9454 SetFilePermissions(filename, PERMS_PRIVATE);
9457 static void SaveTapeExt(char *filename)
9461 tape.file_version = FILE_VERSION_ACTUAL;
9462 tape.game_version = GAME_VERSION_ACTUAL;
9464 tape.num_participating_players = 0;
9466 // count number of participating players
9467 for (i = 0; i < MAX_PLAYERS; i++)
9468 if (tape.player_participates[i])
9469 tape.num_participating_players++;
9471 SaveTapeToFilename(filename);
9473 tape.changed = FALSE;
9476 void SaveTape(int nr)
9478 char *filename = getTapeFilename(nr);
9480 InitTapeDirectory(leveldir_current->subdir);
9482 SaveTapeExt(filename);
9485 void SaveScoreTape(int nr)
9487 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9489 // used instead of "leveldir_current->subdir" (for network games)
9490 InitScoreTapeDirectory(levelset.identifier, nr);
9492 SaveTapeExt(filename);
9495 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9496 unsigned int req_state_added)
9498 char *filename = getTapeFilename(nr);
9499 boolean new_tape = !fileExists(filename);
9500 boolean tape_saved = FALSE;
9502 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9507 Request(msg_saved, REQ_CONFIRM | req_state_added);
9515 boolean SaveTapeChecked(int nr)
9517 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9520 boolean SaveTapeChecked_LevelSolved(int nr)
9522 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9523 "Level solved! Tape saved!", REQ_STAY_OPEN);
9526 void DumpTape(struct TapeInfo *tape)
9528 int tape_frame_counter;
9531 if (tape->no_valid_file)
9533 Warn("cannot dump -- no valid tape file found");
9540 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9541 tape->level_nr, tape->file_version, tape->game_version);
9542 Print(" (effective engine version %08d)\n",
9543 tape->engine_version);
9544 Print("Level series identifier: '%s'\n", tape->level_identifier);
9546 Print("Solution tape: %s\n",
9547 tape->solved ? "yes" :
9548 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9550 Print("Special tape properties: ");
9551 if (tape->property_bits == TAPE_PROPERTY_NONE)
9553 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9554 Print("[em_random_bug]");
9555 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9556 Print("[game_speed]");
9557 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9559 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9560 Print("[single_step]");
9561 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9562 Print("[snapshot]");
9563 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9564 Print("[replayed]");
9565 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9566 Print("[tas_keys]");
9567 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9568 Print("[small_graphics]");
9571 int year2 = tape->date / 10000;
9572 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9573 int month_index_raw = (tape->date / 100) % 100;
9574 int month_index = month_index_raw % 12; // prevent invalid index
9575 int month = month_index + 1;
9576 int day = tape->date % 100;
9578 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9582 tape_frame_counter = 0;
9584 for (i = 0; i < tape->length; i++)
9586 if (i >= MAX_TAPE_LEN)
9591 for (j = 0; j < MAX_PLAYERS; j++)
9593 if (tape->player_participates[j])
9595 int action = tape->pos[i].action[j];
9597 Print("%d:%02x ", j, action);
9598 Print("[%c%c%c%c|%c%c] - ",
9599 (action & JOY_LEFT ? '<' : ' '),
9600 (action & JOY_RIGHT ? '>' : ' '),
9601 (action & JOY_UP ? '^' : ' '),
9602 (action & JOY_DOWN ? 'v' : ' '),
9603 (action & JOY_BUTTON_1 ? '1' : ' '),
9604 (action & JOY_BUTTON_2 ? '2' : ' '));
9608 Print("(%03d) ", tape->pos[i].delay);
9609 Print("[%05d]\n", tape_frame_counter);
9611 tape_frame_counter += tape->pos[i].delay;
9617 void DumpTapes(void)
9619 static LevelDirTree *dumptape_leveldir = NULL;
9621 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9622 global.dumptape_leveldir);
9624 if (dumptape_leveldir == NULL)
9625 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9627 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9628 global.dumptape_level_nr > dumptape_leveldir->last_level)
9629 Fail("no such level number: %d", global.dumptape_level_nr);
9631 leveldir_current = dumptape_leveldir;
9633 if (options.mytapes)
9634 LoadTape(global.dumptape_level_nr);
9636 LoadSolutionTape(global.dumptape_level_nr);
9644 // ============================================================================
9645 // score file functions
9646 // ============================================================================
9648 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9652 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9654 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9655 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9656 scores->entry[i].score = 0;
9657 scores->entry[i].time = 0;
9659 scores->entry[i].id = -1;
9660 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9661 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9662 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9663 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9664 strcpy(scores->entry[i].country_code, "??");
9667 scores->num_entries = 0;
9668 scores->last_added = -1;
9669 scores->last_added_local = -1;
9671 scores->updated = FALSE;
9672 scores->uploaded = FALSE;
9673 scores->tape_downloaded = FALSE;
9674 scores->force_last_added = FALSE;
9676 // The following values are intentionally not reset here:
9680 // - continue_playing
9681 // - continue_on_return
9684 static void setScoreInfoToDefaults(void)
9686 setScoreInfoToDefaultsExt(&scores);
9689 static void setServerScoreInfoToDefaults(void)
9691 setScoreInfoToDefaultsExt(&server_scores);
9694 static void LoadScore_OLD(int nr)
9697 char *filename = getScoreFilename(nr);
9698 char cookie[MAX_LINE_LEN];
9699 char line[MAX_LINE_LEN];
9703 if (!(file = fopen(filename, MODE_READ)))
9706 // check file identifier
9707 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9709 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9710 cookie[strlen(cookie) - 1] = '\0';
9712 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9714 Warn("unknown format of score file '%s'", filename);
9721 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9723 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9724 Warn("fscanf() failed; %s", strerror(errno));
9726 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9729 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9730 line[strlen(line) - 1] = '\0';
9732 for (line_ptr = line; *line_ptr; line_ptr++)
9734 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9736 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9737 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9746 static void ConvertScore_OLD(void)
9748 // only convert score to time for levels that rate playing time over score
9749 if (!level.rate_time_over_score)
9752 // convert old score to playing time for score-less levels (like Supaplex)
9753 int time_final_max = 999;
9756 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9758 int score = scores.entry[i].score;
9760 if (score > 0 && score < time_final_max)
9761 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9765 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9767 scores->file_version = getFileVersion(file);
9768 scores->game_version = getFileVersion(file);
9773 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9775 char *level_identifier = NULL;
9776 int level_identifier_size;
9779 level_identifier_size = getFile16BitBE(file);
9781 level_identifier = checked_malloc(level_identifier_size);
9783 for (i = 0; i < level_identifier_size; i++)
9784 level_identifier[i] = getFile8Bit(file);
9786 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9787 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9789 checked_free(level_identifier);
9791 scores->level_nr = getFile16BitBE(file);
9792 scores->num_entries = getFile16BitBE(file);
9794 chunk_size = 2 + level_identifier_size + 2 + 2;
9799 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9803 for (i = 0; i < scores->num_entries; i++)
9805 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9806 scores->entry[i].name[j] = getFile8Bit(file);
9808 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9811 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9816 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9820 for (i = 0; i < scores->num_entries; i++)
9821 scores->entry[i].score = getFile16BitBE(file);
9823 chunk_size = scores->num_entries * 2;
9828 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9832 for (i = 0; i < scores->num_entries; i++)
9833 scores->entry[i].score = getFile32BitBE(file);
9835 chunk_size = scores->num_entries * 4;
9840 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9844 for (i = 0; i < scores->num_entries; i++)
9845 scores->entry[i].time = getFile32BitBE(file);
9847 chunk_size = scores->num_entries * 4;
9852 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9856 for (i = 0; i < scores->num_entries; i++)
9858 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9859 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9861 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9864 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9869 void LoadScore(int nr)
9871 char *filename = getScoreFilename(nr);
9872 char cookie[MAX_LINE_LEN];
9873 char chunk_name[CHUNK_ID_LEN + 1];
9875 boolean old_score_file_format = FALSE;
9878 // always start with reliable default values
9879 setScoreInfoToDefaults();
9881 if (!(file = openFile(filename, MODE_READ)))
9884 getFileChunkBE(file, chunk_name, NULL);
9885 if (strEqual(chunk_name, "RND1"))
9887 getFile32BitBE(file); // not used
9889 getFileChunkBE(file, chunk_name, NULL);
9890 if (!strEqual(chunk_name, "SCOR"))
9892 Warn("unknown format of score file '%s'", filename);
9899 else // check for old file format with cookie string
9901 strcpy(cookie, chunk_name);
9902 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9904 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9905 cookie[strlen(cookie) - 1] = '\0';
9907 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9909 Warn("unknown format of score file '%s'", filename);
9916 old_score_file_format = TRUE;
9919 if (old_score_file_format)
9921 // score files from versions before 4.2.4.0 without chunk structure
9924 // convert score to time, if possible (mainly for Supaplex levels)
9933 int (*loader)(File *, int, struct ScoreInfo *);
9937 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9938 { "INFO", -1, LoadScore_INFO },
9939 { "NAME", -1, LoadScore_NAME },
9940 { "SCOR", -1, LoadScore_SCOR },
9941 { "SC4R", -1, LoadScore_SC4R },
9942 { "TIME", -1, LoadScore_TIME },
9943 { "TAPE", -1, LoadScore_TAPE },
9948 while (getFileChunkBE(file, chunk_name, &chunk_size))
9952 while (chunk_info[i].name != NULL &&
9953 !strEqual(chunk_name, chunk_info[i].name))
9956 if (chunk_info[i].name == NULL)
9958 Warn("unknown chunk '%s' in score file '%s'",
9959 chunk_name, filename);
9961 ReadUnusedBytesFromFile(file, chunk_size);
9963 else if (chunk_info[i].size != -1 &&
9964 chunk_info[i].size != chunk_size)
9966 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9967 chunk_size, chunk_name, filename);
9969 ReadUnusedBytesFromFile(file, chunk_size);
9973 // call function to load this score chunk
9974 int chunk_size_expected =
9975 (chunk_info[i].loader)(file, chunk_size, &scores);
9977 // the size of some chunks cannot be checked before reading other
9978 // chunks first (like "HEAD" and "BODY") that contain some header
9979 // information, so check them here
9980 if (chunk_size_expected != chunk_size)
9982 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9983 chunk_size, chunk_name, filename);
9992 #if ENABLE_HISTORIC_CHUNKS
9993 void SaveScore_OLD(int nr)
9996 char *filename = getScoreFilename(nr);
9999 // used instead of "leveldir_current->subdir" (for network games)
10000 InitScoreDirectory(levelset.identifier);
10002 if (!(file = fopen(filename, MODE_WRITE)))
10004 Warn("cannot save score for level %d", nr);
10009 fprintf(file, "%s\n\n", SCORE_COOKIE);
10011 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10012 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10016 SetFilePermissions(filename, PERMS_PRIVATE);
10020 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10022 putFileVersion(file, scores->file_version);
10023 putFileVersion(file, scores->game_version);
10026 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10028 int level_identifier_size = strlen(scores->level_identifier) + 1;
10031 putFile16BitBE(file, level_identifier_size);
10033 for (i = 0; i < level_identifier_size; i++)
10034 putFile8Bit(file, scores->level_identifier[i]);
10036 putFile16BitBE(file, scores->level_nr);
10037 putFile16BitBE(file, scores->num_entries);
10040 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10044 for (i = 0; i < scores->num_entries; i++)
10046 int name_size = strlen(scores->entry[i].name);
10048 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10049 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10053 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10057 for (i = 0; i < scores->num_entries; i++)
10058 putFile16BitBE(file, scores->entry[i].score);
10061 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10065 for (i = 0; i < scores->num_entries; i++)
10066 putFile32BitBE(file, scores->entry[i].score);
10069 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10073 for (i = 0; i < scores->num_entries; i++)
10074 putFile32BitBE(file, scores->entry[i].time);
10077 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10081 for (i = 0; i < scores->num_entries; i++)
10083 int size = strlen(scores->entry[i].tape_basename);
10085 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10086 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10090 static void SaveScoreToFilename(char *filename)
10093 int info_chunk_size;
10094 int name_chunk_size;
10095 int scor_chunk_size;
10096 int sc4r_chunk_size;
10097 int time_chunk_size;
10098 int tape_chunk_size;
10099 boolean has_large_score_values;
10102 if (!(file = fopen(filename, MODE_WRITE)))
10104 Warn("cannot save score file '%s'", filename);
10109 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10110 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10111 scor_chunk_size = scores.num_entries * 2;
10112 sc4r_chunk_size = scores.num_entries * 4;
10113 time_chunk_size = scores.num_entries * 4;
10114 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10116 has_large_score_values = FALSE;
10117 for (i = 0; i < scores.num_entries; i++)
10118 if (scores.entry[i].score > 0xffff)
10119 has_large_score_values = TRUE;
10121 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10122 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10124 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10125 SaveScore_VERS(file, &scores);
10127 putFileChunkBE(file, "INFO", info_chunk_size);
10128 SaveScore_INFO(file, &scores);
10130 putFileChunkBE(file, "NAME", name_chunk_size);
10131 SaveScore_NAME(file, &scores);
10133 if (has_large_score_values)
10135 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10136 SaveScore_SC4R(file, &scores);
10140 putFileChunkBE(file, "SCOR", scor_chunk_size);
10141 SaveScore_SCOR(file, &scores);
10144 putFileChunkBE(file, "TIME", time_chunk_size);
10145 SaveScore_TIME(file, &scores);
10147 putFileChunkBE(file, "TAPE", tape_chunk_size);
10148 SaveScore_TAPE(file, &scores);
10152 SetFilePermissions(filename, PERMS_PRIVATE);
10155 void SaveScore(int nr)
10157 char *filename = getScoreFilename(nr);
10160 // used instead of "leveldir_current->subdir" (for network games)
10161 InitScoreDirectory(levelset.identifier);
10163 scores.file_version = FILE_VERSION_ACTUAL;
10164 scores.game_version = GAME_VERSION_ACTUAL;
10166 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10167 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10168 scores.level_nr = level_nr;
10170 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10171 if (scores.entry[i].score == 0 &&
10172 scores.entry[i].time == 0 &&
10173 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10176 scores.num_entries = i;
10178 if (scores.num_entries == 0)
10181 SaveScoreToFilename(filename);
10184 static void LoadServerScoreFromCache(int nr)
10186 struct ScoreEntry score_entry;
10195 { &score_entry.score, FALSE, 0 },
10196 { &score_entry.time, FALSE, 0 },
10197 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10198 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10199 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10200 { &score_entry.id, FALSE, 0 },
10201 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10202 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10203 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10204 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10208 char *filename = getScoreCacheFilename(nr);
10209 SetupFileHash *score_hash = loadSetupFileHash(filename);
10212 server_scores.num_entries = 0;
10214 if (score_hash == NULL)
10217 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10219 score_entry = server_scores.entry[i];
10221 for (j = 0; score_mapping[j].value != NULL; j++)
10225 sprintf(token, "%02d.%d", i, j);
10227 char *value = getHashEntry(score_hash, token);
10232 if (score_mapping[j].is_string)
10234 char *score_value = (char *)score_mapping[j].value;
10235 int value_size = score_mapping[j].string_size;
10237 strncpy(score_value, value, value_size);
10238 score_value[value_size] = '\0';
10242 int *score_value = (int *)score_mapping[j].value;
10244 *score_value = atoi(value);
10247 server_scores.num_entries = i + 1;
10250 server_scores.entry[i] = score_entry;
10253 freeSetupFileHash(score_hash);
10256 void LoadServerScore(int nr, boolean download_score)
10258 if (!setup.use_api_server)
10261 // always start with reliable default values
10262 setServerScoreInfoToDefaults();
10264 // 1st step: load server scores from cache file (which may not exist)
10265 // (this should prevent reading it while the thread is writing to it)
10266 LoadServerScoreFromCache(nr);
10268 if (download_score && runtime.use_api_server)
10270 // 2nd step: download server scores from score server to cache file
10271 // (as thread, as it might time out if the server is not reachable)
10272 ApiGetScoreAsThread(nr);
10276 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10278 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10280 // if score tape not uploaded, ask for uploading missing tapes later
10281 if (!setup.has_remaining_tapes)
10282 setup.ask_for_remaining_tapes = TRUE;
10284 setup.provide_uploading_tapes = TRUE;
10285 setup.has_remaining_tapes = TRUE;
10287 SaveSetup_ServerSetup();
10290 void SaveServerScore(int nr, boolean tape_saved)
10292 if (!runtime.use_api_server)
10294 PrepareScoreTapesForUpload(leveldir_current->subdir);
10299 ApiAddScoreAsThread(nr, tape_saved, NULL);
10302 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10303 char *score_tape_filename)
10305 if (!runtime.use_api_server)
10308 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10311 void LoadLocalAndServerScore(int nr, boolean download_score)
10313 int last_added_local = scores.last_added_local;
10314 boolean force_last_added = scores.force_last_added;
10316 // needed if only showing server scores
10317 setScoreInfoToDefaults();
10319 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10322 // restore last added local score entry (before merging server scores)
10323 scores.last_added = scores.last_added_local = last_added_local;
10325 if (setup.use_api_server &&
10326 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10328 // load server scores from cache file and trigger update from server
10329 LoadServerScore(nr, download_score);
10331 // merge local scores with scores from server
10332 MergeServerScore();
10335 if (force_last_added)
10336 scores.force_last_added = force_last_added;
10340 // ============================================================================
10341 // setup file functions
10342 // ============================================================================
10344 #define TOKEN_STR_PLAYER_PREFIX "player_"
10347 static struct TokenInfo global_setup_tokens[] =
10351 &setup.player_name, "player_name"
10355 &setup.multiple_users, "multiple_users"
10359 &setup.sound, "sound"
10363 &setup.sound_loops, "repeating_sound_loops"
10367 &setup.sound_music, "background_music"
10371 &setup.sound_simple, "simple_sound_effects"
10375 &setup.toons, "toons"
10379 &setup.global_animations, "global_animations"
10383 &setup.scroll_delay, "scroll_delay"
10387 &setup.forced_scroll_delay, "forced_scroll_delay"
10391 &setup.scroll_delay_value, "scroll_delay_value"
10395 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10399 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10403 &setup.fade_screens, "fade_screens"
10407 &setup.autorecord, "automatic_tape_recording"
10411 &setup.autorecord_after_replay, "autorecord_after_replay"
10415 &setup.auto_pause_on_start, "auto_pause_on_start"
10419 &setup.show_titlescreen, "show_titlescreen"
10423 &setup.quick_doors, "quick_doors"
10427 &setup.team_mode, "team_mode"
10431 &setup.handicap, "handicap"
10435 &setup.skip_levels, "skip_levels"
10439 &setup.increment_levels, "increment_levels"
10443 &setup.auto_play_next_level, "auto_play_next_level"
10447 &setup.count_score_after_game, "count_score_after_game"
10451 &setup.show_scores_after_game, "show_scores_after_game"
10455 &setup.time_limit, "time_limit"
10459 &setup.fullscreen, "fullscreen"
10463 &setup.window_scaling_percent, "window_scaling_percent"
10467 &setup.window_scaling_quality, "window_scaling_quality"
10471 &setup.screen_rendering_mode, "screen_rendering_mode"
10475 &setup.vsync_mode, "vsync_mode"
10479 &setup.ask_on_escape, "ask_on_escape"
10483 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10487 &setup.ask_on_game_over, "ask_on_game_over"
10491 &setup.ask_on_quit_game, "ask_on_quit_game"
10495 &setup.ask_on_quit_program, "ask_on_quit_program"
10499 &setup.quick_switch, "quick_player_switch"
10503 &setup.input_on_focus, "input_on_focus"
10507 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10511 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10515 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10519 &setup.game_speed_extended, "game_speed_extended"
10523 &setup.game_frame_delay, "game_frame_delay"
10527 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10531 &setup.bd_skip_hatching, "bd_skip_hatching"
10535 &setup.bd_scroll_delay, "bd_scroll_delay"
10539 &setup.bd_smooth_movements, "bd_smooth_movements"
10543 &setup.sp_show_border_elements, "sp_show_border_elements"
10547 &setup.small_game_graphics, "small_game_graphics"
10551 &setup.show_load_save_buttons, "show_load_save_buttons"
10555 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10559 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10563 &setup.graphics_set, "graphics_set"
10567 &setup.sounds_set, "sounds_set"
10571 &setup.music_set, "music_set"
10575 &setup.override_level_graphics, "override_level_graphics"
10579 &setup.override_level_sounds, "override_level_sounds"
10583 &setup.override_level_music, "override_level_music"
10587 &setup.volume_simple, "volume_simple"
10591 &setup.volume_loops, "volume_loops"
10595 &setup.volume_music, "volume_music"
10599 &setup.network_mode, "network_mode"
10603 &setup.network_player_nr, "network_player"
10607 &setup.network_server_hostname, "network_server_hostname"
10611 &setup.touch.control_type, "touch.control_type"
10615 &setup.touch.move_distance, "touch.move_distance"
10619 &setup.touch.drop_distance, "touch.drop_distance"
10623 &setup.touch.transparency, "touch.transparency"
10627 &setup.touch.draw_outlined, "touch.draw_outlined"
10631 &setup.touch.draw_pressed, "touch.draw_pressed"
10635 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10639 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10643 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10647 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10651 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10655 static struct TokenInfo auto_setup_tokens[] =
10659 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10663 static struct TokenInfo server_setup_tokens[] =
10667 &setup.player_uuid, "player_uuid"
10671 &setup.player_version, "player_version"
10675 &setup.use_api_server, TEST_PREFIX "use_api_server"
10679 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10683 &setup.api_server_password, TEST_PREFIX "api_server_password"
10687 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10691 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10695 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10699 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10703 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10707 static struct TokenInfo editor_setup_tokens[] =
10711 &setup.editor.el_classic, "editor.el_classic"
10715 &setup.editor.el_custom, "editor.el_custom"
10719 &setup.editor.el_user_defined, "editor.el_user_defined"
10723 &setup.editor.el_dynamic, "editor.el_dynamic"
10727 &setup.editor.el_headlines, "editor.el_headlines"
10731 &setup.editor.show_element_token, "editor.show_element_token"
10735 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10739 static struct TokenInfo editor_cascade_setup_tokens[] =
10743 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10747 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10751 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10755 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10759 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10763 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10767 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10771 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10775 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10779 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10783 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10787 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10791 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10795 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10799 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10803 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10807 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10811 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10815 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10819 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10823 static struct TokenInfo shortcut_setup_tokens[] =
10827 &setup.shortcut.save_game, "shortcut.save_game"
10831 &setup.shortcut.load_game, "shortcut.load_game"
10835 &setup.shortcut.restart_game, "shortcut.restart_game"
10839 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10843 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10847 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10851 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10855 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10859 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10863 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10867 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10871 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10875 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10879 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10883 &setup.shortcut.tape_record, "shortcut.tape_record"
10887 &setup.shortcut.tape_play, "shortcut.tape_play"
10891 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10895 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10899 &setup.shortcut.sound_music, "shortcut.sound_music"
10903 &setup.shortcut.snap_left, "shortcut.snap_left"
10907 &setup.shortcut.snap_right, "shortcut.snap_right"
10911 &setup.shortcut.snap_up, "shortcut.snap_up"
10915 &setup.shortcut.snap_down, "shortcut.snap_down"
10919 static struct SetupInputInfo setup_input;
10920 static struct TokenInfo player_setup_tokens[] =
10924 &setup_input.use_joystick, ".use_joystick"
10928 &setup_input.joy.device_name, ".joy.device_name"
10932 &setup_input.joy.xleft, ".joy.xleft"
10936 &setup_input.joy.xmiddle, ".joy.xmiddle"
10940 &setup_input.joy.xright, ".joy.xright"
10944 &setup_input.joy.yupper, ".joy.yupper"
10948 &setup_input.joy.ymiddle, ".joy.ymiddle"
10952 &setup_input.joy.ylower, ".joy.ylower"
10956 &setup_input.joy.snap, ".joy.snap_field"
10960 &setup_input.joy.drop, ".joy.place_bomb"
10964 &setup_input.key.left, ".key.move_left"
10968 &setup_input.key.right, ".key.move_right"
10972 &setup_input.key.up, ".key.move_up"
10976 &setup_input.key.down, ".key.move_down"
10980 &setup_input.key.snap, ".key.snap_field"
10984 &setup_input.key.drop, ".key.place_bomb"
10988 static struct TokenInfo system_setup_tokens[] =
10992 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10996 &setup.system.sdl_videodriver, "system.sdl_videodriver"
11000 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
11004 &setup.system.audio_fragment_size, "system.audio_fragment_size"
11008 static struct TokenInfo internal_setup_tokens[] =
11012 &setup.internal.program_title, "program_title"
11016 &setup.internal.program_version, "program_version"
11020 &setup.internal.program_author, "program_author"
11024 &setup.internal.program_email, "program_email"
11028 &setup.internal.program_website, "program_website"
11032 &setup.internal.program_copyright, "program_copyright"
11036 &setup.internal.program_company, "program_company"
11040 &setup.internal.program_icon_file, "program_icon_file"
11044 &setup.internal.default_graphics_set, "default_graphics_set"
11048 &setup.internal.default_sounds_set, "default_sounds_set"
11052 &setup.internal.default_music_set, "default_music_set"
11056 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11060 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11064 &setup.internal.fallback_music_file, "fallback_music_file"
11068 &setup.internal.default_level_series, "default_level_series"
11072 &setup.internal.default_window_width, "default_window_width"
11076 &setup.internal.default_window_height, "default_window_height"
11080 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11084 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11088 &setup.internal.create_user_levelset, "create_user_levelset"
11092 &setup.internal.info_screens_from_main, "info_screens_from_main"
11096 &setup.internal.menu_game, "menu_game"
11100 &setup.internal.menu_engines, "menu_engines"
11104 &setup.internal.menu_editor, "menu_editor"
11108 &setup.internal.menu_graphics, "menu_graphics"
11112 &setup.internal.menu_sound, "menu_sound"
11116 &setup.internal.menu_artwork, "menu_artwork"
11120 &setup.internal.menu_input, "menu_input"
11124 &setup.internal.menu_touch, "menu_touch"
11128 &setup.internal.menu_shortcuts, "menu_shortcuts"
11132 &setup.internal.menu_exit, "menu_exit"
11136 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11140 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11144 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11148 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11152 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11156 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11160 &setup.internal.info_title, "info_title"
11164 &setup.internal.info_elements, "info_elements"
11168 &setup.internal.info_music, "info_music"
11172 &setup.internal.info_credits, "info_credits"
11176 &setup.internal.info_program, "info_program"
11180 &setup.internal.info_version, "info_version"
11184 &setup.internal.info_levelset, "info_levelset"
11188 &setup.internal.info_exit, "info_exit"
11192 static struct TokenInfo debug_setup_tokens[] =
11196 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11200 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11204 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11208 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11212 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11216 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11220 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11224 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11228 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11232 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11236 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11240 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11244 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11248 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11252 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11256 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11260 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11264 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11268 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11272 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11276 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11279 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11283 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11287 &setup.debug.xsn_mode, "debug.xsn_mode"
11291 &setup.debug.xsn_percent, "debug.xsn_percent"
11295 static struct TokenInfo options_setup_tokens[] =
11299 &setup.options.verbose, "options.verbose"
11303 &setup.options.debug, "options.debug"
11307 &setup.options.debug_mode, "options.debug_mode"
11311 static void setSetupInfoToDefaults(struct SetupInfo *si)
11315 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11317 si->multiple_users = TRUE;
11320 si->sound_loops = TRUE;
11321 si->sound_music = TRUE;
11322 si->sound_simple = TRUE;
11324 si->global_animations = TRUE;
11325 si->scroll_delay = TRUE;
11326 si->forced_scroll_delay = FALSE;
11327 si->scroll_delay_value = STD_SCROLL_DELAY;
11328 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11329 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11330 si->fade_screens = TRUE;
11331 si->autorecord = TRUE;
11332 si->autorecord_after_replay = TRUE;
11333 si->auto_pause_on_start = FALSE;
11334 si->show_titlescreen = TRUE;
11335 si->quick_doors = FALSE;
11336 si->team_mode = FALSE;
11337 si->handicap = TRUE;
11338 si->skip_levels = TRUE;
11339 si->increment_levels = TRUE;
11340 si->auto_play_next_level = TRUE;
11341 si->count_score_after_game = TRUE;
11342 si->show_scores_after_game = TRUE;
11343 si->time_limit = TRUE;
11344 si->fullscreen = FALSE;
11345 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11346 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11347 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11348 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11349 si->ask_on_escape = TRUE;
11350 si->ask_on_escape_editor = TRUE;
11351 si->ask_on_game_over = TRUE;
11352 si->ask_on_quit_game = TRUE;
11353 si->ask_on_quit_program = TRUE;
11354 si->quick_switch = FALSE;
11355 si->input_on_focus = FALSE;
11356 si->prefer_aga_graphics = TRUE;
11357 si->prefer_lowpass_sounds = FALSE;
11358 si->prefer_extra_panel_items = TRUE;
11359 si->game_speed_extended = FALSE;
11360 si->game_frame_delay = GAME_FRAME_DELAY;
11361 si->bd_skip_uncovering = FALSE;
11362 si->bd_skip_hatching = FALSE;
11363 si->bd_scroll_delay = TRUE;
11364 si->bd_smooth_movements = AUTO;
11365 si->sp_show_border_elements = FALSE;
11366 si->small_game_graphics = FALSE;
11367 si->show_load_save_buttons = FALSE;
11368 si->show_undo_redo_buttons = FALSE;
11369 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11371 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11372 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11373 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11375 si->override_level_graphics = FALSE;
11376 si->override_level_sounds = FALSE;
11377 si->override_level_music = FALSE;
11379 si->volume_simple = 100; // percent
11380 si->volume_loops = 100; // percent
11381 si->volume_music = 100; // percent
11383 si->network_mode = FALSE;
11384 si->network_player_nr = 0; // first player
11385 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11387 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11388 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11389 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11390 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11391 si->touch.draw_outlined = TRUE;
11392 si->touch.draw_pressed = TRUE;
11394 for (i = 0; i < 2; i++)
11396 char *default_grid_button[6][2] =
11402 { "111222", " vv " },
11403 { "111222", " vv " }
11405 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11406 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11407 int min_xsize = MIN(6, grid_xsize);
11408 int min_ysize = MIN(6, grid_ysize);
11409 int startx = grid_xsize - min_xsize;
11410 int starty = grid_ysize - min_ysize;
11413 // virtual buttons grid can only be set to defaults if video is initialized
11414 // (this will be repeated if virtual buttons are not loaded from setup file)
11415 if (video.initialized)
11417 si->touch.grid_xsize[i] = grid_xsize;
11418 si->touch.grid_ysize[i] = grid_ysize;
11422 si->touch.grid_xsize[i] = -1;
11423 si->touch.grid_ysize[i] = -1;
11426 for (x = 0; x < MAX_GRID_XSIZE; x++)
11427 for (y = 0; y < MAX_GRID_YSIZE; y++)
11428 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11430 for (x = 0; x < min_xsize; x++)
11431 for (y = 0; y < min_ysize; y++)
11432 si->touch.grid_button[i][x][starty + y] =
11433 default_grid_button[y][0][x];
11435 for (x = 0; x < min_xsize; x++)
11436 for (y = 0; y < min_ysize; y++)
11437 si->touch.grid_button[i][startx + x][starty + y] =
11438 default_grid_button[y][1][x];
11441 si->touch.grid_initialized = video.initialized;
11443 si->touch.overlay_buttons = FALSE;
11445 si->editor.el_boulderdash = TRUE;
11446 si->editor.el_boulderdash_native = TRUE;
11447 si->editor.el_boulderdash_effects = TRUE;
11448 si->editor.el_emerald_mine = TRUE;
11449 si->editor.el_emerald_mine_club = TRUE;
11450 si->editor.el_more = TRUE;
11451 si->editor.el_sokoban = TRUE;
11452 si->editor.el_supaplex = TRUE;
11453 si->editor.el_diamond_caves = TRUE;
11454 si->editor.el_dx_boulderdash = TRUE;
11456 si->editor.el_mirror_magic = TRUE;
11457 si->editor.el_deflektor = TRUE;
11459 si->editor.el_chars = TRUE;
11460 si->editor.el_steel_chars = TRUE;
11462 si->editor.el_classic = TRUE;
11463 si->editor.el_custom = TRUE;
11465 si->editor.el_user_defined = FALSE;
11466 si->editor.el_dynamic = TRUE;
11468 si->editor.el_headlines = TRUE;
11470 si->editor.show_element_token = FALSE;
11472 si->editor.show_read_only_warning = TRUE;
11474 si->editor.use_template_for_new_levels = TRUE;
11476 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11477 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11478 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11479 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11480 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11482 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11483 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11484 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11485 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11486 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11488 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11489 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11490 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11491 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11492 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11493 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11495 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11496 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11497 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11499 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11500 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11501 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11502 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11504 for (i = 0; i < MAX_PLAYERS; i++)
11506 si->input[i].use_joystick = FALSE;
11507 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11508 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11509 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11510 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11511 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11512 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11513 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11514 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11515 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11516 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11517 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11518 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11519 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11520 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11521 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11524 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11525 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11526 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11527 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11529 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11530 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11531 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11532 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11533 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11534 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11535 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11537 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11539 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11540 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11541 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11543 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11544 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11545 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11547 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11548 si->internal.choose_from_top_leveldir = FALSE;
11549 si->internal.show_scaling_in_title = TRUE;
11550 si->internal.create_user_levelset = TRUE;
11551 si->internal.info_screens_from_main = FALSE;
11553 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11554 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11556 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11557 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11558 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11559 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11560 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11561 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11562 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11563 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11564 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11565 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11567 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11568 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11569 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11570 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11571 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11572 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11573 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11574 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11575 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11576 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11578 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11579 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11581 si->debug.show_frames_per_second = FALSE;
11583 si->debug.xsn_mode = AUTO;
11584 si->debug.xsn_percent = 0;
11586 si->options.verbose = FALSE;
11587 si->options.debug = FALSE;
11588 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11590 #if defined(PLATFORM_ANDROID)
11591 si->fullscreen = TRUE;
11592 si->touch.overlay_buttons = TRUE;
11595 setHideSetupEntry(&setup.debug.xsn_mode);
11598 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11600 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11603 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11605 si->player_uuid = NULL; // (will be set later)
11606 si->player_version = 1; // (will be set later)
11608 si->use_api_server = TRUE;
11609 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11610 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11611 si->ask_for_uploading_tapes = TRUE;
11612 si->ask_for_remaining_tapes = FALSE;
11613 si->provide_uploading_tapes = TRUE;
11614 si->ask_for_using_api_server = TRUE;
11615 si->has_remaining_tapes = FALSE;
11618 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11620 si->editor_cascade.el_bd = TRUE;
11621 si->editor_cascade.el_bd_native = TRUE;
11622 si->editor_cascade.el_bd_effects = FALSE;
11623 si->editor_cascade.el_em = TRUE;
11624 si->editor_cascade.el_emc = TRUE;
11625 si->editor_cascade.el_rnd = TRUE;
11626 si->editor_cascade.el_sb = TRUE;
11627 si->editor_cascade.el_sp = TRUE;
11628 si->editor_cascade.el_dc = TRUE;
11629 si->editor_cascade.el_dx = TRUE;
11631 si->editor_cascade.el_mm = TRUE;
11632 si->editor_cascade.el_df = TRUE;
11634 si->editor_cascade.el_chars = FALSE;
11635 si->editor_cascade.el_steel_chars = FALSE;
11636 si->editor_cascade.el_ce = FALSE;
11637 si->editor_cascade.el_ge = FALSE;
11638 si->editor_cascade.el_es = FALSE;
11639 si->editor_cascade.el_ref = FALSE;
11640 si->editor_cascade.el_user = FALSE;
11641 si->editor_cascade.el_dynamic = FALSE;
11644 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11646 static char *getHideSetupToken(void *setup_value)
11648 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11650 if (setup_value != NULL)
11651 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11653 return hide_setup_token;
11656 void setHideSetupEntry(void *setup_value)
11658 char *hide_setup_token = getHideSetupToken(setup_value);
11660 if (hide_setup_hash == NULL)
11661 hide_setup_hash = newSetupFileHash();
11663 if (setup_value != NULL)
11664 setHashEntry(hide_setup_hash, hide_setup_token, "");
11667 void removeHideSetupEntry(void *setup_value)
11669 char *hide_setup_token = getHideSetupToken(setup_value);
11671 if (setup_value != NULL)
11672 removeHashEntry(hide_setup_hash, hide_setup_token);
11675 boolean hideSetupEntry(void *setup_value)
11677 char *hide_setup_token = getHideSetupToken(setup_value);
11679 return (setup_value != NULL &&
11680 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11683 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11684 struct TokenInfo *token_info,
11685 int token_nr, char *token_text)
11687 char *token_hide_text = getStringCat2(token_text, ".hide");
11688 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11690 // set the value of this setup option in the setup option structure
11691 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11693 // check if this setup option should be hidden in the setup menu
11694 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11695 setHideSetupEntry(token_info[token_nr].value);
11697 free(token_hide_text);
11700 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11701 struct TokenInfo *token_info,
11704 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11705 token_info[token_nr].text);
11708 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11712 if (!setup_file_hash)
11715 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11716 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11718 setup.touch.grid_initialized = TRUE;
11719 for (i = 0; i < 2; i++)
11721 int grid_xsize = setup.touch.grid_xsize[i];
11722 int grid_ysize = setup.touch.grid_ysize[i];
11725 // if virtual buttons are not loaded from setup file, repeat initializing
11726 // virtual buttons grid with default values later when video is initialized
11727 if (grid_xsize == -1 ||
11730 setup.touch.grid_initialized = FALSE;
11735 for (y = 0; y < grid_ysize; y++)
11737 char token_string[MAX_LINE_LEN];
11739 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11741 char *value_string = getHashEntry(setup_file_hash, token_string);
11743 if (value_string == NULL)
11746 for (x = 0; x < grid_xsize; x++)
11748 char c = value_string[x];
11750 setup.touch.grid_button[i][x][y] =
11751 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11756 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11757 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11759 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11760 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11762 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11766 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11768 setup_input = setup.input[pnr];
11769 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11771 char full_token[100];
11773 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11774 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11777 setup.input[pnr] = setup_input;
11780 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11781 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11783 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11784 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11786 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11787 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11789 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11790 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11792 setHideRelatedSetupEntries();
11795 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11799 if (!setup_file_hash)
11802 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11803 setSetupInfo(auto_setup_tokens, i,
11804 getHashEntry(setup_file_hash,
11805 auto_setup_tokens[i].text));
11808 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11812 if (!setup_file_hash)
11815 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11816 setSetupInfo(server_setup_tokens, i,
11817 getHashEntry(setup_file_hash,
11818 server_setup_tokens[i].text));
11821 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11825 if (!setup_file_hash)
11828 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11829 setSetupInfo(editor_cascade_setup_tokens, i,
11830 getHashEntry(setup_file_hash,
11831 editor_cascade_setup_tokens[i].text));
11834 void LoadUserNames(void)
11836 int last_user_nr = user.nr;
11839 if (global.user_names != NULL)
11841 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11842 checked_free(global.user_names[i]);
11844 checked_free(global.user_names);
11847 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11849 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11853 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11855 if (setup_file_hash)
11857 char *player_name = getHashEntry(setup_file_hash, "player_name");
11859 global.user_names[i] = getFixedUserName(player_name);
11861 freeSetupFileHash(setup_file_hash);
11864 if (global.user_names[i] == NULL)
11865 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11868 user.nr = last_user_nr;
11871 void LoadSetupFromFilename(char *filename)
11873 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11875 if (setup_file_hash)
11877 decodeSetupFileHash_Default(setup_file_hash);
11879 freeSetupFileHash(setup_file_hash);
11883 Debug("setup", "using default setup values");
11887 static void LoadSetup_SpecialPostProcessing(void)
11889 char *player_name_new;
11891 // needed to work around problems with fixed length strings
11892 player_name_new = getFixedUserName(setup.player_name);
11893 free(setup.player_name);
11894 setup.player_name = player_name_new;
11896 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11897 if (setup.scroll_delay == FALSE)
11899 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11900 setup.scroll_delay = TRUE; // now always "on"
11903 // make sure that scroll delay value stays inside valid range
11904 setup.scroll_delay_value =
11905 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11908 void LoadSetup_Default(void)
11912 // always start with reliable default values
11913 setSetupInfoToDefaults(&setup);
11915 // try to load setup values from default setup file
11916 filename = getDefaultSetupFilename();
11918 if (fileExists(filename))
11919 LoadSetupFromFilename(filename);
11921 // try to load setup values from platform setup file
11922 filename = getPlatformSetupFilename();
11924 if (fileExists(filename))
11925 LoadSetupFromFilename(filename);
11927 // try to load setup values from user setup file
11928 filename = getSetupFilename();
11930 LoadSetupFromFilename(filename);
11932 LoadSetup_SpecialPostProcessing();
11935 void LoadSetup_AutoSetup(void)
11937 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11938 SetupFileHash *setup_file_hash = NULL;
11940 // always start with reliable default values
11941 setSetupInfoToDefaults_AutoSetup(&setup);
11943 setup_file_hash = loadSetupFileHash(filename);
11945 if (setup_file_hash)
11947 decodeSetupFileHash_AutoSetup(setup_file_hash);
11949 freeSetupFileHash(setup_file_hash);
11955 void LoadSetup_ServerSetup(void)
11957 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11958 SetupFileHash *setup_file_hash = NULL;
11960 // always start with reliable default values
11961 setSetupInfoToDefaults_ServerSetup(&setup);
11963 setup_file_hash = loadSetupFileHash(filename);
11965 if (setup_file_hash)
11967 decodeSetupFileHash_ServerSetup(setup_file_hash);
11969 freeSetupFileHash(setup_file_hash);
11974 if (setup.player_uuid == NULL)
11976 // player UUID does not yet exist in setup file
11977 setup.player_uuid = getStringCopy(getUUID());
11978 setup.player_version = 2;
11980 SaveSetup_ServerSetup();
11984 void LoadSetup_EditorCascade(void)
11986 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11987 SetupFileHash *setup_file_hash = NULL;
11989 // always start with reliable default values
11990 setSetupInfoToDefaults_EditorCascade(&setup);
11992 setup_file_hash = loadSetupFileHash(filename);
11994 if (setup_file_hash)
11996 decodeSetupFileHash_EditorCascade(setup_file_hash);
11998 freeSetupFileHash(setup_file_hash);
12004 void LoadSetup(void)
12006 LoadSetup_Default();
12007 LoadSetup_AutoSetup();
12008 LoadSetup_ServerSetup();
12009 LoadSetup_EditorCascade();
12012 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12013 char *mapping_line)
12015 char mapping_guid[MAX_LINE_LEN];
12016 char *mapping_start, *mapping_end;
12018 // get GUID from game controller mapping line: copy complete line
12019 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12020 mapping_guid[MAX_LINE_LEN - 1] = '\0';
12022 // get GUID from game controller mapping line: cut after GUID part
12023 mapping_start = strchr(mapping_guid, ',');
12024 if (mapping_start != NULL)
12025 *mapping_start = '\0';
12027 // cut newline from game controller mapping line
12028 mapping_end = strchr(mapping_line, '\n');
12029 if (mapping_end != NULL)
12030 *mapping_end = '\0';
12032 // add mapping entry to game controller mappings hash
12033 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12036 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12041 if (!(file = fopen(filename, MODE_READ)))
12043 Warn("cannot read game controller mappings file '%s'", filename);
12048 while (!feof(file))
12050 char line[MAX_LINE_LEN];
12052 if (!fgets(line, MAX_LINE_LEN, file))
12055 addGameControllerMappingToHash(mappings_hash, line);
12061 void SaveSetup_Default(void)
12063 char *filename = getSetupFilename();
12067 InitUserDataDirectory();
12069 if (!(file = fopen(filename, MODE_WRITE)))
12071 Warn("cannot write setup file '%s'", filename);
12076 fprintFileHeader(file, SETUP_FILENAME);
12078 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12080 // just to make things nicer :)
12081 if (global_setup_tokens[i].value == &setup.multiple_users ||
12082 global_setup_tokens[i].value == &setup.sound ||
12083 global_setup_tokens[i].value == &setup.graphics_set ||
12084 global_setup_tokens[i].value == &setup.volume_simple ||
12085 global_setup_tokens[i].value == &setup.network_mode ||
12086 global_setup_tokens[i].value == &setup.touch.control_type ||
12087 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12088 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12089 fprintf(file, "\n");
12091 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12094 for (i = 0; i < 2; i++)
12096 int grid_xsize = setup.touch.grid_xsize[i];
12097 int grid_ysize = setup.touch.grid_ysize[i];
12100 fprintf(file, "\n");
12102 for (y = 0; y < grid_ysize; y++)
12104 char token_string[MAX_LINE_LEN];
12105 char value_string[MAX_LINE_LEN];
12107 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12109 for (x = 0; x < grid_xsize; x++)
12111 char c = setup.touch.grid_button[i][x][y];
12113 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12116 value_string[grid_xsize] = '\0';
12118 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12122 fprintf(file, "\n");
12123 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12124 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12126 fprintf(file, "\n");
12127 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12128 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12130 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12134 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12135 fprintf(file, "\n");
12137 setup_input = setup.input[pnr];
12138 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12139 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12142 fprintf(file, "\n");
12143 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12144 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12146 // (internal setup values not saved to user setup file)
12148 fprintf(file, "\n");
12149 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12150 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12151 setup.debug.xsn_mode != AUTO)
12152 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12154 fprintf(file, "\n");
12155 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12156 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12160 SetFilePermissions(filename, PERMS_PRIVATE);
12163 void SaveSetup_AutoSetup(void)
12165 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12169 InitUserDataDirectory();
12171 if (!(file = fopen(filename, MODE_WRITE)))
12173 Warn("cannot write auto setup file '%s'", filename);
12180 fprintFileHeader(file, AUTOSETUP_FILENAME);
12182 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12183 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12187 SetFilePermissions(filename, PERMS_PRIVATE);
12192 void SaveSetup_ServerSetup(void)
12194 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12198 InitUserDataDirectory();
12200 if (!(file = fopen(filename, MODE_WRITE)))
12202 Warn("cannot write server setup file '%s'", filename);
12209 fprintFileHeader(file, SERVERSETUP_FILENAME);
12211 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12213 // just to make things nicer :)
12214 if (server_setup_tokens[i].value == &setup.use_api_server)
12215 fprintf(file, "\n");
12217 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12222 SetFilePermissions(filename, PERMS_PRIVATE);
12227 void SaveSetup_EditorCascade(void)
12229 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12233 InitUserDataDirectory();
12235 if (!(file = fopen(filename, MODE_WRITE)))
12237 Warn("cannot write editor cascade state file '%s'", filename);
12244 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12246 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12247 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12251 SetFilePermissions(filename, PERMS_PRIVATE);
12256 void SaveSetup(void)
12258 SaveSetup_Default();
12259 SaveSetup_AutoSetup();
12260 SaveSetup_ServerSetup();
12261 SaveSetup_EditorCascade();
12264 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12269 if (!(file = fopen(filename, MODE_WRITE)))
12271 Warn("cannot write game controller mappings file '%s'", filename);
12276 BEGIN_HASH_ITERATION(mappings_hash, itr)
12278 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12280 END_HASH_ITERATION(mappings_hash, itr)
12285 void SaveSetup_AddGameControllerMapping(char *mapping)
12287 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12288 SetupFileHash *mappings_hash = newSetupFileHash();
12290 InitUserDataDirectory();
12292 // load existing personal game controller mappings
12293 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12295 // add new mapping to personal game controller mappings
12296 addGameControllerMappingToHash(mappings_hash, mapping);
12298 // save updated personal game controller mappings
12299 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12301 freeSetupFileHash(mappings_hash);
12305 void LoadCustomElementDescriptions(void)
12307 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12308 SetupFileHash *setup_file_hash;
12311 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12313 if (element_info[i].custom_description != NULL)
12315 free(element_info[i].custom_description);
12316 element_info[i].custom_description = NULL;
12320 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12323 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12325 char *token = getStringCat2(element_info[i].token_name, ".name");
12326 char *value = getHashEntry(setup_file_hash, token);
12329 element_info[i].custom_description = getStringCopy(value);
12334 freeSetupFileHash(setup_file_hash);
12337 static int getElementFromToken(char *token)
12339 char *value = getHashEntry(element_token_hash, token);
12342 return atoi(value);
12344 Warn("unknown element token '%s'", token);
12346 return EL_UNDEFINED;
12349 void FreeGlobalAnimEventInfo(void)
12351 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12353 if (gaei->event_list == NULL)
12358 for (i = 0; i < gaei->num_event_lists; i++)
12360 checked_free(gaei->event_list[i]->event_value);
12361 checked_free(gaei->event_list[i]);
12364 checked_free(gaei->event_list);
12366 gaei->event_list = NULL;
12367 gaei->num_event_lists = 0;
12370 static int AddGlobalAnimEventList(void)
12372 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12373 int list_pos = gaei->num_event_lists++;
12375 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12376 sizeof(struct GlobalAnimEventListInfo *));
12378 gaei->event_list[list_pos] =
12379 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12381 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12383 gaeli->event_value = NULL;
12384 gaeli->num_event_values = 0;
12389 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12391 // do not add empty global animation events
12392 if (event_value == ANIM_EVENT_NONE)
12395 // if list position is undefined, create new list
12396 if (list_pos == ANIM_EVENT_UNDEFINED)
12397 list_pos = AddGlobalAnimEventList();
12399 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12400 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12401 int value_pos = gaeli->num_event_values++;
12403 gaeli->event_value = checked_realloc(gaeli->event_value,
12404 gaeli->num_event_values * sizeof(int *));
12406 gaeli->event_value[value_pos] = event_value;
12411 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12413 if (list_pos == ANIM_EVENT_UNDEFINED)
12414 return ANIM_EVENT_NONE;
12416 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12417 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12419 return gaeli->event_value[value_pos];
12422 int GetGlobalAnimEventValueCount(int list_pos)
12424 if (list_pos == ANIM_EVENT_UNDEFINED)
12427 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12428 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12430 return gaeli->num_event_values;
12433 // This function checks if a string <s> of the format "string1, string2, ..."
12434 // exactly contains a string <s_contained>.
12436 static boolean string_has_parameter(char *s, char *s_contained)
12440 if (s == NULL || s_contained == NULL)
12443 if (strlen(s_contained) > strlen(s))
12446 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12448 char next_char = s[strlen(s_contained)];
12450 // check if next character is delimiter or whitespace
12451 if (next_char == ',' || next_char == '\0' ||
12452 next_char == ' ' || next_char == '\t')
12456 // check if string contains another parameter string after a comma
12457 substring = strchr(s, ',');
12458 if (substring == NULL) // string does not contain a comma
12461 // advance string pointer to next character after the comma
12464 // skip potential whitespaces after the comma
12465 while (*substring == ' ' || *substring == '\t')
12468 return string_has_parameter(substring, s_contained);
12471 static int get_anim_parameter_value_ce(char *s)
12474 char *pattern_1 = "ce_change:custom_";
12475 char *pattern_2 = ".page_";
12476 int pattern_1_len = strlen(pattern_1);
12477 char *matching_char = strstr(s_ptr, pattern_1);
12478 int result = ANIM_EVENT_NONE;
12480 if (matching_char == NULL)
12481 return ANIM_EVENT_NONE;
12483 result = ANIM_EVENT_CE_CHANGE;
12485 s_ptr = matching_char + pattern_1_len;
12487 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12488 if (*s_ptr >= '0' && *s_ptr <= '9')
12490 int gic_ce_nr = (*s_ptr++ - '0');
12492 if (*s_ptr >= '0' && *s_ptr <= '9')
12494 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12496 if (*s_ptr >= '0' && *s_ptr <= '9')
12497 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12500 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12501 return ANIM_EVENT_NONE;
12503 // custom element stored as 0 to 255
12506 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12510 // invalid custom element number specified
12512 return ANIM_EVENT_NONE;
12515 // check for change page number ("page_X" or "page_XX") (optional)
12516 if (strPrefix(s_ptr, pattern_2))
12518 s_ptr += strlen(pattern_2);
12520 if (*s_ptr >= '0' && *s_ptr <= '9')
12522 int gic_page_nr = (*s_ptr++ - '0');
12524 if (*s_ptr >= '0' && *s_ptr <= '9')
12525 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12527 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12528 return ANIM_EVENT_NONE;
12530 // change page stored as 1 to 32 (0 means "all change pages")
12532 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12536 // invalid animation part number specified
12538 return ANIM_EVENT_NONE;
12542 // discard result if next character is neither delimiter nor whitespace
12543 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12544 *s_ptr == ' ' || *s_ptr == '\t'))
12545 return ANIM_EVENT_NONE;
12550 static int get_anim_parameter_value(char *s)
12552 int event_value[] =
12560 char *pattern_1[] =
12568 char *pattern_2 = ".part_";
12569 char *matching_char = NULL;
12571 int pattern_1_len = 0;
12572 int result = ANIM_EVENT_NONE;
12575 result = get_anim_parameter_value_ce(s);
12577 if (result != ANIM_EVENT_NONE)
12580 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12582 matching_char = strstr(s_ptr, pattern_1[i]);
12583 pattern_1_len = strlen(pattern_1[i]);
12584 result = event_value[i];
12586 if (matching_char != NULL)
12590 if (matching_char == NULL)
12591 return ANIM_EVENT_NONE;
12593 s_ptr = matching_char + pattern_1_len;
12595 // check for main animation number ("anim_X" or "anim_XX")
12596 if (*s_ptr >= '0' && *s_ptr <= '9')
12598 int gic_anim_nr = (*s_ptr++ - '0');
12600 if (*s_ptr >= '0' && *s_ptr <= '9')
12601 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12603 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12604 return ANIM_EVENT_NONE;
12606 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12610 // invalid main animation number specified
12612 return ANIM_EVENT_NONE;
12615 // check for animation part number ("part_X" or "part_XX") (optional)
12616 if (strPrefix(s_ptr, pattern_2))
12618 s_ptr += strlen(pattern_2);
12620 if (*s_ptr >= '0' && *s_ptr <= '9')
12622 int gic_part_nr = (*s_ptr++ - '0');
12624 if (*s_ptr >= '0' && *s_ptr <= '9')
12625 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12627 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12628 return ANIM_EVENT_NONE;
12630 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12634 // invalid animation part number specified
12636 return ANIM_EVENT_NONE;
12640 // discard result if next character is neither delimiter nor whitespace
12641 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12642 *s_ptr == ' ' || *s_ptr == '\t'))
12643 return ANIM_EVENT_NONE;
12648 static int get_anim_parameter_values(char *s)
12650 int list_pos = ANIM_EVENT_UNDEFINED;
12651 int event_value = ANIM_EVENT_DEFAULT;
12653 if (string_has_parameter(s, "any"))
12654 event_value |= ANIM_EVENT_ANY;
12656 if (string_has_parameter(s, "click:self") ||
12657 string_has_parameter(s, "click") ||
12658 string_has_parameter(s, "self"))
12659 event_value |= ANIM_EVENT_SELF;
12661 if (string_has_parameter(s, "unclick:any"))
12662 event_value |= ANIM_EVENT_UNCLICK_ANY;
12664 // if animation event found, add it to global animation event list
12665 if (event_value != ANIM_EVENT_NONE)
12666 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12670 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12671 event_value = get_anim_parameter_value(s);
12673 // if animation event found, add it to global animation event list
12674 if (event_value != ANIM_EVENT_NONE)
12675 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12677 // continue with next part of the string, starting with next comma
12678 s = strchr(s + 1, ',');
12684 static int get_anim_action_parameter_value(char *token)
12686 // check most common default case first to massively speed things up
12687 if (strEqual(token, ARG_UNDEFINED))
12688 return ANIM_EVENT_ACTION_NONE;
12690 int result = getImageIDFromToken(token);
12694 char *gfx_token = getStringCat2("gfx.", token);
12696 result = getImageIDFromToken(gfx_token);
12698 checked_free(gfx_token);
12703 Key key = getKeyFromX11KeyName(token);
12705 if (key != KSYM_UNDEFINED)
12706 result = -(int)key;
12713 result = get_hash_from_string(token); // unsigned int => int
12714 result = ABS(result); // may be negative now
12715 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12717 setHashEntry(anim_url_hash, int2str(result, 0), token);
12722 result = ANIM_EVENT_ACTION_NONE;
12727 int get_parameter_value(char *value_raw, char *suffix, int type)
12729 char *value = getStringToLower(value_raw);
12730 int result = 0; // probably a save default value
12732 if (strEqual(suffix, ".direction"))
12734 result = (strEqual(value, "left") ? MV_LEFT :
12735 strEqual(value, "right") ? MV_RIGHT :
12736 strEqual(value, "up") ? MV_UP :
12737 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12739 else if (strEqual(suffix, ".position"))
12741 result = (strEqual(value, "left") ? POS_LEFT :
12742 strEqual(value, "right") ? POS_RIGHT :
12743 strEqual(value, "top") ? POS_TOP :
12744 strEqual(value, "upper") ? POS_UPPER :
12745 strEqual(value, "middle") ? POS_MIDDLE :
12746 strEqual(value, "lower") ? POS_LOWER :
12747 strEqual(value, "bottom") ? POS_BOTTOM :
12748 strEqual(value, "any") ? POS_ANY :
12749 strEqual(value, "ce") ? POS_CE :
12750 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12751 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12753 else if (strEqual(suffix, ".align"))
12755 result = (strEqual(value, "left") ? ALIGN_LEFT :
12756 strEqual(value, "right") ? ALIGN_RIGHT :
12757 strEqual(value, "center") ? ALIGN_CENTER :
12758 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12760 else if (strEqual(suffix, ".valign"))
12762 result = (strEqual(value, "top") ? VALIGN_TOP :
12763 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12764 strEqual(value, "middle") ? VALIGN_MIDDLE :
12765 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12767 else if (strEqual(suffix, ".anim_mode"))
12769 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12770 string_has_parameter(value, "loop") ? ANIM_LOOP :
12771 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12772 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12773 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12774 string_has_parameter(value, "random") ? ANIM_RANDOM :
12775 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12776 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12777 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12778 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12779 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12780 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12781 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12782 string_has_parameter(value, "all") ? ANIM_ALL :
12783 string_has_parameter(value, "tiled") ? ANIM_TILED :
12784 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12787 if (string_has_parameter(value, "once"))
12788 result |= ANIM_ONCE;
12790 if (string_has_parameter(value, "reverse"))
12791 result |= ANIM_REVERSE;
12793 if (string_has_parameter(value, "opaque_player"))
12794 result |= ANIM_OPAQUE_PLAYER;
12796 if (string_has_parameter(value, "static_panel"))
12797 result |= ANIM_STATIC_PANEL;
12799 else if (strEqual(suffix, ".init_event") ||
12800 strEqual(suffix, ".anim_event"))
12802 result = get_anim_parameter_values(value);
12804 else if (strEqual(suffix, ".init_delay_action") ||
12805 strEqual(suffix, ".anim_delay_action") ||
12806 strEqual(suffix, ".post_delay_action") ||
12807 strEqual(suffix, ".init_event_action") ||
12808 strEqual(suffix, ".anim_event_action"))
12810 result = get_anim_action_parameter_value(value_raw);
12812 else if (strEqual(suffix, ".class"))
12814 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12815 get_hash_from_string(value));
12817 else if (strEqual(suffix, ".style"))
12819 result = STYLE_DEFAULT;
12821 if (string_has_parameter(value, "accurate_borders"))
12822 result |= STYLE_ACCURATE_BORDERS;
12824 if (string_has_parameter(value, "inner_corners"))
12825 result |= STYLE_INNER_CORNERS;
12827 if (string_has_parameter(value, "reverse"))
12828 result |= STYLE_REVERSE;
12830 if (string_has_parameter(value, "leftmost_position"))
12831 result |= STYLE_LEFTMOST_POSITION;
12833 if (string_has_parameter(value, "block_clicks"))
12834 result |= STYLE_BLOCK;
12836 if (string_has_parameter(value, "passthrough_clicks"))
12837 result |= STYLE_PASSTHROUGH;
12839 if (string_has_parameter(value, "multiple_actions"))
12840 result |= STYLE_MULTIPLE_ACTIONS;
12842 if (string_has_parameter(value, "consume_ce_event"))
12843 result |= STYLE_CONSUME_CE_EVENT;
12845 else if (strEqual(suffix, ".fade_mode"))
12847 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12848 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12849 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12850 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12851 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12852 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12853 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12854 FADE_MODE_DEFAULT);
12856 else if (strEqual(suffix, ".auto_delay_unit"))
12858 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12859 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12860 AUTO_DELAY_UNIT_DEFAULT);
12862 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12864 result = gfx.get_font_from_token_function(value);
12866 else // generic parameter of type integer or boolean
12868 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12869 type == TYPE_INTEGER ? get_integer_from_string(value) :
12870 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12871 ARG_UNDEFINED_VALUE);
12879 static int get_token_parameter_value(char *token, char *value_raw)
12883 if (token == NULL || value_raw == NULL)
12884 return ARG_UNDEFINED_VALUE;
12886 suffix = strrchr(token, '.');
12887 if (suffix == NULL)
12890 if (strEqual(suffix, ".element"))
12891 return getElementFromToken(value_raw);
12893 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12894 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12897 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12898 boolean ignore_defaults)
12902 for (i = 0; image_config_vars[i].token != NULL; i++)
12904 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12906 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12907 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12911 *image_config_vars[i].value =
12912 get_token_parameter_value(image_config_vars[i].token, value);
12916 void InitMenuDesignSettings_Static(void)
12918 // always start with reliable default values from static default config
12919 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12922 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12926 // the following initializes hierarchical values from static configuration
12928 // special case: initialize "ARG_DEFAULT" values in static default config
12929 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12930 titlescreen_initial_first_default.fade_mode =
12931 title_initial_first_default.fade_mode;
12932 titlescreen_initial_first_default.fade_delay =
12933 title_initial_first_default.fade_delay;
12934 titlescreen_initial_first_default.post_delay =
12935 title_initial_first_default.post_delay;
12936 titlescreen_initial_first_default.auto_delay =
12937 title_initial_first_default.auto_delay;
12938 titlescreen_initial_first_default.auto_delay_unit =
12939 title_initial_first_default.auto_delay_unit;
12940 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12941 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12942 titlescreen_first_default.post_delay = title_first_default.post_delay;
12943 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12944 titlescreen_first_default.auto_delay_unit =
12945 title_first_default.auto_delay_unit;
12946 titlemessage_initial_first_default.fade_mode =
12947 title_initial_first_default.fade_mode;
12948 titlemessage_initial_first_default.fade_delay =
12949 title_initial_first_default.fade_delay;
12950 titlemessage_initial_first_default.post_delay =
12951 title_initial_first_default.post_delay;
12952 titlemessage_initial_first_default.auto_delay =
12953 title_initial_first_default.auto_delay;
12954 titlemessage_initial_first_default.auto_delay_unit =
12955 title_initial_first_default.auto_delay_unit;
12956 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12957 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12958 titlemessage_first_default.post_delay = title_first_default.post_delay;
12959 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12960 titlemessage_first_default.auto_delay_unit =
12961 title_first_default.auto_delay_unit;
12963 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12964 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12965 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12966 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12967 titlescreen_initial_default.auto_delay_unit =
12968 title_initial_default.auto_delay_unit;
12969 titlescreen_default.fade_mode = title_default.fade_mode;
12970 titlescreen_default.fade_delay = title_default.fade_delay;
12971 titlescreen_default.post_delay = title_default.post_delay;
12972 titlescreen_default.auto_delay = title_default.auto_delay;
12973 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12974 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12975 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12976 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12977 titlemessage_initial_default.auto_delay_unit =
12978 title_initial_default.auto_delay_unit;
12979 titlemessage_default.fade_mode = title_default.fade_mode;
12980 titlemessage_default.fade_delay = title_default.fade_delay;
12981 titlemessage_default.post_delay = title_default.post_delay;
12982 titlemessage_default.auto_delay = title_default.auto_delay;
12983 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12985 // special case: initialize "ARG_DEFAULT" values in static default config
12986 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12987 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12989 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12990 titlescreen_first[i] = titlescreen_first_default;
12991 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12992 titlemessage_first[i] = titlemessage_first_default;
12994 titlescreen_initial[i] = titlescreen_initial_default;
12995 titlescreen[i] = titlescreen_default;
12996 titlemessage_initial[i] = titlemessage_initial_default;
12997 titlemessage[i] = titlemessage_default;
13000 // special case: initialize "ARG_DEFAULT" values in static default config
13001 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13002 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13004 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
13007 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13008 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13009 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13012 // special case: initialize "ARG_DEFAULT" values in static default config
13013 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13014 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13016 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13017 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13018 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13020 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
13023 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13027 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13031 struct XY *dst, *src;
13033 game_buttons_xy[] =
13035 { &game.button.save, &game.button.stop },
13036 { &game.button.pause2, &game.button.pause },
13037 { &game.button.load, &game.button.play },
13038 { &game.button.undo, &game.button.stop },
13039 { &game.button.redo, &game.button.play },
13045 // special case: initialize later added SETUP list size from LEVELS value
13046 if (menu.list_size[GAME_MODE_SETUP] == -1)
13047 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13049 // set default position for snapshot buttons to stop/pause/play buttons
13050 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13051 if ((*game_buttons_xy[i].dst).x == -1 &&
13052 (*game_buttons_xy[i].dst).y == -1)
13053 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13055 // --------------------------------------------------------------------------
13056 // dynamic viewports (including playfield margins, borders and alignments)
13057 // --------------------------------------------------------------------------
13059 // dynamic viewports currently only supported for landscape mode
13060 int display_width = MAX(video.display_width, video.display_height);
13061 int display_height = MIN(video.display_width, video.display_height);
13063 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13065 struct RectWithBorder *vp_window = &viewport.window[i];
13066 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13067 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13068 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13069 boolean dynamic_window_width = (vp_window->min_width != -1);
13070 boolean dynamic_window_height = (vp_window->min_height != -1);
13071 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13072 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13074 // adjust window size if min/max width/height is specified
13076 if (vp_window->min_width != -1)
13078 int window_width = display_width;
13080 // when using static window height, use aspect ratio of display
13081 if (vp_window->min_height == -1)
13082 window_width = vp_window->height * display_width / display_height;
13084 vp_window->width = MAX(vp_window->min_width, window_width);
13087 if (vp_window->min_height != -1)
13089 int window_height = display_height;
13091 // when using static window width, use aspect ratio of display
13092 if (vp_window->min_width == -1)
13093 window_height = vp_window->width * display_height / display_width;
13095 vp_window->height = MAX(vp_window->min_height, window_height);
13098 if (vp_window->max_width != -1)
13099 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13101 if (vp_window->max_height != -1)
13102 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13104 int playfield_width = vp_window->width;
13105 int playfield_height = vp_window->height;
13107 // adjust playfield size and position according to specified margins
13109 playfield_width -= vp_playfield->margin_left;
13110 playfield_width -= vp_playfield->margin_right;
13112 playfield_height -= vp_playfield->margin_top;
13113 playfield_height -= vp_playfield->margin_bottom;
13115 // adjust playfield size if min/max width/height is specified
13117 if (vp_playfield->min_width != -1)
13118 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13120 if (vp_playfield->min_height != -1)
13121 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13123 if (vp_playfield->max_width != -1)
13124 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13126 if (vp_playfield->max_height != -1)
13127 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13129 // adjust playfield position according to specified alignment
13131 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13132 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13133 else if (vp_playfield->align == ALIGN_CENTER)
13134 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13135 else if (vp_playfield->align == ALIGN_RIGHT)
13136 vp_playfield->x += playfield_width - vp_playfield->width;
13138 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13139 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13140 else if (vp_playfield->valign == VALIGN_MIDDLE)
13141 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13142 else if (vp_playfield->valign == VALIGN_BOTTOM)
13143 vp_playfield->y += playfield_height - vp_playfield->height;
13145 vp_playfield->x += vp_playfield->margin_left;
13146 vp_playfield->y += vp_playfield->margin_top;
13148 // adjust individual playfield borders if only default border is specified
13150 if (vp_playfield->border_left == -1)
13151 vp_playfield->border_left = vp_playfield->border_size;
13152 if (vp_playfield->border_right == -1)
13153 vp_playfield->border_right = vp_playfield->border_size;
13154 if (vp_playfield->border_top == -1)
13155 vp_playfield->border_top = vp_playfield->border_size;
13156 if (vp_playfield->border_bottom == -1)
13157 vp_playfield->border_bottom = vp_playfield->border_size;
13159 // set dynamic playfield borders if borders are specified as undefined
13160 // (but only if window size was dynamic and playfield size was static)
13162 if (dynamic_window_width && !dynamic_playfield_width)
13164 if (vp_playfield->border_left == -1)
13166 vp_playfield->border_left = (vp_playfield->x -
13167 vp_playfield->margin_left);
13168 vp_playfield->x -= vp_playfield->border_left;
13169 vp_playfield->width += vp_playfield->border_left;
13172 if (vp_playfield->border_right == -1)
13174 vp_playfield->border_right = (vp_window->width -
13176 vp_playfield->width -
13177 vp_playfield->margin_right);
13178 vp_playfield->width += vp_playfield->border_right;
13182 if (dynamic_window_height && !dynamic_playfield_height)
13184 if (vp_playfield->border_top == -1)
13186 vp_playfield->border_top = (vp_playfield->y -
13187 vp_playfield->margin_top);
13188 vp_playfield->y -= vp_playfield->border_top;
13189 vp_playfield->height += vp_playfield->border_top;
13192 if (vp_playfield->border_bottom == -1)
13194 vp_playfield->border_bottom = (vp_window->height -
13196 vp_playfield->height -
13197 vp_playfield->margin_bottom);
13198 vp_playfield->height += vp_playfield->border_bottom;
13202 // adjust playfield size to be a multiple of a defined alignment tile size
13204 int align_size = vp_playfield->align_size;
13205 int playfield_xtiles = vp_playfield->width / align_size;
13206 int playfield_ytiles = vp_playfield->height / align_size;
13207 int playfield_width_corrected = playfield_xtiles * align_size;
13208 int playfield_height_corrected = playfield_ytiles * align_size;
13209 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13210 i == GFX_SPECIAL_ARG_EDITOR);
13212 if (is_playfield_mode &&
13213 dynamic_playfield_width &&
13214 vp_playfield->width != playfield_width_corrected)
13216 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13218 vp_playfield->width = playfield_width_corrected;
13220 if (vp_playfield->align == ALIGN_LEFT)
13222 vp_playfield->border_left += playfield_xdiff;
13224 else if (vp_playfield->align == ALIGN_RIGHT)
13226 vp_playfield->border_right += playfield_xdiff;
13228 else if (vp_playfield->align == ALIGN_CENTER)
13230 int border_left_diff = playfield_xdiff / 2;
13231 int border_right_diff = playfield_xdiff - border_left_diff;
13233 vp_playfield->border_left += border_left_diff;
13234 vp_playfield->border_right += border_right_diff;
13238 if (is_playfield_mode &&
13239 dynamic_playfield_height &&
13240 vp_playfield->height != playfield_height_corrected)
13242 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13244 vp_playfield->height = playfield_height_corrected;
13246 if (vp_playfield->valign == VALIGN_TOP)
13248 vp_playfield->border_top += playfield_ydiff;
13250 else if (vp_playfield->align == VALIGN_BOTTOM)
13252 vp_playfield->border_right += playfield_ydiff;
13254 else if (vp_playfield->align == VALIGN_MIDDLE)
13256 int border_top_diff = playfield_ydiff / 2;
13257 int border_bottom_diff = playfield_ydiff - border_top_diff;
13259 vp_playfield->border_top += border_top_diff;
13260 vp_playfield->border_bottom += border_bottom_diff;
13264 // adjust door positions according to specified alignment
13266 for (j = 0; j < 2; j++)
13268 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13270 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13271 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13272 else if (vp_door->align == ALIGN_CENTER)
13273 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13274 else if (vp_door->align == ALIGN_RIGHT)
13275 vp_door->x += vp_window->width - vp_door->width;
13277 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13278 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13279 else if (vp_door->valign == VALIGN_MIDDLE)
13280 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13281 else if (vp_door->valign == VALIGN_BOTTOM)
13282 vp_door->y += vp_window->height - vp_door->height;
13287 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13291 struct XYTileSize *dst, *src;
13294 editor_buttons_xy[] =
13297 &editor.button.element_left, &editor.palette.element_left,
13298 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13301 &editor.button.element_middle, &editor.palette.element_middle,
13302 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13305 &editor.button.element_right, &editor.palette.element_right,
13306 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13313 // set default position for element buttons to element graphics
13314 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13316 if ((*editor_buttons_xy[i].dst).x == -1 &&
13317 (*editor_buttons_xy[i].dst).y == -1)
13319 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13321 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13323 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13327 // adjust editor palette rows and columns if specified to be dynamic
13329 if (editor.palette.cols == -1)
13331 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13332 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13333 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13335 editor.palette.cols = (vp_width - sc_width) / bt_width;
13337 if (editor.palette.x == -1)
13339 int palette_width = editor.palette.cols * bt_width + sc_width;
13341 editor.palette.x = (vp_width - palette_width) / 2;
13345 if (editor.palette.rows == -1)
13347 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13348 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13349 int tx_height = getFontHeight(FONT_TEXT_2);
13351 editor.palette.rows = (vp_height - tx_height) / bt_height;
13353 if (editor.palette.y == -1)
13355 int palette_height = editor.palette.rows * bt_height + tx_height;
13357 editor.palette.y = (vp_height - palette_height) / 2;
13362 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13363 boolean initialize)
13365 // special case: check if network and preview player positions are redefined,
13366 // to compare this later against the main menu level preview being redefined
13367 struct TokenIntPtrInfo menu_config_players[] =
13369 { "main.network_players.x", &menu.main.network_players.redefined },
13370 { "main.network_players.y", &menu.main.network_players.redefined },
13371 { "main.preview_players.x", &menu.main.preview_players.redefined },
13372 { "main.preview_players.y", &menu.main.preview_players.redefined },
13373 { "preview.x", &preview.redefined },
13374 { "preview.y", &preview.redefined }
13380 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13381 *menu_config_players[i].value = FALSE;
13385 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13386 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13387 *menu_config_players[i].value = TRUE;
13391 static void InitMenuDesignSettings_PreviewPlayers(void)
13393 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13396 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13398 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13401 static void LoadMenuDesignSettingsFromFilename(char *filename)
13403 static struct TitleFadingInfo tfi;
13404 static struct TitleMessageInfo tmi;
13405 static struct TokenInfo title_tokens[] =
13407 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13408 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13409 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13410 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13411 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13415 static struct TokenInfo titlemessage_tokens[] =
13417 { TYPE_INTEGER, &tmi.x, ".x" },
13418 { TYPE_INTEGER, &tmi.y, ".y" },
13419 { TYPE_INTEGER, &tmi.width, ".width" },
13420 { TYPE_INTEGER, &tmi.height, ".height" },
13421 { TYPE_INTEGER, &tmi.chars, ".chars" },
13422 { TYPE_INTEGER, &tmi.lines, ".lines" },
13423 { TYPE_INTEGER, &tmi.align, ".align" },
13424 { TYPE_INTEGER, &tmi.valign, ".valign" },
13425 { TYPE_INTEGER, &tmi.font, ".font" },
13426 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13427 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13428 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13429 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13430 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13431 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13432 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13433 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13434 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13440 struct TitleFadingInfo *info;
13445 // initialize first titles from "enter screen" definitions, if defined
13446 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13447 { &title_first_default, "menu.enter_screen.TITLE" },
13449 // initialize title screens from "next screen" definitions, if defined
13450 { &title_initial_default, "menu.next_screen.TITLE" },
13451 { &title_default, "menu.next_screen.TITLE" },
13457 struct TitleMessageInfo *array;
13460 titlemessage_arrays[] =
13462 // initialize first titles from "enter screen" definitions, if defined
13463 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13464 { titlescreen_first, "menu.enter_screen.TITLE" },
13465 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13466 { titlemessage_first, "menu.enter_screen.TITLE" },
13468 // initialize titles from "next screen" definitions, if defined
13469 { titlescreen_initial, "menu.next_screen.TITLE" },
13470 { titlescreen, "menu.next_screen.TITLE" },
13471 { titlemessage_initial, "menu.next_screen.TITLE" },
13472 { titlemessage, "menu.next_screen.TITLE" },
13474 // overwrite titles with title definitions, if defined
13475 { titlescreen_initial_first, "[title_initial]" },
13476 { titlescreen_first, "[title]" },
13477 { titlemessage_initial_first, "[title_initial]" },
13478 { titlemessage_first, "[title]" },
13480 { titlescreen_initial, "[title_initial]" },
13481 { titlescreen, "[title]" },
13482 { titlemessage_initial, "[title_initial]" },
13483 { titlemessage, "[title]" },
13485 // overwrite titles with title screen/message definitions, if defined
13486 { titlescreen_initial_first, "[titlescreen_initial]" },
13487 { titlescreen_first, "[titlescreen]" },
13488 { titlemessage_initial_first, "[titlemessage_initial]" },
13489 { titlemessage_first, "[titlemessage]" },
13491 { titlescreen_initial, "[titlescreen_initial]" },
13492 { titlescreen, "[titlescreen]" },
13493 { titlemessage_initial, "[titlemessage_initial]" },
13494 { titlemessage, "[titlemessage]" },
13498 SetupFileHash *setup_file_hash;
13501 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13504 // the following initializes hierarchical values from dynamic configuration
13506 // special case: initialize with default values that may be overwritten
13507 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13508 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13510 struct TokenIntPtrInfo menu_config[] =
13512 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13513 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13514 { "menu.list_size", &menu.list_size[i] }
13517 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13519 char *token = menu_config[j].token;
13520 char *value = getHashEntry(setup_file_hash, token);
13523 *menu_config[j].value = get_integer_from_string(value);
13527 // special case: initialize with default values that may be overwritten
13528 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13529 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13531 struct TokenIntPtrInfo menu_config[] =
13533 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13534 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13535 { "menu.list_size.INFO", &menu.list_size_info[i] },
13536 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13537 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13540 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13542 char *token = menu_config[j].token;
13543 char *value = getHashEntry(setup_file_hash, token);
13546 *menu_config[j].value = get_integer_from_string(value);
13550 // special case: initialize with default values that may be overwritten
13551 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13552 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13554 struct TokenIntPtrInfo menu_config[] =
13556 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13557 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13560 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13562 char *token = menu_config[j].token;
13563 char *value = getHashEntry(setup_file_hash, token);
13566 *menu_config[j].value = get_integer_from_string(value);
13570 // special case: initialize with default values that may be overwritten
13571 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13572 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13574 struct TokenIntPtrInfo menu_config[] =
13576 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13577 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13578 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13579 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13580 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13581 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13582 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13583 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13584 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13585 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13588 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13590 char *token = menu_config[j].token;
13591 char *value = getHashEntry(setup_file_hash, token);
13594 *menu_config[j].value = get_integer_from_string(value);
13598 // special case: initialize with default values that may be overwritten
13599 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13600 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13602 struct TokenIntPtrInfo menu_config[] =
13604 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13605 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13606 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13607 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13608 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13609 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13610 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13611 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13612 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13615 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13617 char *token = menu_config[j].token;
13618 char *value = getHashEntry(setup_file_hash, token);
13621 *menu_config[j].value = get_token_parameter_value(token, value);
13625 // special case: initialize with default values that may be overwritten
13626 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13627 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13631 char *token_prefix;
13632 struct RectWithBorder *struct_ptr;
13636 { "viewport.window", &viewport.window[i] },
13637 { "viewport.playfield", &viewport.playfield[i] },
13638 { "viewport.door_1", &viewport.door_1[i] },
13639 { "viewport.door_2", &viewport.door_2[i] }
13642 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13644 struct TokenIntPtrInfo vp_config[] =
13646 { ".x", &vp_struct[j].struct_ptr->x },
13647 { ".y", &vp_struct[j].struct_ptr->y },
13648 { ".width", &vp_struct[j].struct_ptr->width },
13649 { ".height", &vp_struct[j].struct_ptr->height },
13650 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13651 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13652 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13653 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13654 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13655 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13656 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13657 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13658 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13659 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13660 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13661 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13662 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13663 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13664 { ".align", &vp_struct[j].struct_ptr->align },
13665 { ".valign", &vp_struct[j].struct_ptr->valign }
13668 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13670 char *token = getStringCat2(vp_struct[j].token_prefix,
13671 vp_config[k].token);
13672 char *value = getHashEntry(setup_file_hash, token);
13675 *vp_config[k].value = get_token_parameter_value(token, value);
13682 // special case: initialize with default values that may be overwritten
13683 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13684 for (i = 0; title_info[i].info != NULL; i++)
13686 struct TitleFadingInfo *info = title_info[i].info;
13687 char *base_token = title_info[i].text;
13689 for (j = 0; title_tokens[j].type != -1; j++)
13691 char *token = getStringCat2(base_token, title_tokens[j].text);
13692 char *value = getHashEntry(setup_file_hash, token);
13696 int parameter_value = get_token_parameter_value(token, value);
13700 *(int *)title_tokens[j].value = (int)parameter_value;
13709 // special case: initialize with default values that may be overwritten
13710 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13711 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13713 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13714 char *base_token = titlemessage_arrays[i].text;
13716 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13718 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13719 char *value = getHashEntry(setup_file_hash, token);
13723 int parameter_value = get_token_parameter_value(token, value);
13725 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13729 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13730 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13732 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13742 // read (and overwrite with) values that may be specified in config file
13743 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13745 // special case: check if network and preview player positions are redefined
13746 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13748 freeSetupFileHash(setup_file_hash);
13751 void LoadMenuDesignSettings(void)
13753 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13755 InitMenuDesignSettings_Static();
13756 InitMenuDesignSettings_SpecialPreProcessing();
13757 InitMenuDesignSettings_PreviewPlayers();
13759 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13761 // first look for special settings configured in level series config
13762 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13764 if (fileExists(filename_base))
13765 LoadMenuDesignSettingsFromFilename(filename_base);
13768 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13770 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13771 LoadMenuDesignSettingsFromFilename(filename_local);
13773 InitMenuDesignSettings_SpecialPostProcessing();
13776 void LoadMenuDesignSettings_AfterGraphics(void)
13778 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13781 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13782 boolean ignore_defaults)
13786 for (i = 0; sound_config_vars[i].token != NULL; i++)
13788 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13790 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13791 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13795 *sound_config_vars[i].value =
13796 get_token_parameter_value(sound_config_vars[i].token, value);
13800 void InitSoundSettings_Static(void)
13802 // always start with reliable default values from static default config
13803 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13806 static void LoadSoundSettingsFromFilename(char *filename)
13808 SetupFileHash *setup_file_hash;
13810 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13813 // read (and overwrite with) values that may be specified in config file
13814 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13816 freeSetupFileHash(setup_file_hash);
13819 void LoadSoundSettings(void)
13821 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13823 InitSoundSettings_Static();
13825 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13827 // first look for special settings configured in level series config
13828 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13830 if (fileExists(filename_base))
13831 LoadSoundSettingsFromFilename(filename_base);
13834 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13836 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13837 LoadSoundSettingsFromFilename(filename_local);
13840 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13842 char *filename = getEditorSetupFilename();
13843 SetupFileList *setup_file_list, *list;
13844 SetupFileHash *element_hash;
13845 int num_unknown_tokens = 0;
13848 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13851 element_hash = newSetupFileHash();
13853 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13854 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13856 // determined size may be larger than needed (due to unknown elements)
13858 for (list = setup_file_list; list != NULL; list = list->next)
13861 // add space for up to 3 more elements for padding that may be needed
13862 *num_elements += 3;
13864 // free memory for old list of elements, if needed
13865 checked_free(*elements);
13867 // allocate memory for new list of elements
13868 *elements = checked_malloc(*num_elements * sizeof(int));
13871 for (list = setup_file_list; list != NULL; list = list->next)
13873 char *value = getHashEntry(element_hash, list->token);
13875 if (value == NULL) // try to find obsolete token mapping
13877 char *mapped_token = get_mapped_token(list->token);
13879 if (mapped_token != NULL)
13881 value = getHashEntry(element_hash, mapped_token);
13883 free(mapped_token);
13889 (*elements)[(*num_elements)++] = atoi(value);
13893 if (num_unknown_tokens == 0)
13896 Warn("unknown token(s) found in config file:");
13897 Warn("- config file: '%s'", filename);
13899 num_unknown_tokens++;
13902 Warn("- token: '%s'", list->token);
13906 if (num_unknown_tokens > 0)
13909 while (*num_elements % 4) // pad with empty elements, if needed
13910 (*elements)[(*num_elements)++] = EL_EMPTY;
13912 freeSetupFileList(setup_file_list);
13913 freeSetupFileHash(element_hash);
13916 for (i = 0; i < *num_elements; i++)
13917 Debug("editor", "element '%s' [%d]\n",
13918 element_info[(*elements)[i]].token_name, (*elements)[i]);
13922 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13925 SetupFileHash *setup_file_hash = NULL;
13926 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13927 char *filename_music, *filename_prefix, *filename_info;
13933 token_to_value_ptr[] =
13935 { "title_header", &tmp_music_file_info.title_header },
13936 { "artist_header", &tmp_music_file_info.artist_header },
13937 { "album_header", &tmp_music_file_info.album_header },
13938 { "year_header", &tmp_music_file_info.year_header },
13939 { "played_header", &tmp_music_file_info.played_header },
13941 { "title", &tmp_music_file_info.title },
13942 { "artist", &tmp_music_file_info.artist },
13943 { "album", &tmp_music_file_info.album },
13944 { "year", &tmp_music_file_info.year },
13945 { "played", &tmp_music_file_info.played },
13951 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13952 getCustomMusicFilename(basename));
13954 if (filename_music == NULL)
13957 // ---------- try to replace file extension ----------
13959 filename_prefix = getStringCopy(filename_music);
13960 if (strrchr(filename_prefix, '.') != NULL)
13961 *strrchr(filename_prefix, '.') = '\0';
13962 filename_info = getStringCat2(filename_prefix, ".txt");
13964 if (fileExists(filename_info))
13965 setup_file_hash = loadSetupFileHash(filename_info);
13967 free(filename_prefix);
13968 free(filename_info);
13970 if (setup_file_hash == NULL)
13972 // ---------- try to add file extension ----------
13974 filename_prefix = getStringCopy(filename_music);
13975 filename_info = getStringCat2(filename_prefix, ".txt");
13977 if (fileExists(filename_info))
13978 setup_file_hash = loadSetupFileHash(filename_info);
13980 free(filename_prefix);
13981 free(filename_info);
13984 if (setup_file_hash == NULL)
13987 // ---------- music file info found ----------
13989 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13991 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13993 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13995 *token_to_value_ptr[i].value_ptr =
13996 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13999 tmp_music_file_info.basename = getStringCopy(basename);
14000 tmp_music_file_info.music = music;
14001 tmp_music_file_info.is_sound = is_sound;
14003 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14004 *new_music_file_info = tmp_music_file_info;
14006 return new_music_file_info;
14009 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14011 return get_music_file_info_ext(basename, music, FALSE);
14014 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14016 return get_music_file_info_ext(basename, sound, TRUE);
14019 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14020 char *basename, boolean is_sound)
14022 for (; list != NULL; list = list->next)
14023 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14029 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14031 return music_info_listed_ext(list, basename, FALSE);
14034 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14036 return music_info_listed_ext(list, basename, TRUE);
14039 void LoadMusicInfo(void)
14041 int num_music_noconf = getMusicListSize_NoConf();
14042 int num_music = getMusicListSize();
14043 int num_sounds = getSoundListSize();
14044 struct FileInfo *music, *sound;
14045 struct MusicFileInfo *next, **new;
14049 while (music_file_info != NULL)
14051 next = music_file_info->next;
14053 checked_free(music_file_info->basename);
14055 checked_free(music_file_info->title_header);
14056 checked_free(music_file_info->artist_header);
14057 checked_free(music_file_info->album_header);
14058 checked_free(music_file_info->year_header);
14059 checked_free(music_file_info->played_header);
14061 checked_free(music_file_info->title);
14062 checked_free(music_file_info->artist);
14063 checked_free(music_file_info->album);
14064 checked_free(music_file_info->year);
14065 checked_free(music_file_info->played);
14067 free(music_file_info);
14069 music_file_info = next;
14072 new = &music_file_info;
14074 // get (configured or unconfigured) music file info for all levels
14075 for (i = leveldir_current->first_level;
14076 i <= leveldir_current->last_level; i++)
14080 if (levelset.music[i] != MUS_UNDEFINED)
14082 // get music file info for configured level music
14083 music_nr = levelset.music[i];
14085 else if (num_music_noconf > 0)
14087 // get music file info for unconfigured level music
14088 int level_pos = i - leveldir_current->first_level;
14090 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14097 char *basename = getMusicInfoEntryFilename(music_nr);
14099 if (basename == NULL)
14102 if (!music_info_listed(music_file_info, basename))
14104 *new = get_music_file_info(basename, music_nr);
14107 new = &(*new)->next;
14111 // get music file info for all remaining configured music files
14112 for (i = 0; i < num_music; i++)
14114 music = getMusicListEntry(i);
14116 if (music->filename == NULL)
14119 if (strEqual(music->filename, UNDEFINED_FILENAME))
14122 // a configured file may be not recognized as music
14123 if (!FileIsMusic(music->filename))
14126 if (!music_info_listed(music_file_info, music->filename))
14128 *new = get_music_file_info(music->filename, i);
14131 new = &(*new)->next;
14135 // get sound file info for all configured sound files
14136 for (i = 0; i < num_sounds; i++)
14138 sound = getSoundListEntry(i);
14140 if (sound->filename == NULL)
14143 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14146 // a configured file may be not recognized as sound
14147 if (!FileIsSound(sound->filename))
14150 if (!sound_info_listed(music_file_info, sound->filename))
14152 *new = get_sound_file_info(sound->filename, i);
14154 new = &(*new)->next;
14158 // add pointers to previous list nodes
14160 struct MusicFileInfo *node = music_file_info;
14162 while (node != NULL)
14165 node->next->prev = node;
14171 static void add_helpanim_entry(int element, int action, int direction,
14172 int delay, int *num_list_entries)
14174 struct HelpAnimInfo *new_list_entry;
14175 (*num_list_entries)++;
14178 checked_realloc(helpanim_info,
14179 *num_list_entries * sizeof(struct HelpAnimInfo));
14180 new_list_entry = &helpanim_info[*num_list_entries - 1];
14182 new_list_entry->element = element;
14183 new_list_entry->action = action;
14184 new_list_entry->direction = direction;
14185 new_list_entry->delay = delay;
14188 static void print_unknown_token(char *filename, char *token, int token_nr)
14193 Warn("unknown token(s) found in config file:");
14194 Warn("- config file: '%s'", filename);
14197 Warn("- token: '%s'", token);
14200 static void print_unknown_token_end(int token_nr)
14206 void LoadHelpAnimInfo(void)
14208 char *filename = getHelpAnimFilename();
14209 SetupFileList *setup_file_list = NULL, *list;
14210 SetupFileHash *element_hash, *action_hash, *direction_hash;
14211 int num_list_entries = 0;
14212 int num_unknown_tokens = 0;
14215 if (fileExists(filename))
14216 setup_file_list = loadSetupFileList(filename);
14218 if (setup_file_list == NULL)
14220 // use reliable default values from static configuration
14221 SetupFileList *insert_ptr;
14223 insert_ptr = setup_file_list =
14224 newSetupFileList(helpanim_config[0].token,
14225 helpanim_config[0].value);
14227 for (i = 1; helpanim_config[i].token; i++)
14228 insert_ptr = addListEntry(insert_ptr,
14229 helpanim_config[i].token,
14230 helpanim_config[i].value);
14233 element_hash = newSetupFileHash();
14234 action_hash = newSetupFileHash();
14235 direction_hash = newSetupFileHash();
14237 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14238 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14240 for (i = 0; i < NUM_ACTIONS; i++)
14241 setHashEntry(action_hash, element_action_info[i].suffix,
14242 i_to_a(element_action_info[i].value));
14244 // do not store direction index (bit) here, but direction value!
14245 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14246 setHashEntry(direction_hash, element_direction_info[i].suffix,
14247 i_to_a(1 << element_direction_info[i].value));
14249 for (list = setup_file_list; list != NULL; list = list->next)
14251 char *element_token, *action_token, *direction_token;
14252 char *element_value, *action_value, *direction_value;
14253 int delay = atoi(list->value);
14255 if (strEqual(list->token, "end"))
14257 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14262 /* first try to break element into element/action/direction parts;
14263 if this does not work, also accept combined "element[.act][.dir]"
14264 elements (like "dynamite.active"), which are unique elements */
14266 if (strchr(list->token, '.') == NULL) // token contains no '.'
14268 element_value = getHashEntry(element_hash, list->token);
14269 if (element_value != NULL) // element found
14270 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14271 &num_list_entries);
14274 // no further suffixes found -- this is not an element
14275 print_unknown_token(filename, list->token, num_unknown_tokens++);
14281 // token has format "<prefix>.<something>"
14283 action_token = strchr(list->token, '.'); // suffix may be action ...
14284 direction_token = action_token; // ... or direction
14286 element_token = getStringCopy(list->token);
14287 *strchr(element_token, '.') = '\0';
14289 element_value = getHashEntry(element_hash, element_token);
14291 if (element_value == NULL) // this is no element
14293 element_value = getHashEntry(element_hash, list->token);
14294 if (element_value != NULL) // combined element found
14295 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14296 &num_list_entries);
14298 print_unknown_token(filename, list->token, num_unknown_tokens++);
14300 free(element_token);
14305 action_value = getHashEntry(action_hash, action_token);
14307 if (action_value != NULL) // action found
14309 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14310 &num_list_entries);
14312 free(element_token);
14317 direction_value = getHashEntry(direction_hash, direction_token);
14319 if (direction_value != NULL) // direction found
14321 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14322 &num_list_entries);
14324 free(element_token);
14329 if (strchr(action_token + 1, '.') == NULL)
14331 // no further suffixes found -- this is not an action nor direction
14333 element_value = getHashEntry(element_hash, list->token);
14334 if (element_value != NULL) // combined element found
14335 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14336 &num_list_entries);
14338 print_unknown_token(filename, list->token, num_unknown_tokens++);
14340 free(element_token);
14345 // token has format "<prefix>.<suffix>.<something>"
14347 direction_token = strchr(action_token + 1, '.');
14349 action_token = getStringCopy(action_token);
14350 *strchr(action_token + 1, '.') = '\0';
14352 action_value = getHashEntry(action_hash, action_token);
14354 if (action_value == NULL) // this is no action
14356 element_value = getHashEntry(element_hash, list->token);
14357 if (element_value != NULL) // combined element found
14358 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14359 &num_list_entries);
14361 print_unknown_token(filename, list->token, num_unknown_tokens++);
14363 free(element_token);
14364 free(action_token);
14369 direction_value = getHashEntry(direction_hash, direction_token);
14371 if (direction_value != NULL) // direction found
14373 add_helpanim_entry(atoi(element_value), atoi(action_value),
14374 atoi(direction_value), delay, &num_list_entries);
14376 free(element_token);
14377 free(action_token);
14382 // this is no direction
14384 element_value = getHashEntry(element_hash, list->token);
14385 if (element_value != NULL) // combined element found
14386 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14387 &num_list_entries);
14389 print_unknown_token(filename, list->token, num_unknown_tokens++);
14391 free(element_token);
14392 free(action_token);
14395 print_unknown_token_end(num_unknown_tokens);
14397 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14398 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14400 freeSetupFileList(setup_file_list);
14401 freeSetupFileHash(element_hash);
14402 freeSetupFileHash(action_hash);
14403 freeSetupFileHash(direction_hash);
14406 for (i = 0; i < num_list_entries; i++)
14407 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14408 EL_NAME(helpanim_info[i].element),
14409 helpanim_info[i].element,
14410 helpanim_info[i].action,
14411 helpanim_info[i].direction,
14412 helpanim_info[i].delay);
14416 void LoadHelpTextInfo(void)
14418 char *filename = getHelpTextFilename();
14421 if (helptext_info != NULL)
14423 freeSetupFileHash(helptext_info);
14424 helptext_info = NULL;
14427 if (fileExists(filename))
14428 helptext_info = loadSetupFileHash(filename);
14430 if (helptext_info == NULL)
14432 // use reliable default values from static configuration
14433 helptext_info = newSetupFileHash();
14435 for (i = 0; helptext_config[i].token; i++)
14436 setHashEntry(helptext_info,
14437 helptext_config[i].token,
14438 helptext_config[i].value);
14442 BEGIN_HASH_ITERATION(helptext_info, itr)
14444 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14445 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14447 END_HASH_ITERATION(hash, itr)
14452 // ----------------------------------------------------------------------------
14454 // ----------------------------------------------------------------------------
14456 #define MAX_NUM_CONVERT_LEVELS 1000
14458 void ConvertLevels(void)
14460 static LevelDirTree *convert_leveldir = NULL;
14461 static int convert_level_nr = -1;
14462 static int num_levels_handled = 0;
14463 static int num_levels_converted = 0;
14464 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14467 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14468 global.convert_leveldir);
14470 if (convert_leveldir == NULL)
14471 Fail("no such level identifier: '%s'", global.convert_leveldir);
14473 leveldir_current = convert_leveldir;
14475 if (global.convert_level_nr != -1)
14477 convert_leveldir->first_level = global.convert_level_nr;
14478 convert_leveldir->last_level = global.convert_level_nr;
14481 convert_level_nr = convert_leveldir->first_level;
14483 PrintLine("=", 79);
14484 Print("Converting levels\n");
14485 PrintLine("-", 79);
14486 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14487 Print("Level series name: '%s'\n", convert_leveldir->name);
14488 Print("Level series author: '%s'\n", convert_leveldir->author);
14489 Print("Number of levels: %d\n", convert_leveldir->levels);
14490 PrintLine("=", 79);
14493 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14494 levels_failed[i] = FALSE;
14496 while (convert_level_nr <= convert_leveldir->last_level)
14498 char *level_filename;
14501 level_nr = convert_level_nr++;
14503 Print("Level %03d: ", level_nr);
14505 LoadLevel(level_nr);
14506 if (level.no_level_file || level.no_valid_file)
14508 Print("(no level)\n");
14512 Print("converting level ... ");
14515 // special case: conversion of some EMC levels as requested by ACME
14516 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14519 level_filename = getDefaultLevelFilename(level_nr);
14520 new_level = !fileExists(level_filename);
14524 SaveLevel(level_nr);
14526 num_levels_converted++;
14528 Print("converted.\n");
14532 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14533 levels_failed[level_nr] = TRUE;
14535 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14538 num_levels_handled++;
14542 PrintLine("=", 79);
14543 Print("Number of levels handled: %d\n", num_levels_handled);
14544 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14545 (num_levels_handled ?
14546 num_levels_converted * 100 / num_levels_handled : 0));
14547 PrintLine("-", 79);
14548 Print("Summary (for automatic parsing by scripts):\n");
14549 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14550 convert_leveldir->identifier, num_levels_converted,
14551 num_levels_handled,
14552 (num_levels_handled ?
14553 num_levels_converted * 100 / num_levels_handled : 0));
14555 if (num_levels_handled != num_levels_converted)
14557 Print(", FAILED:");
14558 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14559 if (levels_failed[i])
14564 PrintLine("=", 79);
14566 CloseAllAndExit(0);
14570 // ----------------------------------------------------------------------------
14571 // create and save images for use in level sketches (raw BMP format)
14572 // ----------------------------------------------------------------------------
14574 void CreateLevelSketchImages(void)
14580 InitElementPropertiesGfxElement();
14582 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14583 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14585 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14587 int element = getMappedElement(i);
14588 char basename1[16];
14589 char basename2[16];
14593 sprintf(basename1, "%04d.bmp", i);
14594 sprintf(basename2, "%04ds.bmp", i);
14596 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14597 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14599 DrawSizedElement(0, 0, element, TILESIZE);
14600 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14602 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14603 Fail("cannot save level sketch image file '%s'", filename1);
14605 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14606 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14608 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14609 Fail("cannot save level sketch image file '%s'", filename2);
14614 // create corresponding SQL statements (for normal and small images)
14617 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14618 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14621 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14622 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14624 // optional: create content for forum level sketch demonstration post
14626 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14629 FreeBitmap(bitmap1);
14630 FreeBitmap(bitmap2);
14633 fprintf(stderr, "\n");
14635 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14637 CloseAllAndExit(0);
14641 // ----------------------------------------------------------------------------
14642 // create and save images for element collecting animations (raw BMP format)
14643 // ----------------------------------------------------------------------------
14645 static boolean createCollectImage(int element)
14647 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14650 void CreateCollectElementImages(void)
14654 int anim_frames = num_steps - 1;
14655 int tile_size = TILESIZE;
14656 int anim_width = tile_size * anim_frames;
14657 int anim_height = tile_size;
14658 int num_collect_images = 0;
14659 int pos_collect_images = 0;
14661 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14662 if (createCollectImage(i))
14663 num_collect_images++;
14665 Info("Creating %d element collecting animation images ...",
14666 num_collect_images);
14668 int dst_width = anim_width * 2;
14669 int dst_height = anim_height * num_collect_images / 2;
14670 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14671 char *basename_bmp = "RocksCollect.bmp";
14672 char *basename_png = "RocksCollect.png";
14673 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14674 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14675 int len_filename_bmp = strlen(filename_bmp);
14676 int len_filename_png = strlen(filename_png);
14677 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14678 char cmd_convert[max_command_len];
14680 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14684 // force using RGBA surface for destination bitmap
14685 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14686 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14688 dst_bitmap->surface =
14689 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14691 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14693 if (!createCollectImage(i))
14696 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14697 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14698 int graphic = el2img(i);
14699 char *token_name = element_info[i].token_name;
14700 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14701 Bitmap *src_bitmap;
14704 Info("- creating collecting image for '%s' ...", token_name);
14706 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14708 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14709 tile_size, tile_size, 0, 0);
14711 // force using RGBA surface for temporary bitmap (using transparent black)
14712 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14713 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14715 tmp_bitmap->surface =
14716 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14718 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14720 for (j = 0; j < anim_frames; j++)
14722 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14723 int frame_size = frame_size_final * num_steps;
14724 int offset = (tile_size - frame_size_final) / 2;
14725 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14727 while (frame_size > frame_size_final)
14731 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14733 FreeBitmap(frame_bitmap);
14735 frame_bitmap = half_bitmap;
14738 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14739 frame_size_final, frame_size_final,
14740 dst_x + j * tile_size + offset, dst_y + offset);
14742 FreeBitmap(frame_bitmap);
14745 tmp_bitmap->surface_masked = NULL;
14747 FreeBitmap(tmp_bitmap);
14749 pos_collect_images++;
14752 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14753 Fail("cannot save element collecting image file '%s'", filename_bmp);
14755 FreeBitmap(dst_bitmap);
14757 Info("Converting image file from BMP to PNG ...");
14759 if (system(cmd_convert) != 0)
14760 Fail("converting image file failed");
14762 unlink(filename_bmp);
14766 CloseAllAndExit(0);
14770 // ----------------------------------------------------------------------------
14771 // create and save images for custom and group elements (raw BMP format)
14772 // ----------------------------------------------------------------------------
14774 void CreateCustomElementImages(char *directory)
14776 char *src_basename = "RocksCE-template.ilbm";
14777 char *dst_basename = "RocksCE.bmp";
14778 char *src_filename = getPath2(directory, src_basename);
14779 char *dst_filename = getPath2(directory, dst_basename);
14780 Bitmap *src_bitmap;
14782 int yoffset_ce = 0;
14783 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14786 InitVideoDefaults();
14788 ReCreateBitmap(&backbuffer, video.width, video.height);
14790 src_bitmap = LoadImage(src_filename);
14792 bitmap = CreateBitmap(TILEX * 16 * 2,
14793 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14796 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14803 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14804 TILEX * x, TILEY * y + yoffset_ce);
14806 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14808 TILEX * x + TILEX * 16,
14809 TILEY * y + yoffset_ce);
14811 for (j = 2; j >= 0; j--)
14815 BlitBitmap(src_bitmap, bitmap,
14816 TILEX + c * 7, 0, 6, 10,
14817 TILEX * x + 6 + j * 7,
14818 TILEY * y + 11 + yoffset_ce);
14820 BlitBitmap(src_bitmap, bitmap,
14821 TILEX + c * 8, TILEY, 6, 10,
14822 TILEX * 16 + TILEX * x + 6 + j * 8,
14823 TILEY * y + 10 + yoffset_ce);
14829 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14836 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14837 TILEX * x, TILEY * y + yoffset_ge);
14839 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14841 TILEX * x + TILEX * 16,
14842 TILEY * y + yoffset_ge);
14844 for (j = 1; j >= 0; j--)
14848 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14849 TILEX * x + 6 + j * 10,
14850 TILEY * y + 11 + yoffset_ge);
14852 BlitBitmap(src_bitmap, bitmap,
14853 TILEX + c * 8, TILEY + 12, 6, 10,
14854 TILEX * 16 + TILEX * x + 10 + j * 8,
14855 TILEY * y + 10 + yoffset_ge);
14861 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14862 Fail("cannot save CE graphics file '%s'", dst_filename);
14864 FreeBitmap(bitmap);
14866 CloseAllAndExit(0);