1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
659 EL_BD_MAGIC_WALL, -1,
660 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
661 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
664 EL_BD_MAGIC_WALL, -1,
665 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
666 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
669 EL_BD_MAGIC_WALL, -1,
670 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
671 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
674 EL_BD_MAGIC_WALL, -1,
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
676 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
679 EL_BD_MAGIC_WALL, -1,
680 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
681 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
684 EL_BD_MAGIC_WALL, -1,
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
686 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
689 EL_BD_MAGIC_WALL, -1,
690 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
691 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
696 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
697 &li.bd_clock_extra_time, 30
701 EL_BD_VOODOO_DOLL, -1,
702 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
703 &li.bd_voodoo_collects_diamonds, FALSE
706 EL_BD_VOODOO_DOLL, -1,
707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
708 &li.bd_voodoo_hurt_kills_player, FALSE
711 EL_BD_VOODOO_DOLL, -1,
712 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
713 &li.bd_voodoo_dies_by_rock, FALSE
716 EL_BD_VOODOO_DOLL, -1,
717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
718 &li.bd_voodoo_vanish_by_explosion, TRUE
721 EL_BD_VOODOO_DOLL, -1,
722 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
723 &li.bd_voodoo_penalty_time, 30
728 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
729 &li.bd_slime_is_predictable, TRUE
733 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
734 &li.bd_slime_permeability_rate, 100
738 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
739 &li.bd_slime_permeability_bits_c64, 0
743 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
744 &li.bd_slime_random_seed_c64, -1
748 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
749 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
753 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
754 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
758 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
759 &li.bd_slime_eats_element_2, EL_BD_ROCK
763 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
764 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
768 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
769 &li.bd_slime_eats_element_3, EL_BD_NUT
773 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
774 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
779 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
780 &li.bd_acid_eats_element, EL_BD_SAND
784 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
785 &li.bd_acid_spread_rate, 3
789 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
790 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
795 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
796 &li.bd_biter_move_delay, 0
800 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
801 &li.bd_biter_eats_element, EL_BD_DIAMOND
806 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
807 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
811 EL_BD_EXPANDABLE_WALL_ANY, -1,
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.bd_change_expanding_wall, FALSE
816 EL_BD_EXPANDABLE_WALL_ANY, -1,
817 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
818 &li.bd_expanding_wall_looks_like, EL_BD_WALL
822 EL_BD_REPLICATOR, -1,
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
824 &li.bd_replicators_active, TRUE
827 EL_BD_REPLICATOR, -1,
828 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
829 &li.bd_replicator_create_delay, 4
833 EL_BD_CONVEYOR_LEFT, -1,
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
835 &li.bd_conveyor_belts_active, TRUE
838 EL_BD_CONVEYOR_LEFT, -1,
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
840 &li.bd_conveyor_belts_changed, FALSE
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
846 &li.bd_water_cannot_flow_down, FALSE
851 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
852 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
856 EL_BD_PNEUMATIC_HAMMER, -1,
857 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
858 &li.bd_hammer_walls_break_delay, 5
861 EL_BD_PNEUMATIC_HAMMER, -1,
862 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
863 &li.bd_hammer_walls_reappear, FALSE
866 EL_BD_PNEUMATIC_HAMMER, -1,
867 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
868 &li.bd_hammer_walls_reappear_delay, 100
873 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
874 &li.bd_num_skeletons_needed_for_pot, 5
878 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
879 &li.bd_skeleton_worth_num_diamonds, 0
883 EL_BD_CREATURE_SWITCH, -1,
884 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
885 &li.bd_creatures_start_backwards, FALSE
888 EL_BD_CREATURE_SWITCH, -1,
889 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
890 &li.bd_creatures_turn_on_hatching, FALSE
893 EL_BD_CREATURE_SWITCH, -1,
894 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
895 &li.bd_creatures_auto_turn_delay, 0
900 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
901 &li.bd_sand_looks_like, EL_BD_SAND
904 // (the following values are related to various game elements)
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
909 &li.score[SC_EMERALD], 10
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 &li.score[SC_DIAMOND], 10
920 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
921 &li.score[SC_BUG], 10
926 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
927 &li.score[SC_SPACESHIP], 10
932 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
933 &li.score[SC_PACMAN], 10
938 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
939 &li.score[SC_NUT], 10
944 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
945 &li.score[SC_DYNAMITE], 10
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.score[SC_KEY], 10
956 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
957 &li.score[SC_PEARL], 10
962 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
963 &li.score[SC_CRYSTAL], 10
966 // (amoeba values used by R'n'D game engine only)
969 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
970 &li.amoeba_content, EL_DIAMOND
974 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
979 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
980 &li.grow_into_diggable, TRUE
982 // (amoeba values used by BD game engine only)
985 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
986 &li.bd_amoeba_wait_for_hatching, FALSE
990 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
991 &li.bd_amoeba_start_immediately, TRUE
995 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
996 &li.bd_amoeba_2_explode_by_amoeba, TRUE
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1001 &li.bd_amoeba_threshold_too_big, 200
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1006 &li.bd_amoeba_slow_growth_time, 200
1010 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1011 &li.bd_amoeba_slow_growth_rate, 3
1015 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1016 &li.bd_amoeba_fast_growth_rate, 25
1020 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1021 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1025 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1026 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1032 &li.bd_amoeba_2_threshold_too_big, 200
1036 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1037 &li.bd_amoeba_2_slow_growth_time, 200
1041 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1042 &li.bd_amoeba_2_slow_growth_rate, 3
1046 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1047 &li.bd_amoeba_2_fast_growth_rate, 25
1051 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1052 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1056 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1057 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1061 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1062 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1066 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1067 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1072 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1073 &li.yamyam_content, EL_ROCK, NULL,
1074 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1078 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1079 &li.score[SC_YAMYAM], 10
1084 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1085 &li.score[SC_ROBOT], 10
1089 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1095 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1101 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1102 &li.time_magic_wall, 10
1106 EL_GAME_OF_LIFE, -1,
1107 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1108 &li.game_of_life[0], 2
1111 EL_GAME_OF_LIFE, -1,
1112 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1113 &li.game_of_life[1], 3
1116 EL_GAME_OF_LIFE, -1,
1117 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1118 &li.game_of_life[2], 3
1121 EL_GAME_OF_LIFE, -1,
1122 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1123 &li.game_of_life[3], 3
1126 EL_GAME_OF_LIFE, -1,
1127 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1128 &li.use_life_bugs, FALSE
1133 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1143 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1148 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1153 EL_TIMEGATE_SWITCH, -1,
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1155 &li.time_timegate, 10
1159 EL_LIGHT_SWITCH_ACTIVE, -1,
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1165 EL_SHIELD_NORMAL, -1,
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1167 &li.shield_normal_time, 10
1170 EL_SHIELD_NORMAL, -1,
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1172 &li.score[SC_SHIELD], 10
1176 EL_SHIELD_DEADLY, -1,
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1178 &li.shield_deadly_time, 10
1181 EL_SHIELD_DEADLY, -1,
1182 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1183 &li.score[SC_SHIELD], 10
1188 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1193 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1194 &li.extra_time_score, 10
1198 EL_TIME_ORB_FULL, -1,
1199 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1200 &li.time_orb_time, 10
1203 EL_TIME_ORB_FULL, -1,
1204 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1205 &li.use_time_orb_bug, FALSE
1210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1211 &li.use_spring_bug, FALSE
1216 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1217 &li.android_move_time, 10
1221 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1222 &li.android_clone_time, 10
1225 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1226 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1227 &li.android_clone_element[0], EL_EMPTY, NULL,
1228 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1232 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1233 &li.android_clone_element[0], EL_EMPTY, NULL,
1234 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1240 &li.lenses_score, 10
1244 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1249 EL_EMC_MAGNIFIER, -1,
1250 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1251 &li.magnify_score, 10
1254 EL_EMC_MAGNIFIER, -1,
1255 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1256 &li.magnify_time, 10
1260 EL_EMC_MAGIC_BALL, -1,
1261 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1265 EL_EMC_MAGIC_BALL, -1,
1266 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1267 &li.ball_random, FALSE
1270 EL_EMC_MAGIC_BALL, -1,
1271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1272 &li.ball_active_initial, FALSE
1275 EL_EMC_MAGIC_BALL, -1,
1276 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1277 &li.ball_content, EL_EMPTY, NULL,
1278 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1282 EL_SOKOBAN_FIELD_EMPTY, -1,
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1284 &li.sb_fields_needed, TRUE
1288 EL_SOKOBAN_OBJECT, -1,
1289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1290 &li.sb_objects_needed, TRUE
1295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1296 &li.mm_laser_red, FALSE
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1301 &li.mm_laser_green, FALSE
1305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1306 &li.mm_laser_blue, TRUE
1311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1312 &li.df_laser_red, TRUE
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1317 &li.df_laser_green, TRUE
1321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1322 &li.df_laser_blue, FALSE
1326 EL_MM_FUSE_ACTIVE, -1,
1327 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1328 &li.mm_time_fuse, 25
1332 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1333 &li.mm_time_bomb, 75
1337 EL_MM_GRAY_BALL, -1,
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1339 &li.mm_time_ball, 75
1342 EL_MM_GRAY_BALL, -1,
1343 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1344 &li.mm_ball_choice_mode, ANIM_RANDOM
1347 EL_MM_GRAY_BALL, -1,
1348 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1349 &li.mm_ball_content, EL_EMPTY, NULL,
1350 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1353 EL_MM_GRAY_BALL, -1,
1354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1355 &li.rotate_mm_ball_content, TRUE
1358 EL_MM_GRAY_BALL, -1,
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1360 &li.explode_mm_ball, FALSE
1364 EL_MM_STEEL_BLOCK, -1,
1365 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1366 &li.mm_time_block, 75
1369 EL_MM_LIGHTBALL, -1,
1370 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1371 &li.score[SC_ELEM_BONUS], 10
1381 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1386 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1390 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1391 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1396 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1397 &xx_envelope.autowrap, FALSE
1401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1402 &xx_envelope.centered, FALSE
1407 TYPE_STRING, CONF_VALUE_BYTES(1),
1408 &xx_envelope.text, -1, NULL,
1409 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1410 &xx_default_string_empty[0]
1420 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1424 TYPE_STRING, CONF_VALUE_BYTES(1),
1425 &xx_ei.description[0], -1,
1426 &yy_ei.description[0],
1427 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1428 &xx_default_description[0]
1433 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1434 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1435 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1437 #if ENABLE_RESERVED_CODE
1438 // (reserved for later use)
1441 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1442 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1443 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1449 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1450 &xx_ei.use_gfx_element, FALSE,
1451 &yy_ei.use_gfx_element
1455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1456 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1457 &yy_ei.gfx_element_initial
1462 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1463 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1464 &yy_ei.access_direction
1469 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1470 &xx_ei.collect_score_initial, 10,
1471 &yy_ei.collect_score_initial
1475 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1476 &xx_ei.collect_count_initial, 1,
1477 &yy_ei.collect_count_initial
1482 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1483 &xx_ei.ce_value_fixed_initial, 0,
1484 &yy_ei.ce_value_fixed_initial
1488 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1489 &xx_ei.ce_value_random_initial, 0,
1490 &yy_ei.ce_value_random_initial
1494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1495 &xx_ei.use_last_ce_value, FALSE,
1496 &yy_ei.use_last_ce_value
1501 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1502 &xx_ei.push_delay_fixed, 8,
1503 &yy_ei.push_delay_fixed
1507 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1508 &xx_ei.push_delay_random, 8,
1509 &yy_ei.push_delay_random
1513 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1514 &xx_ei.drop_delay_fixed, 0,
1515 &yy_ei.drop_delay_fixed
1519 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1520 &xx_ei.drop_delay_random, 0,
1521 &yy_ei.drop_delay_random
1525 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1526 &xx_ei.move_delay_fixed, 0,
1527 &yy_ei.move_delay_fixed
1531 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1532 &xx_ei.move_delay_random, 0,
1533 &yy_ei.move_delay_random
1537 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1538 &xx_ei.step_delay_fixed, 0,
1539 &yy_ei.step_delay_fixed
1543 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1544 &xx_ei.step_delay_random, 0,
1545 &yy_ei.step_delay_random
1550 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1551 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1556 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1557 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1558 &yy_ei.move_direction_initial
1562 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1563 &xx_ei.move_stepsize, TILEX / 8,
1564 &yy_ei.move_stepsize
1569 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1570 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1571 &yy_ei.move_enter_element
1575 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1576 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1577 &yy_ei.move_leave_element
1581 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1582 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1583 &yy_ei.move_leave_type
1588 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1589 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1590 &yy_ei.slippery_type
1595 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1596 &xx_ei.explosion_type, EXPLODES_3X3,
1597 &yy_ei.explosion_type
1601 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1602 &xx_ei.explosion_delay, 16,
1603 &yy_ei.explosion_delay
1607 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1608 &xx_ei.ignition_delay, 8,
1609 &yy_ei.ignition_delay
1614 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1615 &xx_ei.content, EL_EMPTY_SPACE,
1617 &xx_num_contents, 1, 1
1620 // ---------- "num_change_pages" must be the last entry ---------------------
1623 -1, SAVE_CONF_ALWAYS,
1624 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1625 &xx_ei.num_change_pages, 1,
1626 &yy_ei.num_change_pages
1637 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1639 // ---------- "current_change_page" must be the first entry -----------------
1642 -1, SAVE_CONF_ALWAYS,
1643 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1644 &xx_current_change_page, -1
1647 // ---------- (the remaining entries can be in any order) -------------------
1651 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1652 &xx_change.can_change, FALSE
1657 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1658 &xx_event_bits[0], 0
1662 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1663 &xx_event_bits[1], 0
1668 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1669 &xx_change.trigger_player, CH_PLAYER_ANY
1673 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1674 &xx_change.trigger_side, CH_SIDE_ANY
1678 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1679 &xx_change.trigger_page, CH_PAGE_ANY
1684 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1685 &xx_change.target_element, EL_EMPTY_SPACE
1690 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1691 &xx_change.delay_fixed, 0
1695 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1696 &xx_change.delay_random, 0
1700 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1701 &xx_change.delay_frames, FRAMES_PER_SECOND
1706 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1707 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1712 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1713 &xx_change.explode, FALSE
1717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1718 &xx_change.use_target_content, FALSE
1722 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1723 &xx_change.only_if_complete, FALSE
1727 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1728 &xx_change.use_random_replace, FALSE
1732 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1733 &xx_change.random_percentage, 100
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1738 &xx_change.replace_when, CP_WHEN_EMPTY
1743 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1744 &xx_change.has_action, FALSE
1748 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1749 &xx_change.action_type, CA_NO_ACTION
1753 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1754 &xx_change.action_mode, CA_MODE_UNDEFINED
1758 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1759 &xx_change.action_arg, CA_ARG_UNDEFINED
1764 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1765 &xx_change.action_element, EL_EMPTY_SPACE
1770 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1771 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1772 &xx_num_contents, 1, 1
1782 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1786 TYPE_STRING, CONF_VALUE_BYTES(1),
1787 &xx_ei.description[0], -1, NULL,
1788 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1789 &xx_default_description[0]
1794 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1795 &xx_ei.use_gfx_element, FALSE
1799 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1800 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1805 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1806 &xx_group.choice_mode, ANIM_RANDOM
1811 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1812 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1813 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1823 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1827 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1828 &xx_ei.use_gfx_element, FALSE
1832 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1833 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1843 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1847 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1848 &li.block_snap_field, TRUE
1852 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1853 &li.continuous_snapping, TRUE
1857 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1858 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1862 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1863 &li.use_start_element[0], FALSE
1867 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1868 &li.start_element[0], EL_PLAYER_1
1872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1873 &li.use_artwork_element[0], FALSE
1877 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1878 &li.artwork_element[0], EL_PLAYER_1
1882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1883 &li.use_explosion_element[0], FALSE
1887 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1888 &li.explosion_element[0], EL_PLAYER_1
1903 filetype_id_list[] =
1905 { LEVEL_FILE_TYPE_RND, "RND" },
1906 { LEVEL_FILE_TYPE_BD, "BD" },
1907 { LEVEL_FILE_TYPE_EM, "EM" },
1908 { LEVEL_FILE_TYPE_SP, "SP" },
1909 { LEVEL_FILE_TYPE_DX, "DX" },
1910 { LEVEL_FILE_TYPE_SB, "SB" },
1911 { LEVEL_FILE_TYPE_DC, "DC" },
1912 { LEVEL_FILE_TYPE_MM, "MM" },
1913 { LEVEL_FILE_TYPE_MM, "DF" },
1918 // ============================================================================
1919 // level file functions
1920 // ============================================================================
1922 static boolean check_special_flags(char *flag)
1924 if (strEqual(options.special_flags, flag) ||
1925 strEqual(leveldir_current->special_flags, flag))
1931 static struct DateInfo getCurrentDate(void)
1933 time_t epoch_seconds = time(NULL);
1934 struct tm *now = localtime(&epoch_seconds);
1935 struct DateInfo date;
1937 date.year = now->tm_year + 1900;
1938 date.month = now->tm_mon + 1;
1939 date.day = now->tm_mday;
1941 date.src = DATE_SRC_CLOCK;
1946 static void resetEventFlags(struct ElementChangeInfo *change)
1950 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1951 change->has_event[i] = FALSE;
1954 static void resetEventBits(void)
1958 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1959 xx_event_bits[i] = 0;
1962 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1966 /* important: only change event flag if corresponding event bit is set
1967 (this is because all xx_event_bits[] values are loaded separately,
1968 and all xx_event_bits[] values are set back to zero before loading
1969 another value xx_event_bits[x] (each value representing 32 flags)) */
1971 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1972 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1973 change->has_event[i] = TRUE;
1976 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1980 /* in contrast to the above function setEventFlagsFromEventBits(), it
1981 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1982 depending on the corresponding change->has_event[i] values here, as
1983 all xx_event_bits[] values are reset in resetEventBits() before */
1985 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1986 if (change->has_event[i])
1987 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1990 static char *getDefaultElementDescription(struct ElementInfo *ei)
1992 static char description[MAX_ELEMENT_NAME_LEN + 1];
1993 char *default_description = (ei->custom_description != NULL ?
1994 ei->custom_description :
1995 ei->editor_description);
1998 // always start with reliable default values
1999 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2000 description[i] = '\0';
2002 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2003 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2005 return &description[0];
2008 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2010 char *default_description = getDefaultElementDescription(ei);
2013 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2014 ei->description[i] = default_description[i];
2017 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2021 for (i = 0; conf[i].data_type != -1; i++)
2023 int default_value = conf[i].default_value;
2024 int data_type = conf[i].data_type;
2025 int conf_type = conf[i].conf_type;
2026 int byte_mask = conf_type & CONF_MASK_BYTES;
2028 if (byte_mask == CONF_MASK_MULTI_BYTES)
2030 int default_num_entities = conf[i].default_num_entities;
2031 int max_num_entities = conf[i].max_num_entities;
2033 *(int *)(conf[i].num_entities) = default_num_entities;
2035 if (data_type == TYPE_STRING)
2037 char *default_string = conf[i].default_string;
2038 char *string = (char *)(conf[i].value);
2040 strncpy(string, default_string, max_num_entities);
2042 else if (data_type == TYPE_ELEMENT_LIST)
2044 int *element_array = (int *)(conf[i].value);
2047 for (j = 0; j < max_num_entities; j++)
2048 element_array[j] = default_value;
2050 else if (data_type == TYPE_CONTENT_LIST)
2052 struct Content *content = (struct Content *)(conf[i].value);
2055 for (c = 0; c < max_num_entities; c++)
2056 for (y = 0; y < 3; y++)
2057 for (x = 0; x < 3; x++)
2058 content[c].e[x][y] = default_value;
2061 else // constant size configuration data (1, 2 or 4 bytes)
2063 if (data_type == TYPE_BOOLEAN)
2064 *(boolean *)(conf[i].value) = default_value;
2066 *(int *) (conf[i].value) = default_value;
2071 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2075 for (i = 0; conf[i].data_type != -1; i++)
2077 int data_type = conf[i].data_type;
2078 int conf_type = conf[i].conf_type;
2079 int byte_mask = conf_type & CONF_MASK_BYTES;
2081 if (byte_mask == CONF_MASK_MULTI_BYTES)
2083 int max_num_entities = conf[i].max_num_entities;
2085 if (data_type == TYPE_STRING)
2087 char *string = (char *)(conf[i].value);
2088 char *string_copy = (char *)(conf[i].value_copy);
2090 strncpy(string_copy, string, max_num_entities);
2092 else if (data_type == TYPE_ELEMENT_LIST)
2094 int *element_array = (int *)(conf[i].value);
2095 int *element_array_copy = (int *)(conf[i].value_copy);
2098 for (j = 0; j < max_num_entities; j++)
2099 element_array_copy[j] = element_array[j];
2101 else if (data_type == TYPE_CONTENT_LIST)
2103 struct Content *content = (struct Content *)(conf[i].value);
2104 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2107 for (c = 0; c < max_num_entities; c++)
2108 for (y = 0; y < 3; y++)
2109 for (x = 0; x < 3; x++)
2110 content_copy[c].e[x][y] = content[c].e[x][y];
2113 else // constant size configuration data (1, 2 or 4 bytes)
2115 if (data_type == TYPE_BOOLEAN)
2116 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2118 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2123 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2127 xx_ei = *ei_from; // copy element data into temporary buffer
2128 yy_ei = *ei_to; // copy element data into temporary buffer
2130 copyConfigFromConfigList(chunk_config_CUSX_base);
2135 // ---------- reinitialize and copy change pages ----------
2137 ei_to->num_change_pages = ei_from->num_change_pages;
2138 ei_to->current_change_page = ei_from->current_change_page;
2140 setElementChangePages(ei_to, ei_to->num_change_pages);
2142 for (i = 0; i < ei_to->num_change_pages; i++)
2143 ei_to->change_page[i] = ei_from->change_page[i];
2145 // ---------- copy group element info ----------
2146 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2147 *ei_to->group = *ei_from->group;
2149 // mark this custom element as modified
2150 ei_to->modified_settings = TRUE;
2153 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2155 int change_page_size = sizeof(struct ElementChangeInfo);
2157 ei->num_change_pages = MAX(1, change_pages);
2160 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2162 if (ei->current_change_page >= ei->num_change_pages)
2163 ei->current_change_page = ei->num_change_pages - 1;
2165 ei->change = &ei->change_page[ei->current_change_page];
2168 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2170 xx_change = *change; // copy change data into temporary buffer
2172 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2174 *change = xx_change;
2176 resetEventFlags(change);
2178 change->direct_action = 0;
2179 change->other_action = 0;
2181 change->pre_change_function = NULL;
2182 change->change_function = NULL;
2183 change->post_change_function = NULL;
2186 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2190 li = *level; // copy level data into temporary buffer
2191 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2192 *level = li; // copy temporary buffer back to level data
2194 setLevelInfoToDefaults_BD();
2195 setLevelInfoToDefaults_EM();
2196 setLevelInfoToDefaults_SP();
2197 setLevelInfoToDefaults_MM();
2199 level->native_bd_level = &native_bd_level;
2200 level->native_em_level = &native_em_level;
2201 level->native_sp_level = &native_sp_level;
2202 level->native_mm_level = &native_mm_level;
2204 level->file_version = FILE_VERSION_ACTUAL;
2205 level->game_version = GAME_VERSION_ACTUAL;
2207 level->creation_date = getCurrentDate();
2209 level->encoding_16bit_field = TRUE;
2210 level->encoding_16bit_yamyam = TRUE;
2211 level->encoding_16bit_amoeba = TRUE;
2213 // clear level name and level author string buffers
2214 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2215 level->name[i] = '\0';
2216 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2217 level->author[i] = '\0';
2219 // set level name and level author to default values
2220 strcpy(level->name, NAMELESS_LEVEL_NAME);
2221 strcpy(level->author, ANONYMOUS_NAME);
2223 // set level playfield to playable default level with player and exit
2224 for (x = 0; x < MAX_LEV_FIELDX; x++)
2225 for (y = 0; y < MAX_LEV_FIELDY; y++)
2226 level->field[x][y] = EL_SAND;
2228 level->field[0][0] = EL_PLAYER_1;
2229 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2231 BorderElement = EL_STEELWALL;
2233 // detect custom elements when loading them
2234 level->file_has_custom_elements = FALSE;
2236 // set all bug compatibility flags to "false" => do not emulate this bug
2237 level->use_action_after_change_bug = FALSE;
2239 if (leveldir_current)
2241 // try to determine better author name than 'anonymous'
2242 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2244 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2245 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2249 switch (LEVELCLASS(leveldir_current))
2251 case LEVELCLASS_TUTORIAL:
2252 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2255 case LEVELCLASS_CONTRIB:
2256 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2257 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2260 case LEVELCLASS_PRIVATE:
2261 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2262 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2266 // keep default value
2273 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2275 static boolean clipboard_elements_initialized = FALSE;
2278 InitElementPropertiesStatic();
2280 li = *level; // copy level data into temporary buffer
2281 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2282 *level = li; // copy temporary buffer back to level data
2284 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2287 struct ElementInfo *ei = &element_info[element];
2289 if (element == EL_MM_GRAY_BALL)
2291 struct LevelInfo_MM *level_mm = level->native_mm_level;
2294 for (j = 0; j < level->num_mm_ball_contents; j++)
2295 level->mm_ball_content[j] =
2296 map_element_MM_to_RND(level_mm->ball_content[j]);
2299 // never initialize clipboard elements after the very first time
2300 // (to be able to use clipboard elements between several levels)
2301 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2304 if (IS_ENVELOPE(element))
2306 int envelope_nr = element - EL_ENVELOPE_1;
2308 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2310 level->envelope[envelope_nr] = xx_envelope;
2313 if (IS_CUSTOM_ELEMENT(element) ||
2314 IS_GROUP_ELEMENT(element) ||
2315 IS_INTERNAL_ELEMENT(element))
2317 xx_ei = *ei; // copy element data into temporary buffer
2319 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2324 setElementChangePages(ei, 1);
2325 setElementChangeInfoToDefaults(ei->change);
2327 if (IS_CUSTOM_ELEMENT(element) ||
2328 IS_GROUP_ELEMENT(element))
2330 setElementDescriptionToDefault(ei);
2332 ei->modified_settings = FALSE;
2335 if (IS_CUSTOM_ELEMENT(element) ||
2336 IS_INTERNAL_ELEMENT(element))
2338 // internal values used in level editor
2340 ei->access_type = 0;
2341 ei->access_layer = 0;
2342 ei->access_protected = 0;
2343 ei->walk_to_action = 0;
2344 ei->smash_targets = 0;
2347 ei->can_explode_by_fire = FALSE;
2348 ei->can_explode_smashed = FALSE;
2349 ei->can_explode_impact = FALSE;
2351 ei->current_change_page = 0;
2354 if (IS_GROUP_ELEMENT(element) ||
2355 IS_INTERNAL_ELEMENT(element))
2357 struct ElementGroupInfo *group;
2359 // initialize memory for list of elements in group
2360 if (ei->group == NULL)
2361 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2365 xx_group = *group; // copy group data into temporary buffer
2367 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2372 if (IS_EMPTY_ELEMENT(element) ||
2373 IS_INTERNAL_ELEMENT(element))
2375 xx_ei = *ei; // copy element data into temporary buffer
2377 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2383 clipboard_elements_initialized = TRUE;
2386 static void setLevelInfoToDefaults(struct LevelInfo *level,
2387 boolean level_info_only,
2388 boolean reset_file_status)
2390 setLevelInfoToDefaults_Level(level);
2392 if (!level_info_only)
2393 setLevelInfoToDefaults_Elements(level);
2395 if (reset_file_status)
2397 level->no_valid_file = FALSE;
2398 level->no_level_file = FALSE;
2401 level->changed = FALSE;
2404 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2406 level_file_info->nr = 0;
2407 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2408 level_file_info->packed = FALSE;
2410 setString(&level_file_info->basename, NULL);
2411 setString(&level_file_info->filename, NULL);
2414 int getMappedElement_SB(int, boolean);
2416 static void ActivateLevelTemplate(void)
2420 if (check_special_flags("load_xsb_to_ces"))
2422 // fill smaller playfields with padding "beyond border wall" elements
2423 if (level.fieldx < level_template.fieldx ||
2424 level.fieldy < level_template.fieldy)
2426 short field[level.fieldx][level.fieldy];
2427 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2428 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2429 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2430 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2432 // copy old playfield (which is smaller than the visible area)
2433 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2434 field[x][y] = level.field[x][y];
2436 // fill new, larger playfield with "beyond border wall" elements
2437 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2438 level.field[x][y] = getMappedElement_SB('_', TRUE);
2440 // copy the old playfield to the middle of the new playfield
2441 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2442 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2444 level.fieldx = new_fieldx;
2445 level.fieldy = new_fieldy;
2449 // Currently there is no special action needed to activate the template
2450 // data, because 'element_info' property settings overwrite the original
2451 // level data, while all other variables do not change.
2453 // Exception: 'from_level_template' elements in the original level playfield
2454 // are overwritten with the corresponding elements at the same position in
2455 // playfield from the level template.
2457 for (x = 0; x < level.fieldx; x++)
2458 for (y = 0; y < level.fieldy; y++)
2459 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2460 level.field[x][y] = level_template.field[x][y];
2462 if (check_special_flags("load_xsb_to_ces"))
2464 struct LevelInfo level_backup = level;
2466 // overwrite all individual level settings from template level settings
2467 level = level_template;
2469 // restore level file info
2470 level.file_info = level_backup.file_info;
2472 // restore playfield size
2473 level.fieldx = level_backup.fieldx;
2474 level.fieldy = level_backup.fieldy;
2476 // restore playfield content
2477 for (x = 0; x < level.fieldx; x++)
2478 for (y = 0; y < level.fieldy; y++)
2479 level.field[x][y] = level_backup.field[x][y];
2481 // restore name and author from individual level
2482 strcpy(level.name, level_backup.name);
2483 strcpy(level.author, level_backup.author);
2485 // restore flag "use_custom_template"
2486 level.use_custom_template = level_backup.use_custom_template;
2490 static boolean checkForPackageFromBasename_BD(char *basename)
2492 // check for native BD level file extensions
2493 if (!strSuffixLower(basename, ".bd") &&
2494 !strSuffixLower(basename, ".bdr") &&
2495 !strSuffixLower(basename, ".brc") &&
2496 !strSuffixLower(basename, ".gds"))
2499 // check for standard single-level BD files (like "001.bd")
2500 if (strSuffixLower(basename, ".bd") &&
2501 strlen(basename) == 6 &&
2502 basename[0] >= '0' && basename[0] <= '9' &&
2503 basename[1] >= '0' && basename[1] <= '9' &&
2504 basename[2] >= '0' && basename[2] <= '9')
2507 // this is a level package in native BD file format
2511 static char *getLevelFilenameFromBasename(char *basename)
2513 static char *filename = NULL;
2515 checked_free(filename);
2517 filename = getPath2(getCurrentLevelDir(), basename);
2522 static int getFileTypeFromBasename(char *basename)
2524 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2526 static char *filename = NULL;
2527 struct stat file_status;
2529 // ---------- try to determine file type from filename ----------
2531 // check for typical filename of a Supaplex level package file
2532 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2533 return LEVEL_FILE_TYPE_SP;
2535 // check for typical filename of a Diamond Caves II level package file
2536 if (strSuffixLower(basename, ".dc") ||
2537 strSuffixLower(basename, ".dc2"))
2538 return LEVEL_FILE_TYPE_DC;
2540 // check for typical filename of a Sokoban level package file
2541 if (strSuffixLower(basename, ".xsb") &&
2542 strchr(basename, '%') == NULL)
2543 return LEVEL_FILE_TYPE_SB;
2545 // check for typical filename of a Boulder Dash (GDash) level package file
2546 if (checkForPackageFromBasename_BD(basename))
2547 return LEVEL_FILE_TYPE_BD;
2549 // ---------- try to determine file type from filesize ----------
2551 checked_free(filename);
2552 filename = getPath2(getCurrentLevelDir(), basename);
2554 if (stat(filename, &file_status) == 0)
2556 // check for typical filesize of a Supaplex level package file
2557 if (file_status.st_size == 170496)
2558 return LEVEL_FILE_TYPE_SP;
2561 return LEVEL_FILE_TYPE_UNKNOWN;
2564 static int getFileTypeFromMagicBytes(char *filename, int type)
2568 if ((file = openFile(filename, MODE_READ)))
2570 char chunk_name[CHUNK_ID_LEN + 1];
2572 getFileChunkBE(file, chunk_name, NULL);
2574 if (strEqual(chunk_name, "MMII") ||
2575 strEqual(chunk_name, "MIRR"))
2576 type = LEVEL_FILE_TYPE_MM;
2584 static boolean checkForPackageFromBasename(char *basename)
2586 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2587 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2589 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2592 static char *getSingleLevelBasenameExt(int nr, char *extension)
2594 static char basename[MAX_FILENAME_LEN];
2597 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2599 sprintf(basename, "%03d.%s", nr, extension);
2604 static char *getSingleLevelBasename(int nr)
2606 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2609 static char *getPackedLevelBasename(int type)
2611 static char basename[MAX_FILENAME_LEN];
2612 char *directory = getCurrentLevelDir();
2614 DirectoryEntry *dir_entry;
2616 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2618 if ((dir = openDirectory(directory)) == NULL)
2620 Warn("cannot read current level directory '%s'", directory);
2625 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2627 char *entry_basename = dir_entry->basename;
2628 int entry_type = getFileTypeFromBasename(entry_basename);
2630 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2632 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2635 strcpy(basename, entry_basename);
2642 closeDirectory(dir);
2647 static char *getSingleLevelFilename(int nr)
2649 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2652 #if ENABLE_UNUSED_CODE
2653 static char *getPackedLevelFilename(int type)
2655 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2659 char *getDefaultLevelFilename(int nr)
2661 return getSingleLevelFilename(nr);
2664 #if ENABLE_UNUSED_CODE
2665 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2669 lfi->packed = FALSE;
2671 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2672 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2676 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2677 int type, char *format, ...)
2679 static char basename[MAX_FILENAME_LEN];
2682 va_start(ap, format);
2683 vsprintf(basename, format, ap);
2687 lfi->packed = FALSE;
2689 setString(&lfi->basename, basename);
2690 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2693 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2699 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2700 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2703 static int getFiletypeFromID(char *filetype_id)
2705 char *filetype_id_lower;
2706 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2709 if (filetype_id == NULL)
2710 return LEVEL_FILE_TYPE_UNKNOWN;
2712 filetype_id_lower = getStringToLower(filetype_id);
2714 for (i = 0; filetype_id_list[i].id != NULL; i++)
2716 char *id_lower = getStringToLower(filetype_id_list[i].id);
2718 if (strEqual(filetype_id_lower, id_lower))
2719 filetype = filetype_id_list[i].filetype;
2723 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2727 free(filetype_id_lower);
2732 char *getLocalLevelTemplateFilename(void)
2734 return getDefaultLevelFilename(-1);
2737 char *getGlobalLevelTemplateFilename(void)
2739 // global variable "leveldir_current" must be modified in the loop below
2740 LevelDirTree *leveldir_current_last = leveldir_current;
2741 char *filename = NULL;
2743 // check for template level in path from current to topmost tree node
2745 while (leveldir_current != NULL)
2747 filename = getDefaultLevelFilename(-1);
2749 if (fileExists(filename))
2752 leveldir_current = leveldir_current->node_parent;
2755 // restore global variable "leveldir_current" modified in above loop
2756 leveldir_current = leveldir_current_last;
2761 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2765 // special case: level number is negative => check for level template file
2768 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2769 getSingleLevelBasename(-1));
2771 // replace local level template filename with global template filename
2772 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2774 // no fallback if template file not existing
2778 // special case: check for file name/pattern specified in "levelinfo.conf"
2779 if (leveldir_current->level_filename != NULL)
2781 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2783 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2784 leveldir_current->level_filename, nr);
2786 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2788 if (fileExists(lfi->filename))
2791 else if (leveldir_current->level_filetype != NULL)
2793 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2795 // check for specified native level file with standard file name
2796 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2797 "%03d.%s", nr, LEVELFILE_EXTENSION);
2798 if (fileExists(lfi->filename))
2802 // check for native Rocks'n'Diamonds level file
2803 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2804 "%03d.%s", nr, LEVELFILE_EXTENSION);
2805 if (fileExists(lfi->filename))
2808 // check for native Boulder Dash level file
2809 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2810 if (fileExists(lfi->filename))
2813 // check for Emerald Mine level file (V1)
2814 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2815 'a' + (nr / 10) % 26, '0' + nr % 10);
2816 if (fileExists(lfi->filename))
2818 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2819 'A' + (nr / 10) % 26, '0' + nr % 10);
2820 if (fileExists(lfi->filename))
2823 // check for Emerald Mine level file (V2 to V5)
2824 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2825 if (fileExists(lfi->filename))
2828 // check for Emerald Mine level file (V6 / single mode)
2829 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2830 if (fileExists(lfi->filename))
2832 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2833 if (fileExists(lfi->filename))
2836 // check for Emerald Mine level file (V6 / teamwork mode)
2837 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2838 if (fileExists(lfi->filename))
2840 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2841 if (fileExists(lfi->filename))
2844 // check for various packed level file formats
2845 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2846 if (fileExists(lfi->filename))
2849 // no known level file found -- use default values (and fail later)
2850 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2851 "%03d.%s", nr, LEVELFILE_EXTENSION);
2854 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2856 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2857 lfi->type = getFileTypeFromBasename(lfi->basename);
2859 if (lfi->type == LEVEL_FILE_TYPE_RND)
2860 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2863 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2865 // always start with reliable default values
2866 setFileInfoToDefaults(level_file_info);
2868 level_file_info->nr = nr; // set requested level number
2870 determineLevelFileInfo_Filename(level_file_info);
2871 determineLevelFileInfo_Filetype(level_file_info);
2874 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2875 struct LevelFileInfo *lfi_to)
2877 lfi_to->nr = lfi_from->nr;
2878 lfi_to->type = lfi_from->type;
2879 lfi_to->packed = lfi_from->packed;
2881 setString(&lfi_to->basename, lfi_from->basename);
2882 setString(&lfi_to->filename, lfi_from->filename);
2885 // ----------------------------------------------------------------------------
2886 // functions for loading R'n'D level
2887 // ----------------------------------------------------------------------------
2889 int getMappedElement(int element)
2891 // remap some (historic, now obsolete) elements
2895 case EL_PLAYER_OBSOLETE:
2896 element = EL_PLAYER_1;
2899 case EL_KEY_OBSOLETE:
2903 case EL_EM_KEY_1_FILE_OBSOLETE:
2904 element = EL_EM_KEY_1;
2907 case EL_EM_KEY_2_FILE_OBSOLETE:
2908 element = EL_EM_KEY_2;
2911 case EL_EM_KEY_3_FILE_OBSOLETE:
2912 element = EL_EM_KEY_3;
2915 case EL_EM_KEY_4_FILE_OBSOLETE:
2916 element = EL_EM_KEY_4;
2919 case EL_ENVELOPE_OBSOLETE:
2920 element = EL_ENVELOPE_1;
2928 if (element >= NUM_FILE_ELEMENTS)
2930 Warn("invalid level element %d", element);
2932 element = EL_UNKNOWN;
2940 static int getMappedElementByVersion(int element, int game_version)
2942 // remap some elements due to certain game version
2944 if (game_version <= VERSION_IDENT(2,2,0,0))
2946 // map game font elements
2947 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2948 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2949 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2950 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2953 if (game_version < VERSION_IDENT(3,0,0,0))
2955 // map Supaplex gravity tube elements
2956 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2957 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2958 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2959 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2966 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2968 level->file_version = getFileVersion(file);
2969 level->game_version = getFileVersion(file);
2974 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2976 level->creation_date.year = getFile16BitBE(file);
2977 level->creation_date.month = getFile8Bit(file);
2978 level->creation_date.day = getFile8Bit(file);
2980 level->creation_date.src = DATE_SRC_LEVELFILE;
2985 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2987 int initial_player_stepsize;
2988 int initial_player_gravity;
2991 level->fieldx = getFile8Bit(file);
2992 level->fieldy = getFile8Bit(file);
2994 level->time = getFile16BitBE(file);
2995 level->gems_needed = getFile16BitBE(file);
2997 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2998 level->name[i] = getFile8Bit(file);
2999 level->name[MAX_LEVEL_NAME_LEN] = 0;
3001 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3002 level->score[i] = getFile8Bit(file);
3004 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3005 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3006 for (y = 0; y < 3; y++)
3007 for (x = 0; x < 3; x++)
3008 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3010 level->amoeba_speed = getFile8Bit(file);
3011 level->time_magic_wall = getFile8Bit(file);
3012 level->time_wheel = getFile8Bit(file);
3013 level->amoeba_content = getMappedElement(getFile8Bit(file));
3015 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3018 for (i = 0; i < MAX_PLAYERS; i++)
3019 level->initial_player_stepsize[i] = initial_player_stepsize;
3021 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3023 for (i = 0; i < MAX_PLAYERS; i++)
3024 level->initial_player_gravity[i] = initial_player_gravity;
3026 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3027 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3029 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3031 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3032 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3033 level->can_move_into_acid_bits = getFile32BitBE(file);
3034 level->dont_collide_with_bits = getFile8Bit(file);
3036 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3037 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3039 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3040 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3041 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3043 level->game_engine_type = getFile8Bit(file);
3045 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3050 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3054 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3055 level->name[i] = getFile8Bit(file);
3056 level->name[MAX_LEVEL_NAME_LEN] = 0;
3061 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3065 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3066 level->author[i] = getFile8Bit(file);
3067 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3072 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3075 int chunk_size_expected = level->fieldx * level->fieldy;
3077 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3078 stored with 16-bit encoding (and should be twice as big then).
3079 Even worse, playfield data was stored 16-bit when only yamyam content
3080 contained 16-bit elements and vice versa. */
3082 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3083 chunk_size_expected *= 2;
3085 if (chunk_size_expected != chunk_size)
3087 ReadUnusedBytesFromFile(file, chunk_size);
3088 return chunk_size_expected;
3091 for (y = 0; y < level->fieldy; y++)
3092 for (x = 0; x < level->fieldx; x++)
3093 level->field[x][y] =
3094 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3099 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3102 int header_size = 4;
3103 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3104 int chunk_size_expected = header_size + content_size;
3106 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3107 stored with 16-bit encoding (and should be twice as big then).
3108 Even worse, playfield data was stored 16-bit when only yamyam content
3109 contained 16-bit elements and vice versa. */
3111 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3112 chunk_size_expected += content_size;
3114 if (chunk_size_expected != chunk_size)
3116 ReadUnusedBytesFromFile(file, chunk_size);
3117 return chunk_size_expected;
3121 level->num_yamyam_contents = getFile8Bit(file);
3125 // correct invalid number of content fields -- should never happen
3126 if (level->num_yamyam_contents < 1 ||
3127 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3128 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3130 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3131 for (y = 0; y < 3; y++)
3132 for (x = 0; x < 3; x++)
3133 level->yamyam_content[i].e[x][y] =
3134 getMappedElement(level->encoding_16bit_field ?
3135 getFile16BitBE(file) : getFile8Bit(file));
3139 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3144 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3146 element = getMappedElement(getFile16BitBE(file));
3147 num_contents = getFile8Bit(file);
3149 getFile8Bit(file); // content x size (unused)
3150 getFile8Bit(file); // content y size (unused)
3152 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3154 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3155 for (y = 0; y < 3; y++)
3156 for (x = 0; x < 3; x++)
3157 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3159 // correct invalid number of content fields -- should never happen
3160 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3161 num_contents = STD_ELEMENT_CONTENTS;
3163 if (element == EL_YAMYAM)
3165 level->num_yamyam_contents = num_contents;
3167 for (i = 0; i < num_contents; i++)
3168 for (y = 0; y < 3; y++)
3169 for (x = 0; x < 3; x++)
3170 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3172 else if (element == EL_BD_AMOEBA)
3174 level->amoeba_content = content_array[0][0][0];
3178 Warn("cannot load content for element '%d'", element);
3184 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3190 int chunk_size_expected;
3192 element = getMappedElement(getFile16BitBE(file));
3193 if (!IS_ENVELOPE(element))
3194 element = EL_ENVELOPE_1;
3196 envelope_nr = element - EL_ENVELOPE_1;
3198 envelope_len = getFile16BitBE(file);
3200 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3201 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3203 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3205 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3206 if (chunk_size_expected != chunk_size)
3208 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3209 return chunk_size_expected;
3212 for (i = 0; i < envelope_len; i++)
3213 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3218 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3220 int num_changed_custom_elements = getFile16BitBE(file);
3221 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3224 if (chunk_size_expected != chunk_size)
3226 ReadUnusedBytesFromFile(file, chunk_size - 2);
3227 return chunk_size_expected;
3230 for (i = 0; i < num_changed_custom_elements; i++)
3232 int element = getMappedElement(getFile16BitBE(file));
3233 int properties = getFile32BitBE(file);
3235 if (IS_CUSTOM_ELEMENT(element))
3236 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3238 Warn("invalid custom element number %d", element);
3240 // older game versions that wrote level files with CUS1 chunks used
3241 // different default push delay values (not yet stored in level file)
3242 element_info[element].push_delay_fixed = 2;
3243 element_info[element].push_delay_random = 8;
3246 level->file_has_custom_elements = TRUE;
3251 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3253 int num_changed_custom_elements = getFile16BitBE(file);
3254 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3257 if (chunk_size_expected != chunk_size)
3259 ReadUnusedBytesFromFile(file, chunk_size - 2);
3260 return chunk_size_expected;
3263 for (i = 0; i < num_changed_custom_elements; i++)
3265 int element = getMappedElement(getFile16BitBE(file));
3266 int custom_target_element = getMappedElement(getFile16BitBE(file));
3268 if (IS_CUSTOM_ELEMENT(element))
3269 element_info[element].change->target_element = custom_target_element;
3271 Warn("invalid custom element number %d", element);
3274 level->file_has_custom_elements = TRUE;
3279 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3281 int num_changed_custom_elements = getFile16BitBE(file);
3282 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3285 if (chunk_size_expected != chunk_size)
3287 ReadUnusedBytesFromFile(file, chunk_size - 2);
3288 return chunk_size_expected;
3291 for (i = 0; i < num_changed_custom_elements; i++)
3293 int element = getMappedElement(getFile16BitBE(file));
3294 struct ElementInfo *ei = &element_info[element];
3295 unsigned int event_bits;
3297 if (!IS_CUSTOM_ELEMENT(element))
3299 Warn("invalid custom element number %d", element);
3301 element = EL_INTERNAL_DUMMY;
3304 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3305 ei->description[j] = getFile8Bit(file);
3306 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3308 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3310 // some free bytes for future properties and padding
3311 ReadUnusedBytesFromFile(file, 7);
3313 ei->use_gfx_element = getFile8Bit(file);
3314 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3316 ei->collect_score_initial = getFile8Bit(file);
3317 ei->collect_count_initial = getFile8Bit(file);
3319 ei->push_delay_fixed = getFile16BitBE(file);
3320 ei->push_delay_random = getFile16BitBE(file);
3321 ei->move_delay_fixed = getFile16BitBE(file);
3322 ei->move_delay_random = getFile16BitBE(file);
3324 ei->move_pattern = getFile16BitBE(file);
3325 ei->move_direction_initial = getFile8Bit(file);
3326 ei->move_stepsize = getFile8Bit(file);
3328 for (y = 0; y < 3; y++)
3329 for (x = 0; x < 3; x++)
3330 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3332 // bits 0 - 31 of "has_event[]"
3333 event_bits = getFile32BitBE(file);
3334 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3335 if (event_bits & (1u << j))
3336 ei->change->has_event[j] = TRUE;
3338 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3340 ei->change->delay_fixed = getFile16BitBE(file);
3341 ei->change->delay_random = getFile16BitBE(file);
3342 ei->change->delay_frames = getFile16BitBE(file);
3344 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3346 ei->change->explode = getFile8Bit(file);
3347 ei->change->use_target_content = getFile8Bit(file);
3348 ei->change->only_if_complete = getFile8Bit(file);
3349 ei->change->use_random_replace = getFile8Bit(file);
3351 ei->change->random_percentage = getFile8Bit(file);
3352 ei->change->replace_when = getFile8Bit(file);
3354 for (y = 0; y < 3; y++)
3355 for (x = 0; x < 3; x++)
3356 ei->change->target_content.e[x][y] =
3357 getMappedElement(getFile16BitBE(file));
3359 ei->slippery_type = getFile8Bit(file);
3361 // some free bytes for future properties and padding
3362 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3364 // mark that this custom element has been modified
3365 ei->modified_settings = TRUE;
3368 level->file_has_custom_elements = TRUE;
3373 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3375 struct ElementInfo *ei;
3376 int chunk_size_expected;
3380 // ---------- custom element base property values (96 bytes) ----------------
3382 element = getMappedElement(getFile16BitBE(file));
3384 if (!IS_CUSTOM_ELEMENT(element))
3386 Warn("invalid custom element number %d", element);
3388 ReadUnusedBytesFromFile(file, chunk_size - 2);
3393 ei = &element_info[element];
3395 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3396 ei->description[i] = getFile8Bit(file);
3397 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3399 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3401 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3403 ei->num_change_pages = getFile8Bit(file);
3405 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3406 if (chunk_size_expected != chunk_size)
3408 ReadUnusedBytesFromFile(file, chunk_size - 43);
3409 return chunk_size_expected;
3412 ei->ce_value_fixed_initial = getFile16BitBE(file);
3413 ei->ce_value_random_initial = getFile16BitBE(file);
3414 ei->use_last_ce_value = getFile8Bit(file);
3416 ei->use_gfx_element = getFile8Bit(file);
3417 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3419 ei->collect_score_initial = getFile8Bit(file);
3420 ei->collect_count_initial = getFile8Bit(file);
3422 ei->drop_delay_fixed = getFile8Bit(file);
3423 ei->push_delay_fixed = getFile8Bit(file);
3424 ei->drop_delay_random = getFile8Bit(file);
3425 ei->push_delay_random = getFile8Bit(file);
3426 ei->move_delay_fixed = getFile16BitBE(file);
3427 ei->move_delay_random = getFile16BitBE(file);
3429 // bits 0 - 15 of "move_pattern" ...
3430 ei->move_pattern = getFile16BitBE(file);
3431 ei->move_direction_initial = getFile8Bit(file);
3432 ei->move_stepsize = getFile8Bit(file);
3434 ei->slippery_type = getFile8Bit(file);
3436 for (y = 0; y < 3; y++)
3437 for (x = 0; x < 3; x++)
3438 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3440 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3441 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3442 ei->move_leave_type = getFile8Bit(file);
3444 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3445 ei->move_pattern |= (getFile16BitBE(file) << 16);
3447 ei->access_direction = getFile8Bit(file);
3449 ei->explosion_delay = getFile8Bit(file);
3450 ei->ignition_delay = getFile8Bit(file);
3451 ei->explosion_type = getFile8Bit(file);
3453 // some free bytes for future custom property values and padding
3454 ReadUnusedBytesFromFile(file, 1);
3456 // ---------- change page property values (48 bytes) ------------------------
3458 setElementChangePages(ei, ei->num_change_pages);
3460 for (i = 0; i < ei->num_change_pages; i++)
3462 struct ElementChangeInfo *change = &ei->change_page[i];
3463 unsigned int event_bits;
3465 // always start with reliable default values
3466 setElementChangeInfoToDefaults(change);
3468 // bits 0 - 31 of "has_event[]" ...
3469 event_bits = getFile32BitBE(file);
3470 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3471 if (event_bits & (1u << j))
3472 change->has_event[j] = TRUE;
3474 change->target_element = getMappedElement(getFile16BitBE(file));
3476 change->delay_fixed = getFile16BitBE(file);
3477 change->delay_random = getFile16BitBE(file);
3478 change->delay_frames = getFile16BitBE(file);
3480 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3482 change->explode = getFile8Bit(file);
3483 change->use_target_content = getFile8Bit(file);
3484 change->only_if_complete = getFile8Bit(file);
3485 change->use_random_replace = getFile8Bit(file);
3487 change->random_percentage = getFile8Bit(file);
3488 change->replace_when = getFile8Bit(file);
3490 for (y = 0; y < 3; y++)
3491 for (x = 0; x < 3; x++)
3492 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3494 change->can_change = getFile8Bit(file);
3496 change->trigger_side = getFile8Bit(file);
3498 change->trigger_player = getFile8Bit(file);
3499 change->trigger_page = getFile8Bit(file);
3501 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3502 CH_PAGE_ANY : (1 << change->trigger_page));
3504 change->has_action = getFile8Bit(file);
3505 change->action_type = getFile8Bit(file);
3506 change->action_mode = getFile8Bit(file);
3507 change->action_arg = getFile16BitBE(file);
3509 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3510 event_bits = getFile8Bit(file);
3511 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3512 if (event_bits & (1u << (j - 32)))
3513 change->has_event[j] = TRUE;
3516 // mark this custom element as modified
3517 ei->modified_settings = TRUE;
3519 level->file_has_custom_elements = TRUE;
3524 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3526 struct ElementInfo *ei;
3527 struct ElementGroupInfo *group;
3531 element = getMappedElement(getFile16BitBE(file));
3533 if (!IS_GROUP_ELEMENT(element))
3535 Warn("invalid group element number %d", element);
3537 ReadUnusedBytesFromFile(file, chunk_size - 2);
3542 ei = &element_info[element];
3544 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3545 ei->description[i] = getFile8Bit(file);
3546 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3548 group = element_info[element].group;
3550 group->num_elements = getFile8Bit(file);
3552 ei->use_gfx_element = getFile8Bit(file);
3553 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3555 group->choice_mode = getFile8Bit(file);
3557 // some free bytes for future values and padding
3558 ReadUnusedBytesFromFile(file, 3);
3560 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3561 group->element[i] = getMappedElement(getFile16BitBE(file));
3563 // mark this group element as modified
3564 element_info[element].modified_settings = TRUE;
3566 level->file_has_custom_elements = TRUE;
3571 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3572 int element, int real_element)
3574 int micro_chunk_size = 0;
3575 int conf_type = getFile8Bit(file);
3576 int byte_mask = conf_type & CONF_MASK_BYTES;
3577 boolean element_found = FALSE;
3580 micro_chunk_size += 1;
3582 if (byte_mask == CONF_MASK_MULTI_BYTES)
3584 int num_bytes = getFile16BitBE(file);
3585 byte *buffer = checked_malloc(num_bytes);
3587 ReadBytesFromFile(file, buffer, num_bytes);
3589 for (i = 0; conf[i].data_type != -1; i++)
3591 if (conf[i].element == element &&
3592 conf[i].conf_type == conf_type)
3594 int data_type = conf[i].data_type;
3595 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3596 int max_num_entities = conf[i].max_num_entities;
3598 if (num_entities > max_num_entities)
3600 Warn("truncating number of entities for element %d from %d to %d",
3601 element, num_entities, max_num_entities);
3603 num_entities = max_num_entities;
3606 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3607 data_type == TYPE_CONTENT_LIST))
3609 // for element and content lists, zero entities are not allowed
3610 Warn("found empty list of entities for element %d", element);
3612 // do not set "num_entities" here to prevent reading behind buffer
3614 *(int *)(conf[i].num_entities) = 1; // at least one is required
3618 *(int *)(conf[i].num_entities) = num_entities;
3621 element_found = TRUE;
3623 if (data_type == TYPE_STRING)
3625 char *string = (char *)(conf[i].value);
3628 for (j = 0; j < max_num_entities; j++)
3629 string[j] = (j < num_entities ? buffer[j] : '\0');
3631 else if (data_type == TYPE_ELEMENT_LIST)
3633 int *element_array = (int *)(conf[i].value);
3636 for (j = 0; j < num_entities; j++)
3638 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3640 else if (data_type == TYPE_CONTENT_LIST)
3642 struct Content *content= (struct Content *)(conf[i].value);
3645 for (c = 0; c < num_entities; c++)
3646 for (y = 0; y < 3; y++)
3647 for (x = 0; x < 3; x++)
3648 content[c].e[x][y] =
3649 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3652 element_found = FALSE;
3658 checked_free(buffer);
3660 micro_chunk_size += 2 + num_bytes;
3662 else // constant size configuration data (1, 2 or 4 bytes)
3664 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3665 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3666 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3668 for (i = 0; conf[i].data_type != -1; i++)
3670 if (conf[i].element == element &&
3671 conf[i].conf_type == conf_type)
3673 int data_type = conf[i].data_type;
3675 if (data_type == TYPE_ELEMENT)
3676 value = getMappedElement(value);
3678 if (data_type == TYPE_BOOLEAN)
3679 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3681 *(int *) (conf[i].value) = value;
3683 element_found = TRUE;
3689 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3694 char *error_conf_chunk_bytes =
3695 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3696 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3697 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3698 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3699 int error_element = real_element;
3701 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3702 error_conf_chunk_bytes, error_conf_chunk_token,
3703 error_element, EL_NAME(error_element));
3706 return micro_chunk_size;
3709 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3711 int real_chunk_size = 0;
3713 li = *level; // copy level data into temporary buffer
3715 while (!checkEndOfFile(file))
3717 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3719 if (real_chunk_size >= chunk_size)
3723 *level = li; // copy temporary buffer back to level data
3725 return real_chunk_size;
3728 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3730 int real_chunk_size = 0;
3732 li = *level; // copy level data into temporary buffer
3734 while (!checkEndOfFile(file))
3736 int element = getMappedElement(getFile16BitBE(file));
3738 real_chunk_size += 2;
3739 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3741 if (real_chunk_size >= chunk_size)
3745 *level = li; // copy temporary buffer back to level data
3747 return real_chunk_size;
3750 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3752 int real_chunk_size = 0;
3754 li = *level; // copy level data into temporary buffer
3756 while (!checkEndOfFile(file))
3758 int element = getMappedElement(getFile16BitBE(file));
3760 real_chunk_size += 2;
3761 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3763 if (real_chunk_size >= chunk_size)
3767 *level = li; // copy temporary buffer back to level data
3769 return real_chunk_size;
3772 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3774 int element = getMappedElement(getFile16BitBE(file));
3775 int envelope_nr = element - EL_ENVELOPE_1;
3776 int real_chunk_size = 2;
3778 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3780 while (!checkEndOfFile(file))
3782 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3785 if (real_chunk_size >= chunk_size)
3789 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3791 return real_chunk_size;
3794 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3796 int element = getMappedElement(getFile16BitBE(file));
3797 int real_chunk_size = 2;
3798 struct ElementInfo *ei = &element_info[element];
3801 xx_ei = *ei; // copy element data into temporary buffer
3803 xx_ei.num_change_pages = -1;
3805 while (!checkEndOfFile(file))
3807 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3809 if (xx_ei.num_change_pages != -1)
3812 if (real_chunk_size >= chunk_size)
3818 if (ei->num_change_pages == -1)
3820 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3823 ei->num_change_pages = 1;
3825 setElementChangePages(ei, 1);
3826 setElementChangeInfoToDefaults(ei->change);
3828 return real_chunk_size;
3831 // initialize number of change pages stored for this custom element
3832 setElementChangePages(ei, ei->num_change_pages);
3833 for (i = 0; i < ei->num_change_pages; i++)
3834 setElementChangeInfoToDefaults(&ei->change_page[i]);
3836 // start with reading properties for the first change page
3837 xx_current_change_page = 0;
3839 while (!checkEndOfFile(file))
3841 // level file might contain invalid change page number
3842 if (xx_current_change_page >= ei->num_change_pages)
3845 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3847 xx_change = *change; // copy change data into temporary buffer
3849 resetEventBits(); // reset bits; change page might have changed
3851 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3854 *change = xx_change;
3856 setEventFlagsFromEventBits(change);
3858 if (real_chunk_size >= chunk_size)
3862 level->file_has_custom_elements = TRUE;
3864 return real_chunk_size;
3867 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3869 int element = getMappedElement(getFile16BitBE(file));
3870 int real_chunk_size = 2;
3871 struct ElementInfo *ei = &element_info[element];
3872 struct ElementGroupInfo *group = ei->group;
3877 xx_ei = *ei; // copy element data into temporary buffer
3878 xx_group = *group; // copy group data into temporary buffer
3880 while (!checkEndOfFile(file))
3882 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3885 if (real_chunk_size >= chunk_size)
3892 level->file_has_custom_elements = TRUE;
3894 return real_chunk_size;
3897 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3899 int element = getMappedElement(getFile16BitBE(file));
3900 int real_chunk_size = 2;
3901 struct ElementInfo *ei = &element_info[element];
3903 xx_ei = *ei; // copy element data into temporary buffer
3905 while (!checkEndOfFile(file))
3907 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3910 if (real_chunk_size >= chunk_size)
3916 level->file_has_custom_elements = TRUE;
3918 return real_chunk_size;
3921 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3922 struct LevelFileInfo *level_file_info,
3923 boolean level_info_only)
3925 char *filename = level_file_info->filename;
3926 char cookie[MAX_LINE_LEN];
3927 char chunk_name[CHUNK_ID_LEN + 1];
3931 if (!(file = openFile(filename, MODE_READ)))
3933 level->no_valid_file = TRUE;
3934 level->no_level_file = TRUE;
3936 if (level_info_only)
3939 Warn("cannot read level '%s' -- using empty level", filename);
3941 if (!setup.editor.use_template_for_new_levels)
3944 // if level file not found, try to initialize level data from template
3945 filename = getGlobalLevelTemplateFilename();
3947 if (!(file = openFile(filename, MODE_READ)))
3950 // default: for empty levels, use level template for custom elements
3951 level->use_custom_template = TRUE;
3953 level->no_valid_file = FALSE;
3956 getFileChunkBE(file, chunk_name, NULL);
3957 if (strEqual(chunk_name, "RND1"))
3959 getFile32BitBE(file); // not used
3961 getFileChunkBE(file, chunk_name, NULL);
3962 if (!strEqual(chunk_name, "CAVE"))
3964 level->no_valid_file = TRUE;
3966 Warn("unknown format of level file '%s'", filename);
3973 else // check for pre-2.0 file format with cookie string
3975 strcpy(cookie, chunk_name);
3976 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3978 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3979 cookie[strlen(cookie) - 1] = '\0';
3981 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3983 level->no_valid_file = TRUE;
3985 Warn("unknown format of level file '%s'", filename);
3992 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3994 level->no_valid_file = TRUE;
3996 Warn("unsupported version of level file '%s'", filename);
4003 // pre-2.0 level files have no game version, so use file version here
4004 level->game_version = level->file_version;
4007 if (level->file_version < FILE_VERSION_1_2)
4009 // level files from versions before 1.2.0 without chunk structure
4010 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
4011 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4019 int (*loader)(File *, int, struct LevelInfo *);
4023 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4024 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4025 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4026 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4027 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4028 { "INFO", -1, LoadLevel_INFO },
4029 { "BODY", -1, LoadLevel_BODY },
4030 { "CONT", -1, LoadLevel_CONT },
4031 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4032 { "CNT3", -1, LoadLevel_CNT3 },
4033 { "CUS1", -1, LoadLevel_CUS1 },
4034 { "CUS2", -1, LoadLevel_CUS2 },
4035 { "CUS3", -1, LoadLevel_CUS3 },
4036 { "CUS4", -1, LoadLevel_CUS4 },
4037 { "GRP1", -1, LoadLevel_GRP1 },
4038 { "CONF", -1, LoadLevel_CONF },
4039 { "ELEM", -1, LoadLevel_ELEM },
4040 { "NOTE", -1, LoadLevel_NOTE },
4041 { "CUSX", -1, LoadLevel_CUSX },
4042 { "GRPX", -1, LoadLevel_GRPX },
4043 { "EMPX", -1, LoadLevel_EMPX },
4048 while (getFileChunkBE(file, chunk_name, &chunk_size))
4052 while (chunk_info[i].name != NULL &&
4053 !strEqual(chunk_name, chunk_info[i].name))
4056 if (chunk_info[i].name == NULL)
4058 Warn("unknown chunk '%s' in level file '%s'",
4059 chunk_name, filename);
4061 ReadUnusedBytesFromFile(file, chunk_size);
4063 else if (chunk_info[i].size != -1 &&
4064 chunk_info[i].size != chunk_size)
4066 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4067 chunk_size, chunk_name, filename);
4069 ReadUnusedBytesFromFile(file, chunk_size);
4073 // call function to load this level chunk
4074 int chunk_size_expected =
4075 (chunk_info[i].loader)(file, chunk_size, level);
4077 if (chunk_size_expected < 0)
4079 Warn("error reading chunk '%s' in level file '%s'",
4080 chunk_name, filename);
4085 // the size of some chunks cannot be checked before reading other
4086 // chunks first (like "HEAD" and "BODY") that contain some header
4087 // information, so check them here
4088 if (chunk_size_expected != chunk_size)
4090 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4091 chunk_size, chunk_name, filename);
4103 // ----------------------------------------------------------------------------
4104 // functions for loading BD level
4105 // ----------------------------------------------------------------------------
4107 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4108 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4110 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4112 struct LevelInfo_BD *level_bd = level->native_bd_level;
4113 GdCave *cave = NULL; // will be changed below
4114 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4115 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4118 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4120 // cave and map newly allocated when set to defaults above
4121 cave = level_bd->cave;
4124 cave->intermission = level->bd_intermission;
4127 cave->level_time[0] = level->time;
4128 cave->level_diamonds[0] = level->gems_needed;
4131 cave->scheduling = level->bd_scheduling_type;
4132 cave->pal_timing = level->bd_pal_timing;
4133 cave->level_speed[0] = level->bd_cycle_delay_ms;
4134 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4135 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4136 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4139 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4140 cave->diamond_value = level->score[SC_EMERALD];
4141 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4143 // compatibility settings
4144 cave->lineshift = level->bd_line_shifting_borders;
4145 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4146 cave->short_explosions = level->bd_short_explosions;
4147 cave->gravity_affects_all = level->bd_gravity_affects_all;
4149 // player properties
4150 cave->diagonal_movements = level->bd_diagonal_movements;
4151 cave->active_is_first_found = level->bd_topmost_player_active;
4152 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4153 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4154 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4155 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4157 // element properties
4158 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4159 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4160 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4161 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4162 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4163 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4164 cave->level_magic_wall_time[0] = level->time_magic_wall;
4165 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4166 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4168 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4169 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4170 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4171 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4172 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4173 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4174 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4176 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4177 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4178 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4179 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4180 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4181 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4182 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4183 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4184 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4185 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4186 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4188 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4189 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4190 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4191 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4192 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4193 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4195 cave->slime_predictable = level->bd_slime_is_predictable;
4196 cave->slime_correct_random = level->bd_slime_correct_random;
4197 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4198 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4199 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4200 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4201 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4202 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4203 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4204 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4205 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4206 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4208 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4209 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4210 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4212 cave->biter_delay_frame = level->bd_biter_move_delay;
4213 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4215 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4217 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4219 cave->replicators_active = level->bd_replicators_active;
4220 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4222 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4223 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4225 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4227 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4229 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4230 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4231 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4233 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4234 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4236 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4237 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4239 cave->creatures_backwards = level->bd_creatures_start_backwards;
4240 cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4241 cave->creatures_direction_auto_change_time = level->bd_creatures_auto_turn_delay;
4244 strncpy(cave->name, level->name, sizeof(GdString));
4245 cave->name[sizeof(GdString) - 1] = '\0';
4247 // playfield elements
4248 for (x = 0; x < cave->w; x++)
4249 for (y = 0; y < cave->h; y++)
4250 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4253 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4255 struct LevelInfo_BD *level_bd = level->native_bd_level;
4256 GdCave *cave = level_bd->cave;
4257 int bd_level_nr = level_bd->level_nr;
4260 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4261 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4264 level->bd_intermission = cave->intermission;
4267 level->time = cave->level_time[bd_level_nr];
4268 level->gems_needed = cave->level_diamonds[bd_level_nr];
4271 level->bd_scheduling_type = cave->scheduling;
4272 level->bd_pal_timing = cave->pal_timing;
4273 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4274 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4275 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4276 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4279 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4280 level->score[SC_EMERALD] = cave->diamond_value;
4281 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4283 // compatibility settings
4284 level->bd_line_shifting_borders = cave->lineshift;
4285 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4286 level->bd_short_explosions = cave->short_explosions;
4287 level->bd_gravity_affects_all = cave->gravity_affects_all;
4289 // player properties
4290 level->bd_diagonal_movements = cave->diagonal_movements;
4291 level->bd_topmost_player_active = cave->active_is_first_found;
4292 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4293 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4294 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4295 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4297 // element properties
4298 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4299 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4300 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4301 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4302 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4303 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4304 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4305 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4306 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4308 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4309 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4310 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4311 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4312 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4313 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4314 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4316 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4317 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4318 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4319 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4320 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4321 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4322 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4323 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4324 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4325 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4326 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4328 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4329 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4330 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4331 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4332 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4333 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4335 level->bd_slime_is_predictable = cave->slime_predictable;
4336 level->bd_slime_correct_random = cave->slime_correct_random;
4337 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4338 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4339 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4340 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4341 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4342 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4343 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4344 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4345 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4346 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4348 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4349 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4350 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4352 level->bd_biter_move_delay = cave->biter_delay_frame;
4353 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4355 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4357 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4359 level->bd_replicators_active = cave->replicators_active;
4360 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4362 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4363 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4365 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4367 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4369 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4370 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4371 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4373 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4374 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4376 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4377 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4379 level->bd_creatures_start_backwards = cave->creatures_backwards;
4380 level->bd_creatures_turn_on_hatching = cave->creatures_direction_auto_change_on_start;
4381 level->bd_creatures_auto_turn_delay = cave->creatures_direction_auto_change_time;
4384 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4386 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4387 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4389 // playfield elements
4390 for (x = 0; x < level->fieldx; x++)
4391 for (y = 0; y < level->fieldy; y++)
4392 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4394 checked_free(cave_name);
4397 static void setTapeInfoToDefaults(void);
4399 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4401 struct LevelInfo_BD *level_bd = level->native_bd_level;
4402 GdCave *cave = level_bd->cave;
4403 GdReplay *replay = level_bd->replay;
4409 // always start with reliable default values
4410 setTapeInfoToDefaults();
4412 tape.level_nr = level_nr; // (currently not used)
4413 tape.random_seed = replay->seed;
4415 TapeSetDateFromIsoDateString(replay->date);
4418 tape.pos[tape.counter].delay = 0;
4420 tape.bd_replay = TRUE;
4422 // all time calculations only used to display approximate tape time
4423 int cave_speed = cave->speed;
4424 int milliseconds_game = 0;
4425 int milliseconds_elapsed = 20;
4427 for (i = 0; i < replay->movements->len; i++)
4429 int replay_action = replay->movements->data[i];
4430 int tape_action = map_action_BD_to_RND(replay_action);
4431 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4432 boolean success = 0;
4436 success = TapeAddAction(action);
4438 milliseconds_game += milliseconds_elapsed;
4440 if (milliseconds_game >= cave_speed)
4442 milliseconds_game -= cave_speed;
4449 tape.pos[tape.counter].delay = 0;
4450 tape.pos[tape.counter].action[0] = 0;
4454 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4460 TapeHaltRecording();
4464 // ----------------------------------------------------------------------------
4465 // functions for loading EM level
4466 // ----------------------------------------------------------------------------
4468 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4470 static int ball_xy[8][2] =
4481 struct LevelInfo_EM *level_em = level->native_em_level;
4482 struct CAVE *cav = level_em->cav;
4485 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4486 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4488 cav->time_seconds = level->time;
4489 cav->gems_needed = level->gems_needed;
4491 cav->emerald_score = level->score[SC_EMERALD];
4492 cav->diamond_score = level->score[SC_DIAMOND];
4493 cav->alien_score = level->score[SC_ROBOT];
4494 cav->tank_score = level->score[SC_SPACESHIP];
4495 cav->bug_score = level->score[SC_BUG];
4496 cav->eater_score = level->score[SC_YAMYAM];
4497 cav->nut_score = level->score[SC_NUT];
4498 cav->dynamite_score = level->score[SC_DYNAMITE];
4499 cav->key_score = level->score[SC_KEY];
4500 cav->exit_score = level->score[SC_TIME_BONUS];
4502 cav->num_eater_arrays = level->num_yamyam_contents;
4504 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4505 for (y = 0; y < 3; y++)
4506 for (x = 0; x < 3; x++)
4507 cav->eater_array[i][y * 3 + x] =
4508 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4510 cav->amoeba_time = level->amoeba_speed;
4511 cav->wonderwall_time = level->time_magic_wall;
4512 cav->wheel_time = level->time_wheel;
4514 cav->android_move_time = level->android_move_time;
4515 cav->android_clone_time = level->android_clone_time;
4516 cav->ball_random = level->ball_random;
4517 cav->ball_active = level->ball_active_initial;
4518 cav->ball_time = level->ball_time;
4519 cav->num_ball_arrays = level->num_ball_contents;
4521 cav->lenses_score = level->lenses_score;
4522 cav->magnify_score = level->magnify_score;
4523 cav->slurp_score = level->slurp_score;
4525 cav->lenses_time = level->lenses_time;
4526 cav->magnify_time = level->magnify_time;
4528 cav->wind_time = 9999;
4529 cav->wind_direction =
4530 map_direction_RND_to_EM(level->wind_direction_initial);
4532 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4533 for (j = 0; j < 8; j++)
4534 cav->ball_array[i][j] =
4535 map_element_RND_to_EM_cave(level->ball_content[i].
4536 e[ball_xy[j][0]][ball_xy[j][1]]);
4538 map_android_clone_elements_RND_to_EM(level);
4540 // first fill the complete playfield with the empty space element
4541 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4542 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4543 cav->cave[x][y] = Cblank;
4545 // then copy the real level contents from level file into the playfield
4546 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4548 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4550 if (level->field[x][y] == EL_AMOEBA_DEAD)
4551 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4553 cav->cave[x][y] = new_element;
4556 for (i = 0; i < MAX_PLAYERS; i++)
4558 cav->player_x[i] = -1;
4559 cav->player_y[i] = -1;
4562 // initialize player positions and delete players from the playfield
4563 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4565 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4567 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4569 cav->player_x[player_nr] = x;
4570 cav->player_y[player_nr] = y;
4572 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4577 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4579 static int ball_xy[8][2] =
4590 struct LevelInfo_EM *level_em = level->native_em_level;
4591 struct CAVE *cav = level_em->cav;
4594 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4595 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4597 level->time = cav->time_seconds;
4598 level->gems_needed = cav->gems_needed;
4600 sprintf(level->name, "Level %d", level->file_info.nr);
4602 level->score[SC_EMERALD] = cav->emerald_score;
4603 level->score[SC_DIAMOND] = cav->diamond_score;
4604 level->score[SC_ROBOT] = cav->alien_score;
4605 level->score[SC_SPACESHIP] = cav->tank_score;
4606 level->score[SC_BUG] = cav->bug_score;
4607 level->score[SC_YAMYAM] = cav->eater_score;
4608 level->score[SC_NUT] = cav->nut_score;
4609 level->score[SC_DYNAMITE] = cav->dynamite_score;
4610 level->score[SC_KEY] = cav->key_score;
4611 level->score[SC_TIME_BONUS] = cav->exit_score;
4613 level->num_yamyam_contents = cav->num_eater_arrays;
4615 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4616 for (y = 0; y < 3; y++)
4617 for (x = 0; x < 3; x++)
4618 level->yamyam_content[i].e[x][y] =
4619 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4621 level->amoeba_speed = cav->amoeba_time;
4622 level->time_magic_wall = cav->wonderwall_time;
4623 level->time_wheel = cav->wheel_time;
4625 level->android_move_time = cav->android_move_time;
4626 level->android_clone_time = cav->android_clone_time;
4627 level->ball_random = cav->ball_random;
4628 level->ball_active_initial = cav->ball_active;
4629 level->ball_time = cav->ball_time;
4630 level->num_ball_contents = cav->num_ball_arrays;
4632 level->lenses_score = cav->lenses_score;
4633 level->magnify_score = cav->magnify_score;
4634 level->slurp_score = cav->slurp_score;
4636 level->lenses_time = cav->lenses_time;
4637 level->magnify_time = cav->magnify_time;
4639 level->wind_direction_initial =
4640 map_direction_EM_to_RND(cav->wind_direction);
4642 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4643 for (j = 0; j < 8; j++)
4644 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4645 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4647 map_android_clone_elements_EM_to_RND(level);
4649 // convert the playfield (some elements need special treatment)
4650 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4652 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4654 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4655 new_element = EL_AMOEBA_DEAD;
4657 level->field[x][y] = new_element;
4660 for (i = 0; i < MAX_PLAYERS; i++)
4662 // in case of all players set to the same field, use the first player
4663 int nr = MAX_PLAYERS - i - 1;
4664 int jx = cav->player_x[nr];
4665 int jy = cav->player_y[nr];
4667 if (jx != -1 && jy != -1)
4668 level->field[jx][jy] = EL_PLAYER_1 + nr;
4671 // time score is counted for each 10 seconds left in Emerald Mine levels
4672 level->time_score_base = 10;
4676 // ----------------------------------------------------------------------------
4677 // functions for loading SP level
4678 // ----------------------------------------------------------------------------
4680 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4682 struct LevelInfo_SP *level_sp = level->native_sp_level;
4683 LevelInfoType *header = &level_sp->header;
4686 level_sp->width = level->fieldx;
4687 level_sp->height = level->fieldy;
4689 for (x = 0; x < level->fieldx; x++)
4690 for (y = 0; y < level->fieldy; y++)
4691 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4693 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4695 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4696 header->LevelTitle[i] = level->name[i];
4697 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4699 header->InfotronsNeeded = level->gems_needed;
4701 header->SpecialPortCount = 0;
4703 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4705 boolean gravity_port_found = FALSE;
4706 boolean gravity_port_valid = FALSE;
4707 int gravity_port_flag;
4708 int gravity_port_base_element;
4709 int element = level->field[x][y];
4711 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4712 element <= EL_SP_GRAVITY_ON_PORT_UP)
4714 gravity_port_found = TRUE;
4715 gravity_port_valid = TRUE;
4716 gravity_port_flag = 1;
4717 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4719 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4720 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4722 gravity_port_found = TRUE;
4723 gravity_port_valid = TRUE;
4724 gravity_port_flag = 0;
4725 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4727 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4728 element <= EL_SP_GRAVITY_PORT_UP)
4730 // change R'n'D style gravity inverting special port to normal port
4731 // (there are no gravity inverting ports in native Supaplex engine)
4733 gravity_port_found = TRUE;
4734 gravity_port_valid = FALSE;
4735 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4738 if (gravity_port_found)
4740 if (gravity_port_valid &&
4741 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4743 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4745 port->PortLocation = (y * level->fieldx + x) * 2;
4746 port->Gravity = gravity_port_flag;
4748 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4750 header->SpecialPortCount++;
4754 // change special gravity port to normal port
4756 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4759 level_sp->playfield[x][y] = element - EL_SP_START;
4764 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4766 struct LevelInfo_SP *level_sp = level->native_sp_level;
4767 LevelInfoType *header = &level_sp->header;
4768 boolean num_invalid_elements = 0;
4771 level->fieldx = level_sp->width;
4772 level->fieldy = level_sp->height;
4774 for (x = 0; x < level->fieldx; x++)
4776 for (y = 0; y < level->fieldy; y++)
4778 int element_old = level_sp->playfield[x][y];
4779 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4781 if (element_new == EL_UNKNOWN)
4783 num_invalid_elements++;
4785 Debug("level:native:SP", "invalid element %d at position %d, %d",
4789 level->field[x][y] = element_new;
4793 if (num_invalid_elements > 0)
4794 Warn("found %d invalid elements%s", num_invalid_elements,
4795 (!options.debug ? " (use '--debug' for more details)" : ""));
4797 for (i = 0; i < MAX_PLAYERS; i++)
4798 level->initial_player_gravity[i] =
4799 (header->InitialGravity == 1 ? TRUE : FALSE);
4801 // skip leading spaces
4802 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4803 if (header->LevelTitle[i] != ' ')
4807 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4808 level->name[j] = header->LevelTitle[i];
4809 level->name[j] = '\0';
4811 // cut trailing spaces
4813 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4814 level->name[j - 1] = '\0';
4816 level->gems_needed = header->InfotronsNeeded;
4818 for (i = 0; i < header->SpecialPortCount; i++)
4820 SpecialPortType *port = &header->SpecialPort[i];
4821 int port_location = port->PortLocation;
4822 int gravity = port->Gravity;
4823 int port_x, port_y, port_element;
4825 port_x = (port_location / 2) % level->fieldx;
4826 port_y = (port_location / 2) / level->fieldx;
4828 if (port_x < 0 || port_x >= level->fieldx ||
4829 port_y < 0 || port_y >= level->fieldy)
4831 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4836 port_element = level->field[port_x][port_y];
4838 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4839 port_element > EL_SP_GRAVITY_PORT_UP)
4841 Warn("no special port at position (%d, %d)", port_x, port_y);
4846 // change previous (wrong) gravity inverting special port to either
4847 // gravity enabling special port or gravity disabling special port
4848 level->field[port_x][port_y] +=
4849 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4850 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4853 // change special gravity ports without database entries to normal ports
4854 for (x = 0; x < level->fieldx; x++)
4855 for (y = 0; y < level->fieldy; y++)
4856 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4857 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4858 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4860 level->time = 0; // no time limit
4861 level->amoeba_speed = 0;
4862 level->time_magic_wall = 0;
4863 level->time_wheel = 0;
4864 level->amoeba_content = EL_EMPTY;
4866 // original Supaplex does not use score values -- rate by playing time
4867 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4868 level->score[i] = 0;
4870 level->rate_time_over_score = TRUE;
4872 // there are no yamyams in supaplex levels
4873 for (i = 0; i < level->num_yamyam_contents; i++)
4874 for (x = 0; x < 3; x++)
4875 for (y = 0; y < 3; y++)
4876 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4879 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4881 struct LevelInfo_SP *level_sp = level->native_sp_level;
4882 struct DemoInfo_SP *demo = &level_sp->demo;
4885 // always start with reliable default values
4886 demo->is_available = FALSE;
4889 if (TAPE_IS_EMPTY(tape))
4892 demo->level_nr = tape.level_nr; // (currently not used)
4894 level_sp->header.DemoRandomSeed = tape.random_seed;
4898 for (i = 0; i < tape.length; i++)
4900 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4901 int demo_repeat = tape.pos[i].delay;
4902 int demo_entries = (demo_repeat + 15) / 16;
4904 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4906 Warn("tape truncated: size exceeds maximum SP demo size %d",
4912 for (j = 0; j < demo_repeat / 16; j++)
4913 demo->data[demo->length++] = 0xf0 | demo_action;
4915 if (demo_repeat % 16)
4916 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4919 demo->is_available = TRUE;
4922 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4924 struct LevelInfo_SP *level_sp = level->native_sp_level;
4925 struct DemoInfo_SP *demo = &level_sp->demo;
4926 char *filename = level->file_info.filename;
4929 // always start with reliable default values
4930 setTapeInfoToDefaults();
4932 if (!demo->is_available)
4935 tape.level_nr = demo->level_nr; // (currently not used)
4936 tape.random_seed = level_sp->header.DemoRandomSeed;
4938 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4941 tape.pos[tape.counter].delay = 0;
4943 for (i = 0; i < demo->length; i++)
4945 int demo_action = demo->data[i] & 0x0f;
4946 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4947 int tape_action = map_key_SP_to_RND(demo_action);
4948 int tape_repeat = demo_repeat + 1;
4949 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4950 boolean success = 0;
4953 for (j = 0; j < tape_repeat; j++)
4954 success = TapeAddAction(action);
4958 Warn("SP demo truncated: size exceeds maximum tape size %d",
4965 TapeHaltRecording();
4969 // ----------------------------------------------------------------------------
4970 // functions for loading MM level
4971 // ----------------------------------------------------------------------------
4973 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4975 struct LevelInfo_MM *level_mm = level->native_mm_level;
4978 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4979 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4981 level_mm->time = level->time;
4982 level_mm->kettles_needed = level->gems_needed;
4983 level_mm->auto_count_kettles = level->auto_count_gems;
4985 level_mm->mm_laser_red = level->mm_laser_red;
4986 level_mm->mm_laser_green = level->mm_laser_green;
4987 level_mm->mm_laser_blue = level->mm_laser_blue;
4989 level_mm->df_laser_red = level->df_laser_red;
4990 level_mm->df_laser_green = level->df_laser_green;
4991 level_mm->df_laser_blue = level->df_laser_blue;
4993 strcpy(level_mm->name, level->name);
4994 strcpy(level_mm->author, level->author);
4996 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4997 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4998 level_mm->score[SC_KEY] = level->score[SC_KEY];
4999 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5000 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5002 level_mm->amoeba_speed = level->amoeba_speed;
5003 level_mm->time_fuse = level->mm_time_fuse;
5004 level_mm->time_bomb = level->mm_time_bomb;
5005 level_mm->time_ball = level->mm_time_ball;
5006 level_mm->time_block = level->mm_time_block;
5008 level_mm->num_ball_contents = level->num_mm_ball_contents;
5009 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5010 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5011 level_mm->explode_ball = level->explode_mm_ball;
5013 for (i = 0; i < level->num_mm_ball_contents; i++)
5014 level_mm->ball_content[i] =
5015 map_element_RND_to_MM(level->mm_ball_content[i]);
5017 for (x = 0; x < level->fieldx; x++)
5018 for (y = 0; y < level->fieldy; y++)
5020 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5023 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5025 struct LevelInfo_MM *level_mm = level->native_mm_level;
5028 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5029 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5031 level->time = level_mm->time;
5032 level->gems_needed = level_mm->kettles_needed;
5033 level->auto_count_gems = level_mm->auto_count_kettles;
5035 level->mm_laser_red = level_mm->mm_laser_red;
5036 level->mm_laser_green = level_mm->mm_laser_green;
5037 level->mm_laser_blue = level_mm->mm_laser_blue;
5039 level->df_laser_red = level_mm->df_laser_red;
5040 level->df_laser_green = level_mm->df_laser_green;
5041 level->df_laser_blue = level_mm->df_laser_blue;
5043 strcpy(level->name, level_mm->name);
5045 // only overwrite author from 'levelinfo.conf' if author defined in level
5046 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5047 strcpy(level->author, level_mm->author);
5049 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5050 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5051 level->score[SC_KEY] = level_mm->score[SC_KEY];
5052 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5053 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5055 level->amoeba_speed = level_mm->amoeba_speed;
5056 level->mm_time_fuse = level_mm->time_fuse;
5057 level->mm_time_bomb = level_mm->time_bomb;
5058 level->mm_time_ball = level_mm->time_ball;
5059 level->mm_time_block = level_mm->time_block;
5061 level->num_mm_ball_contents = level_mm->num_ball_contents;
5062 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5063 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5064 level->explode_mm_ball = level_mm->explode_ball;
5066 for (i = 0; i < level->num_mm_ball_contents; i++)
5067 level->mm_ball_content[i] =
5068 map_element_MM_to_RND(level_mm->ball_content[i]);
5070 for (x = 0; x < level->fieldx; x++)
5071 for (y = 0; y < level->fieldy; y++)
5072 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5076 // ----------------------------------------------------------------------------
5077 // functions for loading DC level
5078 // ----------------------------------------------------------------------------
5080 #define DC_LEVEL_HEADER_SIZE 344
5082 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5085 static int last_data_encoded;
5089 int diff_hi, diff_lo;
5090 int data_hi, data_lo;
5091 unsigned short data_decoded;
5095 last_data_encoded = 0;
5102 diff = data_encoded - last_data_encoded;
5103 diff_hi = diff & ~0xff;
5104 diff_lo = diff & 0xff;
5108 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5109 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5110 data_hi = data_hi & 0xff00;
5112 data_decoded = data_hi | data_lo;
5114 last_data_encoded = data_encoded;
5116 offset1 = (offset1 + 1) % 31;
5117 offset2 = offset2 & 0xff;
5119 return data_decoded;
5122 static int getMappedElement_DC(int element)
5130 // 0x0117 - 0x036e: (?)
5133 // 0x042d - 0x0684: (?)
5149 element = EL_CRYSTAL;
5152 case 0x0e77: // quicksand (boulder)
5153 element = EL_QUICKSAND_FAST_FULL;
5156 case 0x0e99: // slow quicksand (boulder)
5157 element = EL_QUICKSAND_FULL;
5161 element = EL_EM_EXIT_OPEN;
5165 element = EL_EM_EXIT_CLOSED;
5169 element = EL_EM_STEEL_EXIT_OPEN;
5173 element = EL_EM_STEEL_EXIT_CLOSED;
5176 case 0x0f4f: // dynamite (lit 1)
5177 element = EL_EM_DYNAMITE_ACTIVE;
5180 case 0x0f57: // dynamite (lit 2)
5181 element = EL_EM_DYNAMITE_ACTIVE;
5184 case 0x0f5f: // dynamite (lit 3)
5185 element = EL_EM_DYNAMITE_ACTIVE;
5188 case 0x0f67: // dynamite (lit 4)
5189 element = EL_EM_DYNAMITE_ACTIVE;
5196 element = EL_AMOEBA_WET;
5200 element = EL_AMOEBA_DROP;
5204 element = EL_DC_MAGIC_WALL;
5208 element = EL_SPACESHIP_UP;
5212 element = EL_SPACESHIP_DOWN;
5216 element = EL_SPACESHIP_LEFT;
5220 element = EL_SPACESHIP_RIGHT;
5224 element = EL_BUG_UP;
5228 element = EL_BUG_DOWN;
5232 element = EL_BUG_LEFT;
5236 element = EL_BUG_RIGHT;
5240 element = EL_MOLE_UP;
5244 element = EL_MOLE_DOWN;
5248 element = EL_MOLE_LEFT;
5252 element = EL_MOLE_RIGHT;
5260 element = EL_YAMYAM_UP;
5264 element = EL_SWITCHGATE_OPEN;
5268 element = EL_SWITCHGATE_CLOSED;
5272 element = EL_DC_SWITCHGATE_SWITCH_UP;
5276 element = EL_TIMEGATE_CLOSED;
5279 case 0x144c: // conveyor belt switch (green)
5280 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5283 case 0x144f: // conveyor belt switch (red)
5284 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5287 case 0x1452: // conveyor belt switch (blue)
5288 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5292 element = EL_CONVEYOR_BELT_3_MIDDLE;
5296 element = EL_CONVEYOR_BELT_3_LEFT;
5300 element = EL_CONVEYOR_BELT_3_RIGHT;
5304 element = EL_CONVEYOR_BELT_1_MIDDLE;
5308 element = EL_CONVEYOR_BELT_1_LEFT;
5312 element = EL_CONVEYOR_BELT_1_RIGHT;
5316 element = EL_CONVEYOR_BELT_4_MIDDLE;
5320 element = EL_CONVEYOR_BELT_4_LEFT;
5324 element = EL_CONVEYOR_BELT_4_RIGHT;
5328 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5332 element = EL_EXPANDABLE_WALL_VERTICAL;
5336 element = EL_EXPANDABLE_WALL_ANY;
5339 case 0x14ce: // growing steel wall (left/right)
5340 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5343 case 0x14df: // growing steel wall (up/down)
5344 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5347 case 0x14e8: // growing steel wall (up/down/left/right)
5348 element = EL_EXPANDABLE_STEELWALL_ANY;
5352 element = EL_SHIELD_DEADLY;
5356 element = EL_EXTRA_TIME;
5364 element = EL_EMPTY_SPACE;
5367 case 0x1578: // quicksand (empty)
5368 element = EL_QUICKSAND_FAST_EMPTY;
5371 case 0x1579: // slow quicksand (empty)
5372 element = EL_QUICKSAND_EMPTY;
5382 element = EL_EM_DYNAMITE;
5385 case 0x15a1: // key (red)
5386 element = EL_EM_KEY_1;
5389 case 0x15a2: // key (yellow)
5390 element = EL_EM_KEY_2;
5393 case 0x15a3: // key (blue)
5394 element = EL_EM_KEY_4;
5397 case 0x15a4: // key (green)
5398 element = EL_EM_KEY_3;
5401 case 0x15a5: // key (white)
5402 element = EL_DC_KEY_WHITE;
5406 element = EL_WALL_SLIPPERY;
5413 case 0x15a8: // wall (not round)
5417 case 0x15a9: // (blue)
5418 element = EL_CHAR_A;
5421 case 0x15aa: // (blue)
5422 element = EL_CHAR_B;
5425 case 0x15ab: // (blue)
5426 element = EL_CHAR_C;
5429 case 0x15ac: // (blue)
5430 element = EL_CHAR_D;
5433 case 0x15ad: // (blue)
5434 element = EL_CHAR_E;
5437 case 0x15ae: // (blue)
5438 element = EL_CHAR_F;
5441 case 0x15af: // (blue)
5442 element = EL_CHAR_G;
5445 case 0x15b0: // (blue)
5446 element = EL_CHAR_H;
5449 case 0x15b1: // (blue)
5450 element = EL_CHAR_I;
5453 case 0x15b2: // (blue)
5454 element = EL_CHAR_J;
5457 case 0x15b3: // (blue)
5458 element = EL_CHAR_K;
5461 case 0x15b4: // (blue)
5462 element = EL_CHAR_L;
5465 case 0x15b5: // (blue)
5466 element = EL_CHAR_M;
5469 case 0x15b6: // (blue)
5470 element = EL_CHAR_N;
5473 case 0x15b7: // (blue)
5474 element = EL_CHAR_O;
5477 case 0x15b8: // (blue)
5478 element = EL_CHAR_P;
5481 case 0x15b9: // (blue)
5482 element = EL_CHAR_Q;
5485 case 0x15ba: // (blue)
5486 element = EL_CHAR_R;
5489 case 0x15bb: // (blue)
5490 element = EL_CHAR_S;
5493 case 0x15bc: // (blue)
5494 element = EL_CHAR_T;
5497 case 0x15bd: // (blue)
5498 element = EL_CHAR_U;
5501 case 0x15be: // (blue)
5502 element = EL_CHAR_V;
5505 case 0x15bf: // (blue)
5506 element = EL_CHAR_W;
5509 case 0x15c0: // (blue)
5510 element = EL_CHAR_X;
5513 case 0x15c1: // (blue)
5514 element = EL_CHAR_Y;
5517 case 0x15c2: // (blue)
5518 element = EL_CHAR_Z;
5521 case 0x15c3: // (blue)
5522 element = EL_CHAR_AUMLAUT;
5525 case 0x15c4: // (blue)
5526 element = EL_CHAR_OUMLAUT;
5529 case 0x15c5: // (blue)
5530 element = EL_CHAR_UUMLAUT;
5533 case 0x15c6: // (blue)
5534 element = EL_CHAR_0;
5537 case 0x15c7: // (blue)
5538 element = EL_CHAR_1;
5541 case 0x15c8: // (blue)
5542 element = EL_CHAR_2;
5545 case 0x15c9: // (blue)
5546 element = EL_CHAR_3;
5549 case 0x15ca: // (blue)
5550 element = EL_CHAR_4;
5553 case 0x15cb: // (blue)
5554 element = EL_CHAR_5;
5557 case 0x15cc: // (blue)
5558 element = EL_CHAR_6;
5561 case 0x15cd: // (blue)
5562 element = EL_CHAR_7;
5565 case 0x15ce: // (blue)
5566 element = EL_CHAR_8;
5569 case 0x15cf: // (blue)
5570 element = EL_CHAR_9;
5573 case 0x15d0: // (blue)
5574 element = EL_CHAR_PERIOD;
5577 case 0x15d1: // (blue)
5578 element = EL_CHAR_EXCLAM;
5581 case 0x15d2: // (blue)
5582 element = EL_CHAR_COLON;
5585 case 0x15d3: // (blue)
5586 element = EL_CHAR_LESS;
5589 case 0x15d4: // (blue)
5590 element = EL_CHAR_GREATER;
5593 case 0x15d5: // (blue)
5594 element = EL_CHAR_QUESTION;
5597 case 0x15d6: // (blue)
5598 element = EL_CHAR_COPYRIGHT;
5601 case 0x15d7: // (blue)
5602 element = EL_CHAR_UP;
5605 case 0x15d8: // (blue)
5606 element = EL_CHAR_DOWN;
5609 case 0x15d9: // (blue)
5610 element = EL_CHAR_BUTTON;
5613 case 0x15da: // (blue)
5614 element = EL_CHAR_PLUS;
5617 case 0x15db: // (blue)
5618 element = EL_CHAR_MINUS;
5621 case 0x15dc: // (blue)
5622 element = EL_CHAR_APOSTROPHE;
5625 case 0x15dd: // (blue)
5626 element = EL_CHAR_PARENLEFT;
5629 case 0x15de: // (blue)
5630 element = EL_CHAR_PARENRIGHT;
5633 case 0x15df: // (green)
5634 element = EL_CHAR_A;
5637 case 0x15e0: // (green)
5638 element = EL_CHAR_B;
5641 case 0x15e1: // (green)
5642 element = EL_CHAR_C;
5645 case 0x15e2: // (green)
5646 element = EL_CHAR_D;
5649 case 0x15e3: // (green)
5650 element = EL_CHAR_E;
5653 case 0x15e4: // (green)
5654 element = EL_CHAR_F;
5657 case 0x15e5: // (green)
5658 element = EL_CHAR_G;
5661 case 0x15e6: // (green)
5662 element = EL_CHAR_H;
5665 case 0x15e7: // (green)
5666 element = EL_CHAR_I;
5669 case 0x15e8: // (green)
5670 element = EL_CHAR_J;
5673 case 0x15e9: // (green)
5674 element = EL_CHAR_K;
5677 case 0x15ea: // (green)
5678 element = EL_CHAR_L;
5681 case 0x15eb: // (green)
5682 element = EL_CHAR_M;
5685 case 0x15ec: // (green)
5686 element = EL_CHAR_N;
5689 case 0x15ed: // (green)
5690 element = EL_CHAR_O;
5693 case 0x15ee: // (green)
5694 element = EL_CHAR_P;
5697 case 0x15ef: // (green)
5698 element = EL_CHAR_Q;
5701 case 0x15f0: // (green)
5702 element = EL_CHAR_R;
5705 case 0x15f1: // (green)
5706 element = EL_CHAR_S;
5709 case 0x15f2: // (green)
5710 element = EL_CHAR_T;
5713 case 0x15f3: // (green)
5714 element = EL_CHAR_U;
5717 case 0x15f4: // (green)
5718 element = EL_CHAR_V;
5721 case 0x15f5: // (green)
5722 element = EL_CHAR_W;
5725 case 0x15f6: // (green)
5726 element = EL_CHAR_X;
5729 case 0x15f7: // (green)
5730 element = EL_CHAR_Y;
5733 case 0x15f8: // (green)
5734 element = EL_CHAR_Z;
5737 case 0x15f9: // (green)
5738 element = EL_CHAR_AUMLAUT;
5741 case 0x15fa: // (green)
5742 element = EL_CHAR_OUMLAUT;
5745 case 0x15fb: // (green)
5746 element = EL_CHAR_UUMLAUT;
5749 case 0x15fc: // (green)
5750 element = EL_CHAR_0;
5753 case 0x15fd: // (green)
5754 element = EL_CHAR_1;
5757 case 0x15fe: // (green)
5758 element = EL_CHAR_2;
5761 case 0x15ff: // (green)
5762 element = EL_CHAR_3;
5765 case 0x1600: // (green)
5766 element = EL_CHAR_4;
5769 case 0x1601: // (green)
5770 element = EL_CHAR_5;
5773 case 0x1602: // (green)
5774 element = EL_CHAR_6;
5777 case 0x1603: // (green)
5778 element = EL_CHAR_7;
5781 case 0x1604: // (green)
5782 element = EL_CHAR_8;
5785 case 0x1605: // (green)
5786 element = EL_CHAR_9;
5789 case 0x1606: // (green)
5790 element = EL_CHAR_PERIOD;
5793 case 0x1607: // (green)
5794 element = EL_CHAR_EXCLAM;
5797 case 0x1608: // (green)
5798 element = EL_CHAR_COLON;
5801 case 0x1609: // (green)
5802 element = EL_CHAR_LESS;
5805 case 0x160a: // (green)
5806 element = EL_CHAR_GREATER;
5809 case 0x160b: // (green)
5810 element = EL_CHAR_QUESTION;
5813 case 0x160c: // (green)
5814 element = EL_CHAR_COPYRIGHT;
5817 case 0x160d: // (green)
5818 element = EL_CHAR_UP;
5821 case 0x160e: // (green)
5822 element = EL_CHAR_DOWN;
5825 case 0x160f: // (green)
5826 element = EL_CHAR_BUTTON;
5829 case 0x1610: // (green)
5830 element = EL_CHAR_PLUS;
5833 case 0x1611: // (green)
5834 element = EL_CHAR_MINUS;
5837 case 0x1612: // (green)
5838 element = EL_CHAR_APOSTROPHE;
5841 case 0x1613: // (green)
5842 element = EL_CHAR_PARENLEFT;
5845 case 0x1614: // (green)
5846 element = EL_CHAR_PARENRIGHT;
5849 case 0x1615: // (blue steel)
5850 element = EL_STEEL_CHAR_A;
5853 case 0x1616: // (blue steel)
5854 element = EL_STEEL_CHAR_B;
5857 case 0x1617: // (blue steel)
5858 element = EL_STEEL_CHAR_C;
5861 case 0x1618: // (blue steel)
5862 element = EL_STEEL_CHAR_D;
5865 case 0x1619: // (blue steel)
5866 element = EL_STEEL_CHAR_E;
5869 case 0x161a: // (blue steel)
5870 element = EL_STEEL_CHAR_F;
5873 case 0x161b: // (blue steel)
5874 element = EL_STEEL_CHAR_G;
5877 case 0x161c: // (blue steel)
5878 element = EL_STEEL_CHAR_H;
5881 case 0x161d: // (blue steel)
5882 element = EL_STEEL_CHAR_I;
5885 case 0x161e: // (blue steel)
5886 element = EL_STEEL_CHAR_J;
5889 case 0x161f: // (blue steel)
5890 element = EL_STEEL_CHAR_K;
5893 case 0x1620: // (blue steel)
5894 element = EL_STEEL_CHAR_L;
5897 case 0x1621: // (blue steel)
5898 element = EL_STEEL_CHAR_M;
5901 case 0x1622: // (blue steel)
5902 element = EL_STEEL_CHAR_N;
5905 case 0x1623: // (blue steel)
5906 element = EL_STEEL_CHAR_O;
5909 case 0x1624: // (blue steel)
5910 element = EL_STEEL_CHAR_P;
5913 case 0x1625: // (blue steel)
5914 element = EL_STEEL_CHAR_Q;
5917 case 0x1626: // (blue steel)
5918 element = EL_STEEL_CHAR_R;
5921 case 0x1627: // (blue steel)
5922 element = EL_STEEL_CHAR_S;
5925 case 0x1628: // (blue steel)
5926 element = EL_STEEL_CHAR_T;
5929 case 0x1629: // (blue steel)
5930 element = EL_STEEL_CHAR_U;
5933 case 0x162a: // (blue steel)
5934 element = EL_STEEL_CHAR_V;
5937 case 0x162b: // (blue steel)
5938 element = EL_STEEL_CHAR_W;
5941 case 0x162c: // (blue steel)
5942 element = EL_STEEL_CHAR_X;
5945 case 0x162d: // (blue steel)
5946 element = EL_STEEL_CHAR_Y;
5949 case 0x162e: // (blue steel)
5950 element = EL_STEEL_CHAR_Z;
5953 case 0x162f: // (blue steel)
5954 element = EL_STEEL_CHAR_AUMLAUT;
5957 case 0x1630: // (blue steel)
5958 element = EL_STEEL_CHAR_OUMLAUT;
5961 case 0x1631: // (blue steel)
5962 element = EL_STEEL_CHAR_UUMLAUT;
5965 case 0x1632: // (blue steel)
5966 element = EL_STEEL_CHAR_0;
5969 case 0x1633: // (blue steel)
5970 element = EL_STEEL_CHAR_1;
5973 case 0x1634: // (blue steel)
5974 element = EL_STEEL_CHAR_2;
5977 case 0x1635: // (blue steel)
5978 element = EL_STEEL_CHAR_3;
5981 case 0x1636: // (blue steel)
5982 element = EL_STEEL_CHAR_4;
5985 case 0x1637: // (blue steel)
5986 element = EL_STEEL_CHAR_5;
5989 case 0x1638: // (blue steel)
5990 element = EL_STEEL_CHAR_6;
5993 case 0x1639: // (blue steel)
5994 element = EL_STEEL_CHAR_7;
5997 case 0x163a: // (blue steel)
5998 element = EL_STEEL_CHAR_8;
6001 case 0x163b: // (blue steel)
6002 element = EL_STEEL_CHAR_9;
6005 case 0x163c: // (blue steel)
6006 element = EL_STEEL_CHAR_PERIOD;
6009 case 0x163d: // (blue steel)
6010 element = EL_STEEL_CHAR_EXCLAM;
6013 case 0x163e: // (blue steel)
6014 element = EL_STEEL_CHAR_COLON;
6017 case 0x163f: // (blue steel)
6018 element = EL_STEEL_CHAR_LESS;
6021 case 0x1640: // (blue steel)
6022 element = EL_STEEL_CHAR_GREATER;
6025 case 0x1641: // (blue steel)
6026 element = EL_STEEL_CHAR_QUESTION;
6029 case 0x1642: // (blue steel)
6030 element = EL_STEEL_CHAR_COPYRIGHT;
6033 case 0x1643: // (blue steel)
6034 element = EL_STEEL_CHAR_UP;
6037 case 0x1644: // (blue steel)
6038 element = EL_STEEL_CHAR_DOWN;
6041 case 0x1645: // (blue steel)
6042 element = EL_STEEL_CHAR_BUTTON;
6045 case 0x1646: // (blue steel)
6046 element = EL_STEEL_CHAR_PLUS;
6049 case 0x1647: // (blue steel)
6050 element = EL_STEEL_CHAR_MINUS;
6053 case 0x1648: // (blue steel)
6054 element = EL_STEEL_CHAR_APOSTROPHE;
6057 case 0x1649: // (blue steel)
6058 element = EL_STEEL_CHAR_PARENLEFT;
6061 case 0x164a: // (blue steel)
6062 element = EL_STEEL_CHAR_PARENRIGHT;
6065 case 0x164b: // (green steel)
6066 element = EL_STEEL_CHAR_A;
6069 case 0x164c: // (green steel)
6070 element = EL_STEEL_CHAR_B;
6073 case 0x164d: // (green steel)
6074 element = EL_STEEL_CHAR_C;
6077 case 0x164e: // (green steel)
6078 element = EL_STEEL_CHAR_D;
6081 case 0x164f: // (green steel)
6082 element = EL_STEEL_CHAR_E;
6085 case 0x1650: // (green steel)
6086 element = EL_STEEL_CHAR_F;
6089 case 0x1651: // (green steel)
6090 element = EL_STEEL_CHAR_G;
6093 case 0x1652: // (green steel)
6094 element = EL_STEEL_CHAR_H;
6097 case 0x1653: // (green steel)
6098 element = EL_STEEL_CHAR_I;
6101 case 0x1654: // (green steel)
6102 element = EL_STEEL_CHAR_J;
6105 case 0x1655: // (green steel)
6106 element = EL_STEEL_CHAR_K;
6109 case 0x1656: // (green steel)
6110 element = EL_STEEL_CHAR_L;
6113 case 0x1657: // (green steel)
6114 element = EL_STEEL_CHAR_M;
6117 case 0x1658: // (green steel)
6118 element = EL_STEEL_CHAR_N;
6121 case 0x1659: // (green steel)
6122 element = EL_STEEL_CHAR_O;
6125 case 0x165a: // (green steel)
6126 element = EL_STEEL_CHAR_P;
6129 case 0x165b: // (green steel)
6130 element = EL_STEEL_CHAR_Q;
6133 case 0x165c: // (green steel)
6134 element = EL_STEEL_CHAR_R;
6137 case 0x165d: // (green steel)
6138 element = EL_STEEL_CHAR_S;
6141 case 0x165e: // (green steel)
6142 element = EL_STEEL_CHAR_T;
6145 case 0x165f: // (green steel)
6146 element = EL_STEEL_CHAR_U;
6149 case 0x1660: // (green steel)
6150 element = EL_STEEL_CHAR_V;
6153 case 0x1661: // (green steel)
6154 element = EL_STEEL_CHAR_W;
6157 case 0x1662: // (green steel)
6158 element = EL_STEEL_CHAR_X;
6161 case 0x1663: // (green steel)
6162 element = EL_STEEL_CHAR_Y;
6165 case 0x1664: // (green steel)
6166 element = EL_STEEL_CHAR_Z;
6169 case 0x1665: // (green steel)
6170 element = EL_STEEL_CHAR_AUMLAUT;
6173 case 0x1666: // (green steel)
6174 element = EL_STEEL_CHAR_OUMLAUT;
6177 case 0x1667: // (green steel)
6178 element = EL_STEEL_CHAR_UUMLAUT;
6181 case 0x1668: // (green steel)
6182 element = EL_STEEL_CHAR_0;
6185 case 0x1669: // (green steel)
6186 element = EL_STEEL_CHAR_1;
6189 case 0x166a: // (green steel)
6190 element = EL_STEEL_CHAR_2;
6193 case 0x166b: // (green steel)
6194 element = EL_STEEL_CHAR_3;
6197 case 0x166c: // (green steel)
6198 element = EL_STEEL_CHAR_4;
6201 case 0x166d: // (green steel)
6202 element = EL_STEEL_CHAR_5;
6205 case 0x166e: // (green steel)
6206 element = EL_STEEL_CHAR_6;
6209 case 0x166f: // (green steel)
6210 element = EL_STEEL_CHAR_7;
6213 case 0x1670: // (green steel)
6214 element = EL_STEEL_CHAR_8;
6217 case 0x1671: // (green steel)
6218 element = EL_STEEL_CHAR_9;
6221 case 0x1672: // (green steel)
6222 element = EL_STEEL_CHAR_PERIOD;
6225 case 0x1673: // (green steel)
6226 element = EL_STEEL_CHAR_EXCLAM;
6229 case 0x1674: // (green steel)
6230 element = EL_STEEL_CHAR_COLON;
6233 case 0x1675: // (green steel)
6234 element = EL_STEEL_CHAR_LESS;
6237 case 0x1676: // (green steel)
6238 element = EL_STEEL_CHAR_GREATER;
6241 case 0x1677: // (green steel)
6242 element = EL_STEEL_CHAR_QUESTION;
6245 case 0x1678: // (green steel)
6246 element = EL_STEEL_CHAR_COPYRIGHT;
6249 case 0x1679: // (green steel)
6250 element = EL_STEEL_CHAR_UP;
6253 case 0x167a: // (green steel)
6254 element = EL_STEEL_CHAR_DOWN;
6257 case 0x167b: // (green steel)
6258 element = EL_STEEL_CHAR_BUTTON;
6261 case 0x167c: // (green steel)
6262 element = EL_STEEL_CHAR_PLUS;
6265 case 0x167d: // (green steel)
6266 element = EL_STEEL_CHAR_MINUS;
6269 case 0x167e: // (green steel)
6270 element = EL_STEEL_CHAR_APOSTROPHE;
6273 case 0x167f: // (green steel)
6274 element = EL_STEEL_CHAR_PARENLEFT;
6277 case 0x1680: // (green steel)
6278 element = EL_STEEL_CHAR_PARENRIGHT;
6281 case 0x1681: // gate (red)
6282 element = EL_EM_GATE_1;
6285 case 0x1682: // secret gate (red)
6286 element = EL_EM_GATE_1_GRAY;
6289 case 0x1683: // gate (yellow)
6290 element = EL_EM_GATE_2;
6293 case 0x1684: // secret gate (yellow)
6294 element = EL_EM_GATE_2_GRAY;
6297 case 0x1685: // gate (blue)
6298 element = EL_EM_GATE_4;
6301 case 0x1686: // secret gate (blue)
6302 element = EL_EM_GATE_4_GRAY;
6305 case 0x1687: // gate (green)
6306 element = EL_EM_GATE_3;
6309 case 0x1688: // secret gate (green)
6310 element = EL_EM_GATE_3_GRAY;
6313 case 0x1689: // gate (white)
6314 element = EL_DC_GATE_WHITE;
6317 case 0x168a: // secret gate (white)
6318 element = EL_DC_GATE_WHITE_GRAY;
6321 case 0x168b: // secret gate (no key)
6322 element = EL_DC_GATE_FAKE_GRAY;
6326 element = EL_ROBOT_WHEEL;
6330 element = EL_DC_TIMEGATE_SWITCH;
6334 element = EL_ACID_POOL_BOTTOM;
6338 element = EL_ACID_POOL_TOPLEFT;
6342 element = EL_ACID_POOL_TOPRIGHT;
6346 element = EL_ACID_POOL_BOTTOMLEFT;
6350 element = EL_ACID_POOL_BOTTOMRIGHT;
6354 element = EL_STEELWALL;
6358 element = EL_STEELWALL_SLIPPERY;
6361 case 0x1695: // steel wall (not round)
6362 element = EL_STEELWALL;
6365 case 0x1696: // steel wall (left)
6366 element = EL_DC_STEELWALL_1_LEFT;
6369 case 0x1697: // steel wall (bottom)
6370 element = EL_DC_STEELWALL_1_BOTTOM;
6373 case 0x1698: // steel wall (right)
6374 element = EL_DC_STEELWALL_1_RIGHT;
6377 case 0x1699: // steel wall (top)
6378 element = EL_DC_STEELWALL_1_TOP;
6381 case 0x169a: // steel wall (left/bottom)
6382 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6385 case 0x169b: // steel wall (right/bottom)
6386 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6389 case 0x169c: // steel wall (right/top)
6390 element = EL_DC_STEELWALL_1_TOPRIGHT;
6393 case 0x169d: // steel wall (left/top)
6394 element = EL_DC_STEELWALL_1_TOPLEFT;
6397 case 0x169e: // steel wall (right/bottom small)
6398 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6401 case 0x169f: // steel wall (left/bottom small)
6402 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6405 case 0x16a0: // steel wall (right/top small)
6406 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6409 case 0x16a1: // steel wall (left/top small)
6410 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6413 case 0x16a2: // steel wall (left/right)
6414 element = EL_DC_STEELWALL_1_VERTICAL;
6417 case 0x16a3: // steel wall (top/bottom)
6418 element = EL_DC_STEELWALL_1_HORIZONTAL;
6421 case 0x16a4: // steel wall 2 (left end)
6422 element = EL_DC_STEELWALL_2_LEFT;
6425 case 0x16a5: // steel wall 2 (right end)
6426 element = EL_DC_STEELWALL_2_RIGHT;
6429 case 0x16a6: // steel wall 2 (top end)
6430 element = EL_DC_STEELWALL_2_TOP;
6433 case 0x16a7: // steel wall 2 (bottom end)
6434 element = EL_DC_STEELWALL_2_BOTTOM;
6437 case 0x16a8: // steel wall 2 (left/right)
6438 element = EL_DC_STEELWALL_2_HORIZONTAL;
6441 case 0x16a9: // steel wall 2 (up/down)
6442 element = EL_DC_STEELWALL_2_VERTICAL;
6445 case 0x16aa: // steel wall 2 (mid)
6446 element = EL_DC_STEELWALL_2_MIDDLE;
6450 element = EL_SIGN_EXCLAMATION;
6454 element = EL_SIGN_RADIOACTIVITY;
6458 element = EL_SIGN_STOP;
6462 element = EL_SIGN_WHEELCHAIR;
6466 element = EL_SIGN_PARKING;
6470 element = EL_SIGN_NO_ENTRY;
6474 element = EL_SIGN_HEART;
6478 element = EL_SIGN_GIVE_WAY;
6482 element = EL_SIGN_ENTRY_FORBIDDEN;
6486 element = EL_SIGN_EMERGENCY_EXIT;
6490 element = EL_SIGN_YIN_YANG;
6494 element = EL_WALL_EMERALD;
6498 element = EL_WALL_DIAMOND;
6502 element = EL_WALL_PEARL;
6506 element = EL_WALL_CRYSTAL;
6510 element = EL_INVISIBLE_WALL;
6514 element = EL_INVISIBLE_STEELWALL;
6518 // EL_INVISIBLE_SAND
6521 element = EL_LIGHT_SWITCH;
6525 element = EL_ENVELOPE_1;
6529 if (element >= 0x0117 && element <= 0x036e) // (?)
6530 element = EL_DIAMOND;
6531 else if (element >= 0x042d && element <= 0x0684) // (?)
6532 element = EL_EMERALD;
6533 else if (element >= 0x157c && element <= 0x158b)
6535 else if (element >= 0x1590 && element <= 0x159f)
6536 element = EL_DC_LANDMINE;
6537 else if (element >= 0x16bc && element <= 0x16cb)
6538 element = EL_INVISIBLE_SAND;
6541 Warn("unknown Diamond Caves element 0x%04x", element);
6543 element = EL_UNKNOWN;
6548 return getMappedElement(element);
6551 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6553 byte header[DC_LEVEL_HEADER_SIZE];
6555 int envelope_header_pos = 62;
6556 int envelope_content_pos = 94;
6557 int level_name_pos = 251;
6558 int level_author_pos = 292;
6559 int envelope_header_len;
6560 int envelope_content_len;
6562 int level_author_len;
6564 int num_yamyam_contents;
6567 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6569 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6571 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6573 header[i * 2 + 0] = header_word >> 8;
6574 header[i * 2 + 1] = header_word & 0xff;
6577 // read some values from level header to check level decoding integrity
6578 fieldx = header[6] | (header[7] << 8);
6579 fieldy = header[8] | (header[9] << 8);
6580 num_yamyam_contents = header[60] | (header[61] << 8);
6582 // do some simple sanity checks to ensure that level was correctly decoded
6583 if (fieldx < 1 || fieldx > 256 ||
6584 fieldy < 1 || fieldy > 256 ||
6585 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6587 level->no_valid_file = TRUE;
6589 Warn("cannot decode level from stream -- using empty level");
6594 // maximum envelope header size is 31 bytes
6595 envelope_header_len = header[envelope_header_pos];
6596 // maximum envelope content size is 110 (156?) bytes
6597 envelope_content_len = header[envelope_content_pos];
6599 // maximum level title size is 40 bytes
6600 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6601 // maximum level author size is 30 (51?) bytes
6602 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6606 for (i = 0; i < envelope_header_len; i++)
6607 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6608 level->envelope[0].text[envelope_size++] =
6609 header[envelope_header_pos + 1 + i];
6611 if (envelope_header_len > 0 && envelope_content_len > 0)
6613 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6614 level->envelope[0].text[envelope_size++] = '\n';
6615 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6616 level->envelope[0].text[envelope_size++] = '\n';
6619 for (i = 0; i < envelope_content_len; i++)
6620 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6621 level->envelope[0].text[envelope_size++] =
6622 header[envelope_content_pos + 1 + i];
6624 level->envelope[0].text[envelope_size] = '\0';
6626 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6627 level->envelope[0].ysize = 10;
6628 level->envelope[0].autowrap = TRUE;
6629 level->envelope[0].centered = TRUE;
6631 for (i = 0; i < level_name_len; i++)
6632 level->name[i] = header[level_name_pos + 1 + i];
6633 level->name[level_name_len] = '\0';
6635 for (i = 0; i < level_author_len; i++)
6636 level->author[i] = header[level_author_pos + 1 + i];
6637 level->author[level_author_len] = '\0';
6639 num_yamyam_contents = header[60] | (header[61] << 8);
6640 level->num_yamyam_contents =
6641 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6643 for (i = 0; i < num_yamyam_contents; i++)
6645 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6647 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6648 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6650 if (i < MAX_ELEMENT_CONTENTS)
6651 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6655 fieldx = header[6] | (header[7] << 8);
6656 fieldy = header[8] | (header[9] << 8);
6657 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6658 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6660 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6662 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6663 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6665 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6666 level->field[x][y] = getMappedElement_DC(element_dc);
6669 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6670 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6671 level->field[x][y] = EL_PLAYER_1;
6673 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6674 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6675 level->field[x][y] = EL_PLAYER_2;
6677 level->gems_needed = header[18] | (header[19] << 8);
6679 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6680 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6681 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6682 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6683 level->score[SC_NUT] = header[28] | (header[29] << 8);
6684 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6685 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6686 level->score[SC_BUG] = header[34] | (header[35] << 8);
6687 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6688 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6689 level->score[SC_KEY] = header[40] | (header[41] << 8);
6690 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6692 level->time = header[44] | (header[45] << 8);
6694 level->amoeba_speed = header[46] | (header[47] << 8);
6695 level->time_light = header[48] | (header[49] << 8);
6696 level->time_timegate = header[50] | (header[51] << 8);
6697 level->time_wheel = header[52] | (header[53] << 8);
6698 level->time_magic_wall = header[54] | (header[55] << 8);
6699 level->extra_time = header[56] | (header[57] << 8);
6700 level->shield_normal_time = header[58] | (header[59] << 8);
6702 // shield and extra time elements do not have a score
6703 level->score[SC_SHIELD] = 0;
6704 level->extra_time_score = 0;
6706 // set time for normal and deadly shields to the same value
6707 level->shield_deadly_time = level->shield_normal_time;
6709 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6710 // can slip down from flat walls, like normal walls and steel walls
6711 level->em_slippery_gems = TRUE;
6713 // time score is counted for each 10 seconds left in Diamond Caves levels
6714 level->time_score_base = 10;
6717 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6718 struct LevelFileInfo *level_file_info,
6719 boolean level_info_only)
6721 char *filename = level_file_info->filename;
6723 int num_magic_bytes = 8;
6724 char magic_bytes[num_magic_bytes + 1];
6725 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6727 if (!(file = openFile(filename, MODE_READ)))
6729 level->no_valid_file = TRUE;
6731 if (!level_info_only)
6732 Warn("cannot read level '%s' -- using empty level", filename);
6737 // fseek(file, 0x0000, SEEK_SET);
6739 if (level_file_info->packed)
6741 // read "magic bytes" from start of file
6742 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6743 magic_bytes[0] = '\0';
6745 // check "magic bytes" for correct file format
6746 if (!strPrefix(magic_bytes, "DC2"))
6748 level->no_valid_file = TRUE;
6750 Warn("unknown DC level file '%s' -- using empty level", filename);
6755 if (strPrefix(magic_bytes, "DC2Win95") ||
6756 strPrefix(magic_bytes, "DC2Win98"))
6758 int position_first_level = 0x00fa;
6759 int extra_bytes = 4;
6762 // advance file stream to first level inside the level package
6763 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6765 // each block of level data is followed by block of non-level data
6766 num_levels_to_skip *= 2;
6768 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6769 while (num_levels_to_skip >= 0)
6771 // advance file stream to next level inside the level package
6772 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6774 level->no_valid_file = TRUE;
6776 Warn("cannot fseek in file '%s' -- using empty level", filename);
6781 // skip apparently unused extra bytes following each level
6782 ReadUnusedBytesFromFile(file, extra_bytes);
6784 // read size of next level in level package
6785 skip_bytes = getFile32BitLE(file);
6787 num_levels_to_skip--;
6792 level->no_valid_file = TRUE;
6794 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6800 LoadLevelFromFileStream_DC(file, level);
6806 // ----------------------------------------------------------------------------
6807 // functions for loading SB level
6808 // ----------------------------------------------------------------------------
6810 int getMappedElement_SB(int element_ascii, boolean use_ces)
6818 sb_element_mapping[] =
6820 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6821 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6822 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6823 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6824 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6825 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6826 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6827 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6834 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6835 if (element_ascii == sb_element_mapping[i].ascii)
6836 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6838 return EL_UNDEFINED;
6841 static void SetLevelSettings_SB(struct LevelInfo *level)
6845 level->use_step_counter = TRUE;
6848 level->score[SC_TIME_BONUS] = 0;
6849 level->time_score_base = 1;
6850 level->rate_time_over_score = TRUE;
6853 level->auto_exit_sokoban = TRUE;
6856 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6857 struct LevelFileInfo *level_file_info,
6858 boolean level_info_only)
6860 char *filename = level_file_info->filename;
6861 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6862 char last_comment[MAX_LINE_LEN];
6863 char level_name[MAX_LINE_LEN];
6866 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6867 boolean read_continued_line = FALSE;
6868 boolean reading_playfield = FALSE;
6869 boolean got_valid_playfield_line = FALSE;
6870 boolean invalid_playfield_char = FALSE;
6871 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6872 int file_level_nr = 0;
6873 int x = 0, y = 0; // initialized to make compilers happy
6875 last_comment[0] = '\0';
6876 level_name[0] = '\0';
6878 if (!(file = openFile(filename, MODE_READ)))
6880 level->no_valid_file = TRUE;
6882 if (!level_info_only)
6883 Warn("cannot read level '%s' -- using empty level", filename);
6888 while (!checkEndOfFile(file))
6890 // level successfully read, but next level may follow here
6891 if (!got_valid_playfield_line && reading_playfield)
6893 // read playfield from single level file -- skip remaining file
6894 if (!level_file_info->packed)
6897 if (file_level_nr >= num_levels_to_skip)
6902 last_comment[0] = '\0';
6903 level_name[0] = '\0';
6905 reading_playfield = FALSE;
6908 got_valid_playfield_line = FALSE;
6910 // read next line of input file
6911 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6914 // cut trailing line break (this can be newline and/or carriage return)
6915 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6916 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6919 // copy raw input line for later use (mainly debugging output)
6920 strcpy(line_raw, line);
6922 if (read_continued_line)
6924 // append new line to existing line, if there is enough space
6925 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6926 strcat(previous_line, line_ptr);
6928 strcpy(line, previous_line); // copy storage buffer to line
6930 read_continued_line = FALSE;
6933 // if the last character is '\', continue at next line
6934 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6936 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6937 strcpy(previous_line, line); // copy line to storage buffer
6939 read_continued_line = TRUE;
6945 if (line[0] == '\0')
6948 // extract comment text from comment line
6951 for (line_ptr = line; *line_ptr; line_ptr++)
6952 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6955 strcpy(last_comment, line_ptr);
6960 // extract level title text from line containing level title
6961 if (line[0] == '\'')
6963 strcpy(level_name, &line[1]);
6965 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6966 level_name[strlen(level_name) - 1] = '\0';
6971 // skip lines containing only spaces (or empty lines)
6972 for (line_ptr = line; *line_ptr; line_ptr++)
6973 if (*line_ptr != ' ')
6975 if (*line_ptr == '\0')
6978 // at this point, we have found a line containing part of a playfield
6980 got_valid_playfield_line = TRUE;
6982 if (!reading_playfield)
6984 reading_playfield = TRUE;
6985 invalid_playfield_char = FALSE;
6987 for (x = 0; x < MAX_LEV_FIELDX; x++)
6988 for (y = 0; y < MAX_LEV_FIELDY; y++)
6989 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6994 // start with topmost tile row
6998 // skip playfield line if larger row than allowed
6999 if (y >= MAX_LEV_FIELDY)
7002 // start with leftmost tile column
7005 // read playfield elements from line
7006 for (line_ptr = line; *line_ptr; line_ptr++)
7008 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7010 // stop parsing playfield line if larger column than allowed
7011 if (x >= MAX_LEV_FIELDX)
7014 if (mapped_sb_element == EL_UNDEFINED)
7016 invalid_playfield_char = TRUE;
7021 level->field[x][y] = mapped_sb_element;
7023 // continue with next tile column
7026 level->fieldx = MAX(x, level->fieldx);
7029 if (invalid_playfield_char)
7031 // if first playfield line, treat invalid lines as comment lines
7033 reading_playfield = FALSE;
7038 // continue with next tile row
7046 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7047 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7049 if (!reading_playfield)
7051 level->no_valid_file = TRUE;
7053 Warn("cannot read level '%s' -- using empty level", filename);
7058 if (*level_name != '\0')
7060 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7061 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7063 else if (*last_comment != '\0')
7065 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7066 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7070 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7073 // set all empty fields beyond the border walls to invisible steel wall
7074 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7076 if ((x == 0 || x == level->fieldx - 1 ||
7077 y == 0 || y == level->fieldy - 1) &&
7078 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7079 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7080 level->field, level->fieldx, level->fieldy);
7083 // set special level settings for Sokoban levels
7084 SetLevelSettings_SB(level);
7086 if (load_xsb_to_ces)
7088 // special global settings can now be set in level template
7089 level->use_custom_template = TRUE;
7094 // -------------------------------------------------------------------------
7095 // functions for handling native levels
7096 // -------------------------------------------------------------------------
7098 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7099 struct LevelFileInfo *level_file_info,
7100 boolean level_info_only)
7104 // determine position of requested level inside level package
7105 if (level_file_info->packed)
7106 pos = level_file_info->nr - leveldir_current->first_level;
7108 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7109 level->no_valid_file = TRUE;
7112 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7113 struct LevelFileInfo *level_file_info,
7114 boolean level_info_only)
7116 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7117 level->no_valid_file = TRUE;
7120 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7121 struct LevelFileInfo *level_file_info,
7122 boolean level_info_only)
7126 // determine position of requested level inside level package
7127 if (level_file_info->packed)
7128 pos = level_file_info->nr - leveldir_current->first_level;
7130 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7131 level->no_valid_file = TRUE;
7134 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7135 struct LevelFileInfo *level_file_info,
7136 boolean level_info_only)
7138 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7139 level->no_valid_file = TRUE;
7142 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7144 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7145 CopyNativeLevel_RND_to_BD(level);
7146 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7147 CopyNativeLevel_RND_to_EM(level);
7148 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7149 CopyNativeLevel_RND_to_SP(level);
7150 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7151 CopyNativeLevel_RND_to_MM(level);
7154 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7156 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7157 CopyNativeLevel_BD_to_RND(level);
7158 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7159 CopyNativeLevel_EM_to_RND(level);
7160 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7161 CopyNativeLevel_SP_to_RND(level);
7162 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7163 CopyNativeLevel_MM_to_RND(level);
7166 void SaveNativeLevel(struct LevelInfo *level)
7168 // saving native level files only supported for some game engines
7169 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7170 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7173 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7174 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7175 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7176 char *filename = getLevelFilenameFromBasename(basename);
7178 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7181 boolean success = FALSE;
7183 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7185 CopyNativeLevel_RND_to_BD(level);
7186 // CopyNativeTape_RND_to_BD(level);
7188 success = SaveNativeLevel_BD(filename);
7190 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7192 CopyNativeLevel_RND_to_SP(level);
7193 CopyNativeTape_RND_to_SP(level);
7195 success = SaveNativeLevel_SP(filename);
7199 Request("Native level file saved!", REQ_CONFIRM);
7201 Request("Failed to save native level file!", REQ_CONFIRM);
7205 // ----------------------------------------------------------------------------
7206 // functions for loading generic level
7207 // ----------------------------------------------------------------------------
7209 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7210 struct LevelFileInfo *level_file_info,
7211 boolean level_info_only)
7213 // always start with reliable default values
7214 setLevelInfoToDefaults(level, level_info_only, TRUE);
7216 switch (level_file_info->type)
7218 case LEVEL_FILE_TYPE_RND:
7219 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7222 case LEVEL_FILE_TYPE_BD:
7223 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7224 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7227 case LEVEL_FILE_TYPE_EM:
7228 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7229 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7232 case LEVEL_FILE_TYPE_SP:
7233 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7234 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7237 case LEVEL_FILE_TYPE_MM:
7238 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7239 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7242 case LEVEL_FILE_TYPE_DC:
7243 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7246 case LEVEL_FILE_TYPE_SB:
7247 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7251 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7255 // if level file is invalid, restore level structure to default values
7256 if (level->no_valid_file)
7257 setLevelInfoToDefaults(level, level_info_only, FALSE);
7259 if (check_special_flags("use_native_bd_game_engine"))
7260 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7262 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7263 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7265 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7266 CopyNativeLevel_Native_to_RND(level);
7269 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7271 static struct LevelFileInfo level_file_info;
7273 // always start with reliable default values
7274 setFileInfoToDefaults(&level_file_info);
7276 level_file_info.nr = 0; // unknown level number
7277 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7279 setString(&level_file_info.filename, filename);
7281 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7284 static void LoadLevel_InitVersion(struct LevelInfo *level)
7288 if (leveldir_current == NULL) // only when dumping level
7291 // all engine modifications also valid for levels which use latest engine
7292 if (level->game_version < VERSION_IDENT(3,2,0,5))
7294 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7295 level->time_score_base = 10;
7298 if (leveldir_current->latest_engine)
7300 // ---------- use latest game engine --------------------------------------
7302 /* For all levels which are forced to use the latest game engine version
7303 (normally all but user contributed, private and undefined levels), set
7304 the game engine version to the actual version; this allows for actual
7305 corrections in the game engine to take effect for existing, converted
7306 levels (from "classic" or other existing games) to make the emulation
7307 of the corresponding game more accurate, while (hopefully) not breaking
7308 existing levels created from other players. */
7310 level->game_version = GAME_VERSION_ACTUAL;
7312 /* Set special EM style gems behaviour: EM style gems slip down from
7313 normal, steel and growing wall. As this is a more fundamental change,
7314 it seems better to set the default behaviour to "off" (as it is more
7315 natural) and make it configurable in the level editor (as a property
7316 of gem style elements). Already existing converted levels (neither
7317 private nor contributed levels) are changed to the new behaviour. */
7319 if (level->file_version < FILE_VERSION_2_0)
7320 level->em_slippery_gems = TRUE;
7325 // ---------- use game engine the level was created with --------------------
7327 /* For all levels which are not forced to use the latest game engine
7328 version (normally user contributed, private and undefined levels),
7329 use the version of the game engine the levels were created for.
7331 Since 2.0.1, the game engine version is now directly stored
7332 in the level file (chunk "VERS"), so there is no need anymore
7333 to set the game version from the file version (except for old,
7334 pre-2.0 levels, where the game version is still taken from the
7335 file format version used to store the level -- see above). */
7337 // player was faster than enemies in 1.0.0 and before
7338 if (level->file_version == FILE_VERSION_1_0)
7339 for (i = 0; i < MAX_PLAYERS; i++)
7340 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7342 // default behaviour for EM style gems was "slippery" only in 2.0.1
7343 if (level->game_version == VERSION_IDENT(2,0,1,0))
7344 level->em_slippery_gems = TRUE;
7346 // springs could be pushed over pits before (pre-release version) 2.2.0
7347 if (level->game_version < VERSION_IDENT(2,2,0,0))
7348 level->use_spring_bug = TRUE;
7350 if (level->game_version < VERSION_IDENT(3,2,0,5))
7352 // time orb caused limited time in endless time levels before 3.2.0-5
7353 level->use_time_orb_bug = TRUE;
7355 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7356 level->block_snap_field = FALSE;
7358 // extra time score was same value as time left score before 3.2.0-5
7359 level->extra_time_score = level->score[SC_TIME_BONUS];
7362 if (level->game_version < VERSION_IDENT(3,2,0,7))
7364 // default behaviour for snapping was "not continuous" before 3.2.0-7
7365 level->continuous_snapping = FALSE;
7368 // only few elements were able to actively move into acid before 3.1.0
7369 // trigger settings did not exist before 3.1.0; set to default "any"
7370 if (level->game_version < VERSION_IDENT(3,1,0,0))
7372 // correct "can move into acid" settings (all zero in old levels)
7374 level->can_move_into_acid_bits = 0; // nothing can move into acid
7375 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7377 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7378 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7379 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7380 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7382 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7383 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7385 // correct trigger settings (stored as zero == "none" in old levels)
7387 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7389 int element = EL_CUSTOM_START + i;
7390 struct ElementInfo *ei = &element_info[element];
7392 for (j = 0; j < ei->num_change_pages; j++)
7394 struct ElementChangeInfo *change = &ei->change_page[j];
7396 change->trigger_player = CH_PLAYER_ANY;
7397 change->trigger_page = CH_PAGE_ANY;
7402 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7404 int element = EL_CUSTOM_256;
7405 struct ElementInfo *ei = &element_info[element];
7406 struct ElementChangeInfo *change = &ei->change_page[0];
7408 /* This is needed to fix a problem that was caused by a bugfix in function
7409 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7410 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7411 not replace walkable elements, but instead just placed the player on it,
7412 without placing the Sokoban field under the player). Unfortunately, this
7413 breaks "Snake Bite" style levels when the snake is halfway through a door
7414 that just closes (the snake head is still alive and can be moved in this
7415 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7416 player (without Sokoban element) which then gets killed as designed). */
7418 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7419 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7420 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7421 change->target_element = EL_PLAYER_1;
7424 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7425 if (level->game_version < VERSION_IDENT(3,2,5,0))
7427 /* This is needed to fix a problem that was caused by a bugfix in function
7428 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7429 corrects the behaviour when a custom element changes to another custom
7430 element with a higher element number that has change actions defined.
7431 Normally, only one change per frame is allowed for custom elements.
7432 Therefore, it is checked if a custom element already changed in the
7433 current frame; if it did, subsequent changes are suppressed.
7434 Unfortunately, this is only checked for element changes, but not for
7435 change actions, which are still executed. As the function above loops
7436 through all custom elements from lower to higher, an element change
7437 resulting in a lower CE number won't be checked again, while a target
7438 element with a higher number will also be checked, and potential change
7439 actions will get executed for this CE, too (which is wrong), while
7440 further changes are ignored (which is correct). As this bugfix breaks
7441 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7442 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7443 behaviour for existing levels and tapes that make use of this bug */
7445 level->use_action_after_change_bug = TRUE;
7448 // not centering level after relocating player was default only in 3.2.3
7449 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7450 level->shifted_relocation = TRUE;
7452 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7453 if (level->game_version < VERSION_IDENT(3,2,6,0))
7454 level->em_explodes_by_fire = TRUE;
7456 // levels were solved by the first player entering an exit up to 4.1.0.0
7457 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7458 level->solved_by_one_player = TRUE;
7460 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7461 if (level->game_version < VERSION_IDENT(4,1,1,1))
7462 level->use_life_bugs = TRUE;
7464 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7465 if (level->game_version < VERSION_IDENT(4,1,1,1))
7466 level->sb_objects_needed = FALSE;
7468 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7469 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7470 level->finish_dig_collect = FALSE;
7472 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7473 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7474 level->keep_walkable_ce = TRUE;
7477 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7479 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7482 // check if this level is (not) a Sokoban level
7483 for (y = 0; y < level->fieldy; y++)
7484 for (x = 0; x < level->fieldx; x++)
7485 if (!IS_SB_ELEMENT(Tile[x][y]))
7486 is_sokoban_level = FALSE;
7488 if (is_sokoban_level)
7490 // set special level settings for Sokoban levels
7491 SetLevelSettings_SB(level);
7495 static void LoadLevel_InitSettings(struct LevelInfo *level)
7497 // adjust level settings for (non-native) Sokoban-style levels
7498 LoadLevel_InitSettings_SB(level);
7500 // rename levels with title "nameless level" or if renaming is forced
7501 if (leveldir_current->empty_level_name != NULL &&
7502 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7503 leveldir_current->force_level_name))
7504 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7505 leveldir_current->empty_level_name, level_nr);
7508 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7512 // map elements that have changed in newer versions
7513 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7514 level->game_version);
7515 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7516 for (x = 0; x < 3; x++)
7517 for (y = 0; y < 3; y++)
7518 level->yamyam_content[i].e[x][y] =
7519 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7520 level->game_version);
7524 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7528 // map custom element change events that have changed in newer versions
7529 // (these following values were accidentally changed in version 3.0.1)
7530 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7531 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7533 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7535 int element = EL_CUSTOM_START + i;
7537 // order of checking and copying events to be mapped is important
7538 // (do not change the start and end value -- they are constant)
7539 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7541 if (HAS_CHANGE_EVENT(element, j - 2))
7543 SET_CHANGE_EVENT(element, j - 2, FALSE);
7544 SET_CHANGE_EVENT(element, j, TRUE);
7548 // order of checking and copying events to be mapped is important
7549 // (do not change the start and end value -- they are constant)
7550 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7552 if (HAS_CHANGE_EVENT(element, j - 1))
7554 SET_CHANGE_EVENT(element, j - 1, FALSE);
7555 SET_CHANGE_EVENT(element, j, TRUE);
7561 // initialize "can_change" field for old levels with only one change page
7562 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7564 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7566 int element = EL_CUSTOM_START + i;
7568 if (CAN_CHANGE(element))
7569 element_info[element].change->can_change = TRUE;
7573 // correct custom element values (for old levels without these options)
7574 if (level->game_version < VERSION_IDENT(3,1,1,0))
7576 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7578 int element = EL_CUSTOM_START + i;
7579 struct ElementInfo *ei = &element_info[element];
7581 if (ei->access_direction == MV_NO_DIRECTION)
7582 ei->access_direction = MV_ALL_DIRECTIONS;
7586 // correct custom element values (fix invalid values for all versions)
7589 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7591 int element = EL_CUSTOM_START + i;
7592 struct ElementInfo *ei = &element_info[element];
7594 for (j = 0; j < ei->num_change_pages; j++)
7596 struct ElementChangeInfo *change = &ei->change_page[j];
7598 if (change->trigger_player == CH_PLAYER_NONE)
7599 change->trigger_player = CH_PLAYER_ANY;
7601 if (change->trigger_side == CH_SIDE_NONE)
7602 change->trigger_side = CH_SIDE_ANY;
7607 // initialize "can_explode" field for old levels which did not store this
7608 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7609 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7611 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7613 int element = EL_CUSTOM_START + i;
7615 if (EXPLODES_1X1_OLD(element))
7616 element_info[element].explosion_type = EXPLODES_1X1;
7618 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7619 EXPLODES_SMASHED(element) ||
7620 EXPLODES_IMPACT(element)));
7624 // correct previously hard-coded move delay values for maze runner style
7625 if (level->game_version < VERSION_IDENT(3,1,1,0))
7627 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7629 int element = EL_CUSTOM_START + i;
7631 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7633 // previously hard-coded and therefore ignored
7634 element_info[element].move_delay_fixed = 9;
7635 element_info[element].move_delay_random = 0;
7640 // set some other uninitialized values of custom elements in older levels
7641 if (level->game_version < VERSION_IDENT(3,1,0,0))
7643 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7645 int element = EL_CUSTOM_START + i;
7647 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7649 element_info[element].explosion_delay = 17;
7650 element_info[element].ignition_delay = 8;
7654 // set mouse click change events to work for left/middle/right mouse button
7655 if (level->game_version < VERSION_IDENT(4,2,3,0))
7657 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7659 int element = EL_CUSTOM_START + i;
7660 struct ElementInfo *ei = &element_info[element];
7662 for (j = 0; j < ei->num_change_pages; j++)
7664 struct ElementChangeInfo *change = &ei->change_page[j];
7666 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7667 change->has_event[CE_PRESSED_BY_MOUSE] ||
7668 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7669 change->has_event[CE_MOUSE_PRESSED_ON_X])
7670 change->trigger_side = CH_SIDE_ANY;
7676 static void LoadLevel_InitElements(struct LevelInfo *level)
7678 LoadLevel_InitStandardElements(level);
7680 if (level->file_has_custom_elements)
7681 LoadLevel_InitCustomElements(level);
7683 // initialize element properties for level editor etc.
7684 InitElementPropertiesEngine(level->game_version);
7685 InitElementPropertiesGfxElement();
7688 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7692 // map elements that have changed in newer versions
7693 for (y = 0; y < level->fieldy; y++)
7694 for (x = 0; x < level->fieldx; x++)
7695 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7696 level->game_version);
7698 // clear unused playfield data (nicer if level gets resized in editor)
7699 for (x = 0; x < MAX_LEV_FIELDX; x++)
7700 for (y = 0; y < MAX_LEV_FIELDY; y++)
7701 if (x >= level->fieldx || y >= level->fieldy)
7702 level->field[x][y] = EL_EMPTY;
7704 // copy elements to runtime playfield array
7705 for (x = 0; x < MAX_LEV_FIELDX; x++)
7706 for (y = 0; y < MAX_LEV_FIELDY; y++)
7707 Tile[x][y] = level->field[x][y];
7709 // initialize level size variables for faster access
7710 lev_fieldx = level->fieldx;
7711 lev_fieldy = level->fieldy;
7713 // determine border element for this level
7714 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7715 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7720 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7722 struct LevelFileInfo *level_file_info = &level->file_info;
7724 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7725 CopyNativeLevel_RND_to_Native(level);
7728 static void LoadLevelTemplate_LoadAndInit(void)
7730 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7732 LoadLevel_InitVersion(&level_template);
7733 LoadLevel_InitElements(&level_template);
7734 LoadLevel_InitSettings(&level_template);
7736 ActivateLevelTemplate();
7739 void LoadLevelTemplate(int nr)
7741 if (!fileExists(getGlobalLevelTemplateFilename()))
7743 Warn("no level template found for this level");
7748 setLevelFileInfo(&level_template.file_info, nr);
7750 LoadLevelTemplate_LoadAndInit();
7753 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7755 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7757 LoadLevelTemplate_LoadAndInit();
7760 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7762 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7764 if (level.use_custom_template)
7766 if (network_level != NULL)
7767 LoadNetworkLevelTemplate(network_level);
7769 LoadLevelTemplate(-1);
7772 LoadLevel_InitVersion(&level);
7773 LoadLevel_InitElements(&level);
7774 LoadLevel_InitPlayfield(&level);
7775 LoadLevel_InitSettings(&level);
7777 LoadLevel_InitNativeEngines(&level);
7780 void LoadLevel(int nr)
7782 SetLevelSetInfo(leveldir_current->identifier, nr);
7784 setLevelFileInfo(&level.file_info, nr);
7786 LoadLevel_LoadAndInit(NULL);
7789 void LoadLevelInfoOnly(int nr)
7791 setLevelFileInfo(&level.file_info, nr);
7793 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7796 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7798 SetLevelSetInfo(network_level->leveldir_identifier,
7799 network_level->file_info.nr);
7801 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7803 LoadLevel_LoadAndInit(network_level);
7806 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7810 chunk_size += putFileVersion(file, level->file_version);
7811 chunk_size += putFileVersion(file, level->game_version);
7816 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7820 chunk_size += putFile16BitBE(file, level->creation_date.year);
7821 chunk_size += putFile8Bit(file, level->creation_date.month);
7822 chunk_size += putFile8Bit(file, level->creation_date.day);
7827 #if ENABLE_HISTORIC_CHUNKS
7828 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7832 putFile8Bit(file, level->fieldx);
7833 putFile8Bit(file, level->fieldy);
7835 putFile16BitBE(file, level->time);
7836 putFile16BitBE(file, level->gems_needed);
7838 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7839 putFile8Bit(file, level->name[i]);
7841 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7842 putFile8Bit(file, level->score[i]);
7844 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7845 for (y = 0; y < 3; y++)
7846 for (x = 0; x < 3; x++)
7847 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7848 level->yamyam_content[i].e[x][y]));
7849 putFile8Bit(file, level->amoeba_speed);
7850 putFile8Bit(file, level->time_magic_wall);
7851 putFile8Bit(file, level->time_wheel);
7852 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7853 level->amoeba_content));
7854 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7855 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7856 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7857 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7859 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7861 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7862 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7863 putFile32BitBE(file, level->can_move_into_acid_bits);
7864 putFile8Bit(file, level->dont_collide_with_bits);
7866 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7867 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7869 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7870 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7871 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7873 putFile8Bit(file, level->game_engine_type);
7875 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7879 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7884 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7885 chunk_size += putFile8Bit(file, level->name[i]);
7890 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7895 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7896 chunk_size += putFile8Bit(file, level->author[i]);
7901 #if ENABLE_HISTORIC_CHUNKS
7902 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7907 for (y = 0; y < level->fieldy; y++)
7908 for (x = 0; x < level->fieldx; x++)
7909 if (level->encoding_16bit_field)
7910 chunk_size += putFile16BitBE(file, level->field[x][y]);
7912 chunk_size += putFile8Bit(file, level->field[x][y]);
7918 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7923 for (y = 0; y < level->fieldy; y++)
7924 for (x = 0; x < level->fieldx; x++)
7925 chunk_size += putFile16BitBE(file, level->field[x][y]);
7930 #if ENABLE_HISTORIC_CHUNKS
7931 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7935 putFile8Bit(file, EL_YAMYAM);
7936 putFile8Bit(file, level->num_yamyam_contents);
7937 putFile8Bit(file, 0);
7938 putFile8Bit(file, 0);
7940 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7941 for (y = 0; y < 3; y++)
7942 for (x = 0; x < 3; x++)
7943 if (level->encoding_16bit_field)
7944 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7946 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7950 #if ENABLE_HISTORIC_CHUNKS
7951 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7954 int num_contents, content_xsize, content_ysize;
7955 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7957 if (element == EL_YAMYAM)
7959 num_contents = level->num_yamyam_contents;
7963 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7964 for (y = 0; y < 3; y++)
7965 for (x = 0; x < 3; x++)
7966 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7968 else if (element == EL_BD_AMOEBA)
7974 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7975 for (y = 0; y < 3; y++)
7976 for (x = 0; x < 3; x++)
7977 content_array[i][x][y] = EL_EMPTY;
7978 content_array[0][0][0] = level->amoeba_content;
7982 // chunk header already written -- write empty chunk data
7983 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7985 Warn("cannot save content for element '%d'", element);
7990 putFile16BitBE(file, element);
7991 putFile8Bit(file, num_contents);
7992 putFile8Bit(file, content_xsize);
7993 putFile8Bit(file, content_ysize);
7995 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7997 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7998 for (y = 0; y < 3; y++)
7999 for (x = 0; x < 3; x++)
8000 putFile16BitBE(file, content_array[i][x][y]);
8004 #if ENABLE_HISTORIC_CHUNKS
8005 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8007 int envelope_nr = element - EL_ENVELOPE_1;
8008 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8012 chunk_size += putFile16BitBE(file, element);
8013 chunk_size += putFile16BitBE(file, envelope_len);
8014 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8015 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8017 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8018 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8020 for (i = 0; i < envelope_len; i++)
8021 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8027 #if ENABLE_HISTORIC_CHUNKS
8028 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8029 int num_changed_custom_elements)
8033 putFile16BitBE(file, num_changed_custom_elements);
8035 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8037 int element = EL_CUSTOM_START + i;
8039 struct ElementInfo *ei = &element_info[element];
8041 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8043 if (check < num_changed_custom_elements)
8045 putFile16BitBE(file, element);
8046 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8053 if (check != num_changed_custom_elements) // should not happen
8054 Warn("inconsistent number of custom element properties");
8058 #if ENABLE_HISTORIC_CHUNKS
8059 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8060 int num_changed_custom_elements)
8064 putFile16BitBE(file, num_changed_custom_elements);
8066 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8068 int element = EL_CUSTOM_START + i;
8070 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8072 if (check < num_changed_custom_elements)
8074 putFile16BitBE(file, element);
8075 putFile16BitBE(file, element_info[element].change->target_element);
8082 if (check != num_changed_custom_elements) // should not happen
8083 Warn("inconsistent number of custom target elements");
8087 #if ENABLE_HISTORIC_CHUNKS
8088 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8089 int num_changed_custom_elements)
8091 int i, j, x, y, check = 0;
8093 putFile16BitBE(file, num_changed_custom_elements);
8095 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8097 int element = EL_CUSTOM_START + i;
8098 struct ElementInfo *ei = &element_info[element];
8100 if (ei->modified_settings)
8102 if (check < num_changed_custom_elements)
8104 putFile16BitBE(file, element);
8106 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8107 putFile8Bit(file, ei->description[j]);
8109 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8111 // some free bytes for future properties and padding
8112 WriteUnusedBytesToFile(file, 7);
8114 putFile8Bit(file, ei->use_gfx_element);
8115 putFile16BitBE(file, ei->gfx_element_initial);
8117 putFile8Bit(file, ei->collect_score_initial);
8118 putFile8Bit(file, ei->collect_count_initial);
8120 putFile16BitBE(file, ei->push_delay_fixed);
8121 putFile16BitBE(file, ei->push_delay_random);
8122 putFile16BitBE(file, ei->move_delay_fixed);
8123 putFile16BitBE(file, ei->move_delay_random);
8125 putFile16BitBE(file, ei->move_pattern);
8126 putFile8Bit(file, ei->move_direction_initial);
8127 putFile8Bit(file, ei->move_stepsize);
8129 for (y = 0; y < 3; y++)
8130 for (x = 0; x < 3; x++)
8131 putFile16BitBE(file, ei->content.e[x][y]);
8133 putFile32BitBE(file, ei->change->events);
8135 putFile16BitBE(file, ei->change->target_element);
8137 putFile16BitBE(file, ei->change->delay_fixed);
8138 putFile16BitBE(file, ei->change->delay_random);
8139 putFile16BitBE(file, ei->change->delay_frames);
8141 putFile16BitBE(file, ei->change->initial_trigger_element);
8143 putFile8Bit(file, ei->change->explode);
8144 putFile8Bit(file, ei->change->use_target_content);
8145 putFile8Bit(file, ei->change->only_if_complete);
8146 putFile8Bit(file, ei->change->use_random_replace);
8148 putFile8Bit(file, ei->change->random_percentage);
8149 putFile8Bit(file, ei->change->replace_when);
8151 for (y = 0; y < 3; y++)
8152 for (x = 0; x < 3; x++)
8153 putFile16BitBE(file, ei->change->content.e[x][y]);
8155 putFile8Bit(file, ei->slippery_type);
8157 // some free bytes for future properties and padding
8158 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8165 if (check != num_changed_custom_elements) // should not happen
8166 Warn("inconsistent number of custom element properties");
8170 #if ENABLE_HISTORIC_CHUNKS
8171 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8173 struct ElementInfo *ei = &element_info[element];
8176 // ---------- custom element base property values (96 bytes) ----------------
8178 putFile16BitBE(file, element);
8180 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8181 putFile8Bit(file, ei->description[i]);
8183 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8185 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8187 putFile8Bit(file, ei->num_change_pages);
8189 putFile16BitBE(file, ei->ce_value_fixed_initial);
8190 putFile16BitBE(file, ei->ce_value_random_initial);
8191 putFile8Bit(file, ei->use_last_ce_value);
8193 putFile8Bit(file, ei->use_gfx_element);
8194 putFile16BitBE(file, ei->gfx_element_initial);
8196 putFile8Bit(file, ei->collect_score_initial);
8197 putFile8Bit(file, ei->collect_count_initial);
8199 putFile8Bit(file, ei->drop_delay_fixed);
8200 putFile8Bit(file, ei->push_delay_fixed);
8201 putFile8Bit(file, ei->drop_delay_random);
8202 putFile8Bit(file, ei->push_delay_random);
8203 putFile16BitBE(file, ei->move_delay_fixed);
8204 putFile16BitBE(file, ei->move_delay_random);
8206 // bits 0 - 15 of "move_pattern" ...
8207 putFile16BitBE(file, ei->move_pattern & 0xffff);
8208 putFile8Bit(file, ei->move_direction_initial);
8209 putFile8Bit(file, ei->move_stepsize);
8211 putFile8Bit(file, ei->slippery_type);
8213 for (y = 0; y < 3; y++)
8214 for (x = 0; x < 3; x++)
8215 putFile16BitBE(file, ei->content.e[x][y]);
8217 putFile16BitBE(file, ei->move_enter_element);
8218 putFile16BitBE(file, ei->move_leave_element);
8219 putFile8Bit(file, ei->move_leave_type);
8221 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8222 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8224 putFile8Bit(file, ei->access_direction);
8226 putFile8Bit(file, ei->explosion_delay);
8227 putFile8Bit(file, ei->ignition_delay);
8228 putFile8Bit(file, ei->explosion_type);
8230 // some free bytes for future custom property values and padding
8231 WriteUnusedBytesToFile(file, 1);
8233 // ---------- change page property values (48 bytes) ------------------------
8235 for (i = 0; i < ei->num_change_pages; i++)
8237 struct ElementChangeInfo *change = &ei->change_page[i];
8238 unsigned int event_bits;
8240 // bits 0 - 31 of "has_event[]" ...
8242 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8243 if (change->has_event[j])
8244 event_bits |= (1u << j);
8245 putFile32BitBE(file, event_bits);
8247 putFile16BitBE(file, change->target_element);
8249 putFile16BitBE(file, change->delay_fixed);
8250 putFile16BitBE(file, change->delay_random);
8251 putFile16BitBE(file, change->delay_frames);
8253 putFile16BitBE(file, change->initial_trigger_element);
8255 putFile8Bit(file, change->explode);
8256 putFile8Bit(file, change->use_target_content);
8257 putFile8Bit(file, change->only_if_complete);
8258 putFile8Bit(file, change->use_random_replace);
8260 putFile8Bit(file, change->random_percentage);
8261 putFile8Bit(file, change->replace_when);
8263 for (y = 0; y < 3; y++)
8264 for (x = 0; x < 3; x++)
8265 putFile16BitBE(file, change->target_content.e[x][y]);
8267 putFile8Bit(file, change->can_change);
8269 putFile8Bit(file, change->trigger_side);
8271 putFile8Bit(file, change->trigger_player);
8272 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8273 log_2(change->trigger_page)));
8275 putFile8Bit(file, change->has_action);
8276 putFile8Bit(file, change->action_type);
8277 putFile8Bit(file, change->action_mode);
8278 putFile16BitBE(file, change->action_arg);
8280 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8282 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8283 if (change->has_event[j])
8284 event_bits |= (1u << (j - 32));
8285 putFile8Bit(file, event_bits);
8290 #if ENABLE_HISTORIC_CHUNKS
8291 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8293 struct ElementInfo *ei = &element_info[element];
8294 struct ElementGroupInfo *group = ei->group;
8297 putFile16BitBE(file, element);
8299 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8300 putFile8Bit(file, ei->description[i]);
8302 putFile8Bit(file, group->num_elements);
8304 putFile8Bit(file, ei->use_gfx_element);
8305 putFile16BitBE(file, ei->gfx_element_initial);
8307 putFile8Bit(file, group->choice_mode);
8309 // some free bytes for future values and padding
8310 WriteUnusedBytesToFile(file, 3);
8312 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8313 putFile16BitBE(file, group->element[i]);
8317 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8318 boolean write_element)
8320 int save_type = entry->save_type;
8321 int data_type = entry->data_type;
8322 int conf_type = entry->conf_type;
8323 int byte_mask = conf_type & CONF_MASK_BYTES;
8324 int element = entry->element;
8325 int default_value = entry->default_value;
8327 boolean modified = FALSE;
8329 if (byte_mask != CONF_MASK_MULTI_BYTES)
8331 void *value_ptr = entry->value;
8332 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8335 // check if any settings have been modified before saving them
8336 if (value != default_value)
8339 // do not save if explicitly told or if unmodified default settings
8340 if ((save_type == SAVE_CONF_NEVER) ||
8341 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8345 num_bytes += putFile16BitBE(file, element);
8347 num_bytes += putFile8Bit(file, conf_type);
8348 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8349 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8350 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8353 else if (data_type == TYPE_STRING)
8355 char *default_string = entry->default_string;
8356 char *string = (char *)(entry->value);
8357 int string_length = strlen(string);
8360 // check if any settings have been modified before saving them
8361 if (!strEqual(string, default_string))
8364 // do not save if explicitly told or if unmodified default settings
8365 if ((save_type == SAVE_CONF_NEVER) ||
8366 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8370 num_bytes += putFile16BitBE(file, element);
8372 num_bytes += putFile8Bit(file, conf_type);
8373 num_bytes += putFile16BitBE(file, string_length);
8375 for (i = 0; i < string_length; i++)
8376 num_bytes += putFile8Bit(file, string[i]);
8378 else if (data_type == TYPE_ELEMENT_LIST)
8380 int *element_array = (int *)(entry->value);
8381 int num_elements = *(int *)(entry->num_entities);
8384 // check if any settings have been modified before saving them
8385 for (i = 0; i < num_elements; i++)
8386 if (element_array[i] != default_value)
8389 // do not save if explicitly told or if unmodified default settings
8390 if ((save_type == SAVE_CONF_NEVER) ||
8391 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8395 num_bytes += putFile16BitBE(file, element);
8397 num_bytes += putFile8Bit(file, conf_type);
8398 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8400 for (i = 0; i < num_elements; i++)
8401 num_bytes += putFile16BitBE(file, element_array[i]);
8403 else if (data_type == TYPE_CONTENT_LIST)
8405 struct Content *content = (struct Content *)(entry->value);
8406 int num_contents = *(int *)(entry->num_entities);
8409 // check if any settings have been modified before saving them
8410 for (i = 0; i < num_contents; i++)
8411 for (y = 0; y < 3; y++)
8412 for (x = 0; x < 3; x++)
8413 if (content[i].e[x][y] != default_value)
8416 // do not save if explicitly told or if unmodified default settings
8417 if ((save_type == SAVE_CONF_NEVER) ||
8418 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8422 num_bytes += putFile16BitBE(file, element);
8424 num_bytes += putFile8Bit(file, conf_type);
8425 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8427 for (i = 0; i < num_contents; i++)
8428 for (y = 0; y < 3; y++)
8429 for (x = 0; x < 3; x++)
8430 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8436 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8441 li = *level; // copy level data into temporary buffer
8443 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8444 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8449 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8454 li = *level; // copy level data into temporary buffer
8456 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8457 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8462 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8464 int envelope_nr = element - EL_ENVELOPE_1;
8468 chunk_size += putFile16BitBE(file, element);
8470 // copy envelope data into temporary buffer
8471 xx_envelope = level->envelope[envelope_nr];
8473 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8474 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8479 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8481 struct ElementInfo *ei = &element_info[element];
8485 chunk_size += putFile16BitBE(file, element);
8487 xx_ei = *ei; // copy element data into temporary buffer
8489 // set default description string for this specific element
8490 strcpy(xx_default_description, getDefaultElementDescription(ei));
8492 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8493 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8495 for (i = 0; i < ei->num_change_pages; i++)
8497 struct ElementChangeInfo *change = &ei->change_page[i];
8499 xx_current_change_page = i;
8501 xx_change = *change; // copy change data into temporary buffer
8504 setEventBitsFromEventFlags(change);
8506 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8507 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8514 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8516 struct ElementInfo *ei = &element_info[element];
8517 struct ElementGroupInfo *group = ei->group;
8521 chunk_size += putFile16BitBE(file, element);
8523 xx_ei = *ei; // copy element data into temporary buffer
8524 xx_group = *group; // copy group data into temporary buffer
8526 // set default description string for this specific element
8527 strcpy(xx_default_description, getDefaultElementDescription(ei));
8529 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8530 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8535 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8537 struct ElementInfo *ei = &element_info[element];
8541 chunk_size += putFile16BitBE(file, element);
8543 xx_ei = *ei; // copy element data into temporary buffer
8545 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8546 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8551 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8552 boolean save_as_template)
8558 if (!(file = fopen(filename, MODE_WRITE)))
8560 Warn("cannot save level file '%s'", filename);
8565 level->file_version = FILE_VERSION_ACTUAL;
8566 level->game_version = GAME_VERSION_ACTUAL;
8568 level->creation_date = getCurrentDate();
8570 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8571 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8573 chunk_size = SaveLevel_VERS(NULL, level);
8574 putFileChunkBE(file, "VERS", chunk_size);
8575 SaveLevel_VERS(file, level);
8577 chunk_size = SaveLevel_DATE(NULL, level);
8578 putFileChunkBE(file, "DATE", chunk_size);
8579 SaveLevel_DATE(file, level);
8581 chunk_size = SaveLevel_NAME(NULL, level);
8582 putFileChunkBE(file, "NAME", chunk_size);
8583 SaveLevel_NAME(file, level);
8585 chunk_size = SaveLevel_AUTH(NULL, level);
8586 putFileChunkBE(file, "AUTH", chunk_size);
8587 SaveLevel_AUTH(file, level);
8589 chunk_size = SaveLevel_INFO(NULL, level);
8590 putFileChunkBE(file, "INFO", chunk_size);
8591 SaveLevel_INFO(file, level);
8593 chunk_size = SaveLevel_BODY(NULL, level);
8594 putFileChunkBE(file, "BODY", chunk_size);
8595 SaveLevel_BODY(file, level);
8597 chunk_size = SaveLevel_ELEM(NULL, level);
8598 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8600 putFileChunkBE(file, "ELEM", chunk_size);
8601 SaveLevel_ELEM(file, level);
8604 for (i = 0; i < NUM_ENVELOPES; i++)
8606 int element = EL_ENVELOPE_1 + i;
8608 chunk_size = SaveLevel_NOTE(NULL, level, element);
8609 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8611 putFileChunkBE(file, "NOTE", chunk_size);
8612 SaveLevel_NOTE(file, level, element);
8616 // if not using template level, check for non-default custom/group elements
8617 if (!level->use_custom_template || save_as_template)
8619 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8621 int element = EL_CUSTOM_START + i;
8623 chunk_size = SaveLevel_CUSX(NULL, level, element);
8624 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8626 putFileChunkBE(file, "CUSX", chunk_size);
8627 SaveLevel_CUSX(file, level, element);
8631 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8633 int element = EL_GROUP_START + i;
8635 chunk_size = SaveLevel_GRPX(NULL, level, element);
8636 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8638 putFileChunkBE(file, "GRPX", chunk_size);
8639 SaveLevel_GRPX(file, level, element);
8643 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8645 int element = GET_EMPTY_ELEMENT(i);
8647 chunk_size = SaveLevel_EMPX(NULL, level, element);
8648 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8650 putFileChunkBE(file, "EMPX", chunk_size);
8651 SaveLevel_EMPX(file, level, element);
8658 SetFilePermissions(filename, PERMS_PRIVATE);
8661 void SaveLevel(int nr)
8663 char *filename = getDefaultLevelFilename(nr);
8665 SaveLevelFromFilename(&level, filename, FALSE);
8668 void SaveLevelTemplate(void)
8670 char *filename = getLocalLevelTemplateFilename();
8672 SaveLevelFromFilename(&level, filename, TRUE);
8675 boolean SaveLevelChecked(int nr)
8677 char *filename = getDefaultLevelFilename(nr);
8678 boolean new_level = !fileExists(filename);
8679 boolean level_saved = FALSE;
8681 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8686 Request("Level saved!", REQ_CONFIRM);
8694 void DumpLevel(struct LevelInfo *level)
8696 if (level->no_level_file || level->no_valid_file)
8698 Warn("cannot dump -- no valid level file found");
8704 Print("Level xxx (file version %08d, game version %08d)\n",
8705 level->file_version, level->game_version);
8708 Print("Level author: '%s'\n", level->author);
8709 Print("Level title: '%s'\n", level->name);
8711 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8713 Print("Level time: %d seconds\n", level->time);
8714 Print("Gems needed: %d\n", level->gems_needed);
8716 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8717 Print("Time for wheel: %d seconds\n", level->time_wheel);
8718 Print("Time for light: %d seconds\n", level->time_light);
8719 Print("Time for timegate: %d seconds\n", level->time_timegate);
8721 Print("Amoeba speed: %d\n", level->amoeba_speed);
8724 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8725 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8726 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8727 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8728 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8729 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8735 for (i = 0; i < NUM_ENVELOPES; i++)
8737 char *text = level->envelope[i].text;
8738 int text_len = strlen(text);
8739 boolean has_text = FALSE;
8741 for (j = 0; j < text_len; j++)
8742 if (text[j] != ' ' && text[j] != '\n')
8748 Print("Envelope %d:\n'%s'\n", i + 1, text);
8756 void DumpLevels(void)
8758 static LevelDirTree *dumplevel_leveldir = NULL;
8760 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8761 global.dumplevel_leveldir);
8763 if (dumplevel_leveldir == NULL)
8764 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8766 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8767 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8768 Fail("no such level number: %d", global.dumplevel_level_nr);
8770 leveldir_current = dumplevel_leveldir;
8772 LoadLevel(global.dumplevel_level_nr);
8779 // ============================================================================
8780 // tape file functions
8781 // ============================================================================
8783 static void setTapeInfoToDefaults(void)
8787 // always start with reliable default values (empty tape)
8790 // default values (also for pre-1.2 tapes) with only the first player
8791 tape.player_participates[0] = TRUE;
8792 for (i = 1; i < MAX_PLAYERS; i++)
8793 tape.player_participates[i] = FALSE;
8795 // at least one (default: the first) player participates in every tape
8796 tape.num_participating_players = 1;
8798 tape.property_bits = TAPE_PROPERTY_NONE;
8800 tape.level_nr = level_nr;
8802 tape.changed = FALSE;
8803 tape.solved = FALSE;
8805 tape.recording = FALSE;
8806 tape.playing = FALSE;
8807 tape.pausing = FALSE;
8809 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8810 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8812 tape.no_info_chunk = TRUE;
8813 tape.no_valid_file = FALSE;
8816 static int getTapePosSize(struct TapeInfo *tape)
8818 int tape_pos_size = 0;
8820 if (tape->use_key_actions)
8821 tape_pos_size += tape->num_participating_players;
8823 if (tape->use_mouse_actions)
8824 tape_pos_size += 3; // x and y position and mouse button mask
8826 tape_pos_size += 1; // tape action delay value
8828 return tape_pos_size;
8831 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8833 tape->use_key_actions = FALSE;
8834 tape->use_mouse_actions = FALSE;
8836 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8837 tape->use_key_actions = TRUE;
8839 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8840 tape->use_mouse_actions = TRUE;
8843 static int getTapeActionValue(struct TapeInfo *tape)
8845 return (tape->use_key_actions &&
8846 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8847 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8848 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8849 TAPE_ACTIONS_DEFAULT);
8852 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8854 tape->file_version = getFileVersion(file);
8855 tape->game_version = getFileVersion(file);
8860 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8864 tape->random_seed = getFile32BitBE(file);
8865 tape->date = getFile32BitBE(file);
8866 tape->length = getFile32BitBE(file);
8868 // read header fields that are new since version 1.2
8869 if (tape->file_version >= FILE_VERSION_1_2)
8871 byte store_participating_players = getFile8Bit(file);
8874 // since version 1.2, tapes store which players participate in the tape
8875 tape->num_participating_players = 0;
8876 for (i = 0; i < MAX_PLAYERS; i++)
8878 tape->player_participates[i] = FALSE;
8880 if (store_participating_players & (1 << i))
8882 tape->player_participates[i] = TRUE;
8883 tape->num_participating_players++;
8887 setTapeActionFlags(tape, getFile8Bit(file));
8889 tape->property_bits = getFile8Bit(file);
8890 tape->solved = getFile8Bit(file);
8892 engine_version = getFileVersion(file);
8893 if (engine_version > 0)
8894 tape->engine_version = engine_version;
8896 tape->engine_version = tape->game_version;
8902 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8904 tape->scr_fieldx = getFile8Bit(file);
8905 tape->scr_fieldy = getFile8Bit(file);
8910 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8912 char *level_identifier = NULL;
8913 int level_identifier_size;
8916 tape->no_info_chunk = FALSE;
8918 level_identifier_size = getFile16BitBE(file);
8920 level_identifier = checked_malloc(level_identifier_size);
8922 for (i = 0; i < level_identifier_size; i++)
8923 level_identifier[i] = getFile8Bit(file);
8925 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8926 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8928 checked_free(level_identifier);
8930 tape->level_nr = getFile16BitBE(file);
8932 chunk_size = 2 + level_identifier_size + 2;
8937 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8940 int tape_pos_size = getTapePosSize(tape);
8941 int chunk_size_expected = tape_pos_size * tape->length;
8943 if (chunk_size_expected != chunk_size)
8945 ReadUnusedBytesFromFile(file, chunk_size);
8946 return chunk_size_expected;
8949 for (i = 0; i < tape->length; i++)
8951 if (i >= MAX_TAPE_LEN)
8953 Warn("tape truncated -- size exceeds maximum tape size %d",
8956 // tape too large; read and ignore remaining tape data from this chunk
8957 for (;i < tape->length; i++)
8958 ReadUnusedBytesFromFile(file, tape_pos_size);
8963 if (tape->use_key_actions)
8965 for (j = 0; j < MAX_PLAYERS; j++)
8967 tape->pos[i].action[j] = MV_NONE;
8969 if (tape->player_participates[j])
8970 tape->pos[i].action[j] = getFile8Bit(file);
8974 if (tape->use_mouse_actions)
8976 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8977 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8978 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8981 tape->pos[i].delay = getFile8Bit(file);
8983 if (tape->file_version == FILE_VERSION_1_0)
8985 // eliminate possible diagonal moves in old tapes
8986 // this is only for backward compatibility
8988 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8989 byte action = tape->pos[i].action[0];
8990 int k, num_moves = 0;
8992 for (k = 0; k < 4; k++)
8994 if (action & joy_dir[k])
8996 tape->pos[i + num_moves].action[0] = joy_dir[k];
8998 tape->pos[i + num_moves].delay = 0;
9007 tape->length += num_moves;
9010 else if (tape->file_version < FILE_VERSION_2_0)
9012 // convert pre-2.0 tapes to new tape format
9014 if (tape->pos[i].delay > 1)
9017 tape->pos[i + 1] = tape->pos[i];
9018 tape->pos[i + 1].delay = 1;
9021 for (j = 0; j < MAX_PLAYERS; j++)
9022 tape->pos[i].action[j] = MV_NONE;
9023 tape->pos[i].delay--;
9030 if (checkEndOfFile(file))
9034 if (i != tape->length)
9035 chunk_size = tape_pos_size * i;
9040 static void LoadTape_SokobanSolution(char *filename)
9043 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9045 if (!(file = openFile(filename, MODE_READ)))
9047 tape.no_valid_file = TRUE;
9052 while (!checkEndOfFile(file))
9054 unsigned char c = getByteFromFile(file);
9056 if (checkEndOfFile(file))
9063 tape.pos[tape.length].action[0] = MV_UP;
9064 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9070 tape.pos[tape.length].action[0] = MV_DOWN;
9071 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9077 tape.pos[tape.length].action[0] = MV_LEFT;
9078 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9084 tape.pos[tape.length].action[0] = MV_RIGHT;
9085 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9093 // ignore white-space characters
9097 tape.no_valid_file = TRUE;
9099 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9107 if (tape.no_valid_file)
9110 tape.length_frames = GetTapeLengthFrames();
9111 tape.length_seconds = GetTapeLengthSeconds();
9114 void LoadTapeFromFilename(char *filename)
9116 char cookie[MAX_LINE_LEN];
9117 char chunk_name[CHUNK_ID_LEN + 1];
9121 // always start with reliable default values
9122 setTapeInfoToDefaults();
9124 if (strSuffix(filename, ".sln"))
9126 LoadTape_SokobanSolution(filename);
9131 if (!(file = openFile(filename, MODE_READ)))
9133 tape.no_valid_file = TRUE;
9138 getFileChunkBE(file, chunk_name, NULL);
9139 if (strEqual(chunk_name, "RND1"))
9141 getFile32BitBE(file); // not used
9143 getFileChunkBE(file, chunk_name, NULL);
9144 if (!strEqual(chunk_name, "TAPE"))
9146 tape.no_valid_file = TRUE;
9148 Warn("unknown format of tape file '%s'", filename);
9155 else // check for pre-2.0 file format with cookie string
9157 strcpy(cookie, chunk_name);
9158 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9160 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9161 cookie[strlen(cookie) - 1] = '\0';
9163 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9165 tape.no_valid_file = TRUE;
9167 Warn("unknown format of tape file '%s'", filename);
9174 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9176 tape.no_valid_file = TRUE;
9178 Warn("unsupported version of tape file '%s'", filename);
9185 // pre-2.0 tape files have no game version, so use file version here
9186 tape.game_version = tape.file_version;
9189 if (tape.file_version < FILE_VERSION_1_2)
9191 // tape files from versions before 1.2.0 without chunk structure
9192 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9193 LoadTape_BODY(file, 2 * tape.length, &tape);
9201 int (*loader)(File *, int, struct TapeInfo *);
9205 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9206 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9207 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9208 { "INFO", -1, LoadTape_INFO },
9209 { "BODY", -1, LoadTape_BODY },
9213 while (getFileChunkBE(file, chunk_name, &chunk_size))
9217 while (chunk_info[i].name != NULL &&
9218 !strEqual(chunk_name, chunk_info[i].name))
9221 if (chunk_info[i].name == NULL)
9223 Warn("unknown chunk '%s' in tape file '%s'",
9224 chunk_name, filename);
9226 ReadUnusedBytesFromFile(file, chunk_size);
9228 else if (chunk_info[i].size != -1 &&
9229 chunk_info[i].size != chunk_size)
9231 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9232 chunk_size, chunk_name, filename);
9234 ReadUnusedBytesFromFile(file, chunk_size);
9238 // call function to load this tape chunk
9239 int chunk_size_expected =
9240 (chunk_info[i].loader)(file, chunk_size, &tape);
9242 // the size of some chunks cannot be checked before reading other
9243 // chunks first (like "HEAD" and "BODY") that contain some header
9244 // information, so check them here
9245 if (chunk_size_expected != chunk_size)
9247 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9248 chunk_size, chunk_name, filename);
9256 tape.length_frames = GetTapeLengthFrames();
9257 tape.length_seconds = GetTapeLengthSeconds();
9260 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9262 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9264 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9265 tape.engine_version);
9269 void LoadTape(int nr)
9271 char *filename = getTapeFilename(nr);
9273 LoadTapeFromFilename(filename);
9276 void LoadSolutionTape(int nr)
9278 char *filename = getSolutionTapeFilename(nr);
9280 LoadTapeFromFilename(filename);
9282 if (TAPE_IS_EMPTY(tape))
9284 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9285 level.native_bd_level->replay != NULL)
9286 CopyNativeTape_BD_to_RND(&level);
9287 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9288 level.native_sp_level->demo.is_available)
9289 CopyNativeTape_SP_to_RND(&level);
9293 void LoadScoreTape(char *score_tape_basename, int nr)
9295 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9297 LoadTapeFromFilename(filename);
9300 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9302 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9304 LoadTapeFromFilename(filename);
9307 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9309 // chunk required for team mode tapes with non-default screen size
9310 return (tape->num_participating_players > 1 &&
9311 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9312 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9315 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9317 putFileVersion(file, tape->file_version);
9318 putFileVersion(file, tape->game_version);
9321 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9324 byte store_participating_players = 0;
9326 // set bits for participating players for compact storage
9327 for (i = 0; i < MAX_PLAYERS; i++)
9328 if (tape->player_participates[i])
9329 store_participating_players |= (1 << i);
9331 putFile32BitBE(file, tape->random_seed);
9332 putFile32BitBE(file, tape->date);
9333 putFile32BitBE(file, tape->length);
9335 putFile8Bit(file, store_participating_players);
9337 putFile8Bit(file, getTapeActionValue(tape));
9339 putFile8Bit(file, tape->property_bits);
9340 putFile8Bit(file, tape->solved);
9342 putFileVersion(file, tape->engine_version);
9345 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9347 putFile8Bit(file, tape->scr_fieldx);
9348 putFile8Bit(file, tape->scr_fieldy);
9351 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9353 int level_identifier_size = strlen(tape->level_identifier) + 1;
9356 putFile16BitBE(file, level_identifier_size);
9358 for (i = 0; i < level_identifier_size; i++)
9359 putFile8Bit(file, tape->level_identifier[i]);
9361 putFile16BitBE(file, tape->level_nr);
9364 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9368 for (i = 0; i < tape->length; i++)
9370 if (tape->use_key_actions)
9372 for (j = 0; j < MAX_PLAYERS; j++)
9373 if (tape->player_participates[j])
9374 putFile8Bit(file, tape->pos[i].action[j]);
9377 if (tape->use_mouse_actions)
9379 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9380 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9381 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9384 putFile8Bit(file, tape->pos[i].delay);
9388 void SaveTapeToFilename(char *filename)
9392 int info_chunk_size;
9393 int body_chunk_size;
9395 if (!(file = fopen(filename, MODE_WRITE)))
9397 Warn("cannot save level recording file '%s'", filename);
9402 tape_pos_size = getTapePosSize(&tape);
9404 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9405 body_chunk_size = tape_pos_size * tape.length;
9407 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9408 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9410 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9411 SaveTape_VERS(file, &tape);
9413 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9414 SaveTape_HEAD(file, &tape);
9416 if (checkSaveTape_SCRN(&tape))
9418 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9419 SaveTape_SCRN(file, &tape);
9422 putFileChunkBE(file, "INFO", info_chunk_size);
9423 SaveTape_INFO(file, &tape);
9425 putFileChunkBE(file, "BODY", body_chunk_size);
9426 SaveTape_BODY(file, &tape);
9430 SetFilePermissions(filename, PERMS_PRIVATE);
9433 static void SaveTapeExt(char *filename)
9437 tape.file_version = FILE_VERSION_ACTUAL;
9438 tape.game_version = GAME_VERSION_ACTUAL;
9440 tape.num_participating_players = 0;
9442 // count number of participating players
9443 for (i = 0; i < MAX_PLAYERS; i++)
9444 if (tape.player_participates[i])
9445 tape.num_participating_players++;
9447 SaveTapeToFilename(filename);
9449 tape.changed = FALSE;
9452 void SaveTape(int nr)
9454 char *filename = getTapeFilename(nr);
9456 InitTapeDirectory(leveldir_current->subdir);
9458 SaveTapeExt(filename);
9461 void SaveScoreTape(int nr)
9463 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9465 // used instead of "leveldir_current->subdir" (for network games)
9466 InitScoreTapeDirectory(levelset.identifier, nr);
9468 SaveTapeExt(filename);
9471 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9472 unsigned int req_state_added)
9474 char *filename = getTapeFilename(nr);
9475 boolean new_tape = !fileExists(filename);
9476 boolean tape_saved = FALSE;
9478 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9483 Request(msg_saved, REQ_CONFIRM | req_state_added);
9491 boolean SaveTapeChecked(int nr)
9493 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9496 boolean SaveTapeChecked_LevelSolved(int nr)
9498 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9499 "Level solved! Tape saved!", REQ_STAY_OPEN);
9502 void DumpTape(struct TapeInfo *tape)
9504 int tape_frame_counter;
9507 if (tape->no_valid_file)
9509 Warn("cannot dump -- no valid tape file found");
9516 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9517 tape->level_nr, tape->file_version, tape->game_version);
9518 Print(" (effective engine version %08d)\n",
9519 tape->engine_version);
9520 Print("Level series identifier: '%s'\n", tape->level_identifier);
9522 Print("Solution tape: %s\n",
9523 tape->solved ? "yes" :
9524 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9526 Print("Special tape properties: ");
9527 if (tape->property_bits == TAPE_PROPERTY_NONE)
9529 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9530 Print("[em_random_bug]");
9531 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9532 Print("[game_speed]");
9533 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9535 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9536 Print("[single_step]");
9537 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9538 Print("[snapshot]");
9539 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9540 Print("[replayed]");
9541 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9542 Print("[tas_keys]");
9543 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9544 Print("[small_graphics]");
9547 int year2 = tape->date / 10000;
9548 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9549 int month_index_raw = (tape->date / 100) % 100;
9550 int month_index = month_index_raw % 12; // prevent invalid index
9551 int month = month_index + 1;
9552 int day = tape->date % 100;
9554 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9558 tape_frame_counter = 0;
9560 for (i = 0; i < tape->length; i++)
9562 if (i >= MAX_TAPE_LEN)
9567 for (j = 0; j < MAX_PLAYERS; j++)
9569 if (tape->player_participates[j])
9571 int action = tape->pos[i].action[j];
9573 Print("%d:%02x ", j, action);
9574 Print("[%c%c%c%c|%c%c] - ",
9575 (action & JOY_LEFT ? '<' : ' '),
9576 (action & JOY_RIGHT ? '>' : ' '),
9577 (action & JOY_UP ? '^' : ' '),
9578 (action & JOY_DOWN ? 'v' : ' '),
9579 (action & JOY_BUTTON_1 ? '1' : ' '),
9580 (action & JOY_BUTTON_2 ? '2' : ' '));
9584 Print("(%03d) ", tape->pos[i].delay);
9585 Print("[%05d]\n", tape_frame_counter);
9587 tape_frame_counter += tape->pos[i].delay;
9593 void DumpTapes(void)
9595 static LevelDirTree *dumptape_leveldir = NULL;
9597 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9598 global.dumptape_leveldir);
9600 if (dumptape_leveldir == NULL)
9601 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9603 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9604 global.dumptape_level_nr > dumptape_leveldir->last_level)
9605 Fail("no such level number: %d", global.dumptape_level_nr);
9607 leveldir_current = dumptape_leveldir;
9609 if (options.mytapes)
9610 LoadTape(global.dumptape_level_nr);
9612 LoadSolutionTape(global.dumptape_level_nr);
9620 // ============================================================================
9621 // score file functions
9622 // ============================================================================
9624 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9628 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9630 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9631 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9632 scores->entry[i].score = 0;
9633 scores->entry[i].time = 0;
9635 scores->entry[i].id = -1;
9636 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9637 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9638 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9639 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9640 strcpy(scores->entry[i].country_code, "??");
9643 scores->num_entries = 0;
9644 scores->last_added = -1;
9645 scores->last_added_local = -1;
9647 scores->updated = FALSE;
9648 scores->uploaded = FALSE;
9649 scores->tape_downloaded = FALSE;
9650 scores->force_last_added = FALSE;
9652 // The following values are intentionally not reset here:
9656 // - continue_playing
9657 // - continue_on_return
9660 static void setScoreInfoToDefaults(void)
9662 setScoreInfoToDefaultsExt(&scores);
9665 static void setServerScoreInfoToDefaults(void)
9667 setScoreInfoToDefaultsExt(&server_scores);
9670 static void LoadScore_OLD(int nr)
9673 char *filename = getScoreFilename(nr);
9674 char cookie[MAX_LINE_LEN];
9675 char line[MAX_LINE_LEN];
9679 if (!(file = fopen(filename, MODE_READ)))
9682 // check file identifier
9683 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9685 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9686 cookie[strlen(cookie) - 1] = '\0';
9688 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9690 Warn("unknown format of score file '%s'", filename);
9697 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9699 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9700 Warn("fscanf() failed; %s", strerror(errno));
9702 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9705 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9706 line[strlen(line) - 1] = '\0';
9708 for (line_ptr = line; *line_ptr; line_ptr++)
9710 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9712 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9713 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9722 static void ConvertScore_OLD(void)
9724 // only convert score to time for levels that rate playing time over score
9725 if (!level.rate_time_over_score)
9728 // convert old score to playing time for score-less levels (like Supaplex)
9729 int time_final_max = 999;
9732 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9734 int score = scores.entry[i].score;
9736 if (score > 0 && score < time_final_max)
9737 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9741 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9743 scores->file_version = getFileVersion(file);
9744 scores->game_version = getFileVersion(file);
9749 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9751 char *level_identifier = NULL;
9752 int level_identifier_size;
9755 level_identifier_size = getFile16BitBE(file);
9757 level_identifier = checked_malloc(level_identifier_size);
9759 for (i = 0; i < level_identifier_size; i++)
9760 level_identifier[i] = getFile8Bit(file);
9762 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9763 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9765 checked_free(level_identifier);
9767 scores->level_nr = getFile16BitBE(file);
9768 scores->num_entries = getFile16BitBE(file);
9770 chunk_size = 2 + level_identifier_size + 2 + 2;
9775 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9779 for (i = 0; i < scores->num_entries; i++)
9781 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9782 scores->entry[i].name[j] = getFile8Bit(file);
9784 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9787 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9792 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9796 for (i = 0; i < scores->num_entries; i++)
9797 scores->entry[i].score = getFile16BitBE(file);
9799 chunk_size = scores->num_entries * 2;
9804 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9808 for (i = 0; i < scores->num_entries; i++)
9809 scores->entry[i].score = getFile32BitBE(file);
9811 chunk_size = scores->num_entries * 4;
9816 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9820 for (i = 0; i < scores->num_entries; i++)
9821 scores->entry[i].time = getFile32BitBE(file);
9823 chunk_size = scores->num_entries * 4;
9828 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9832 for (i = 0; i < scores->num_entries; i++)
9834 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9835 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9837 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9840 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9845 void LoadScore(int nr)
9847 char *filename = getScoreFilename(nr);
9848 char cookie[MAX_LINE_LEN];
9849 char chunk_name[CHUNK_ID_LEN + 1];
9851 boolean old_score_file_format = FALSE;
9854 // always start with reliable default values
9855 setScoreInfoToDefaults();
9857 if (!(file = openFile(filename, MODE_READ)))
9860 getFileChunkBE(file, chunk_name, NULL);
9861 if (strEqual(chunk_name, "RND1"))
9863 getFile32BitBE(file); // not used
9865 getFileChunkBE(file, chunk_name, NULL);
9866 if (!strEqual(chunk_name, "SCOR"))
9868 Warn("unknown format of score file '%s'", filename);
9875 else // check for old file format with cookie string
9877 strcpy(cookie, chunk_name);
9878 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9880 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9881 cookie[strlen(cookie) - 1] = '\0';
9883 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9885 Warn("unknown format of score file '%s'", filename);
9892 old_score_file_format = TRUE;
9895 if (old_score_file_format)
9897 // score files from versions before 4.2.4.0 without chunk structure
9900 // convert score to time, if possible (mainly for Supaplex levels)
9909 int (*loader)(File *, int, struct ScoreInfo *);
9913 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9914 { "INFO", -1, LoadScore_INFO },
9915 { "NAME", -1, LoadScore_NAME },
9916 { "SCOR", -1, LoadScore_SCOR },
9917 { "SC4R", -1, LoadScore_SC4R },
9918 { "TIME", -1, LoadScore_TIME },
9919 { "TAPE", -1, LoadScore_TAPE },
9924 while (getFileChunkBE(file, chunk_name, &chunk_size))
9928 while (chunk_info[i].name != NULL &&
9929 !strEqual(chunk_name, chunk_info[i].name))
9932 if (chunk_info[i].name == NULL)
9934 Warn("unknown chunk '%s' in score file '%s'",
9935 chunk_name, filename);
9937 ReadUnusedBytesFromFile(file, chunk_size);
9939 else if (chunk_info[i].size != -1 &&
9940 chunk_info[i].size != chunk_size)
9942 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9943 chunk_size, chunk_name, filename);
9945 ReadUnusedBytesFromFile(file, chunk_size);
9949 // call function to load this score chunk
9950 int chunk_size_expected =
9951 (chunk_info[i].loader)(file, chunk_size, &scores);
9953 // the size of some chunks cannot be checked before reading other
9954 // chunks first (like "HEAD" and "BODY") that contain some header
9955 // information, so check them here
9956 if (chunk_size_expected != chunk_size)
9958 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9959 chunk_size, chunk_name, filename);
9968 #if ENABLE_HISTORIC_CHUNKS
9969 void SaveScore_OLD(int nr)
9972 char *filename = getScoreFilename(nr);
9975 // used instead of "leveldir_current->subdir" (for network games)
9976 InitScoreDirectory(levelset.identifier);
9978 if (!(file = fopen(filename, MODE_WRITE)))
9980 Warn("cannot save score for level %d", nr);
9985 fprintf(file, "%s\n\n", SCORE_COOKIE);
9987 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9988 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9992 SetFilePermissions(filename, PERMS_PRIVATE);
9996 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9998 putFileVersion(file, scores->file_version);
9999 putFileVersion(file, scores->game_version);
10002 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10004 int level_identifier_size = strlen(scores->level_identifier) + 1;
10007 putFile16BitBE(file, level_identifier_size);
10009 for (i = 0; i < level_identifier_size; i++)
10010 putFile8Bit(file, scores->level_identifier[i]);
10012 putFile16BitBE(file, scores->level_nr);
10013 putFile16BitBE(file, scores->num_entries);
10016 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10020 for (i = 0; i < scores->num_entries; i++)
10022 int name_size = strlen(scores->entry[i].name);
10024 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10025 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10029 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10033 for (i = 0; i < scores->num_entries; i++)
10034 putFile16BitBE(file, scores->entry[i].score);
10037 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10041 for (i = 0; i < scores->num_entries; i++)
10042 putFile32BitBE(file, scores->entry[i].score);
10045 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10049 for (i = 0; i < scores->num_entries; i++)
10050 putFile32BitBE(file, scores->entry[i].time);
10053 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10057 for (i = 0; i < scores->num_entries; i++)
10059 int size = strlen(scores->entry[i].tape_basename);
10061 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10062 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10066 static void SaveScoreToFilename(char *filename)
10069 int info_chunk_size;
10070 int name_chunk_size;
10071 int scor_chunk_size;
10072 int sc4r_chunk_size;
10073 int time_chunk_size;
10074 int tape_chunk_size;
10075 boolean has_large_score_values;
10078 if (!(file = fopen(filename, MODE_WRITE)))
10080 Warn("cannot save score file '%s'", filename);
10085 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10086 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10087 scor_chunk_size = scores.num_entries * 2;
10088 sc4r_chunk_size = scores.num_entries * 4;
10089 time_chunk_size = scores.num_entries * 4;
10090 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10092 has_large_score_values = FALSE;
10093 for (i = 0; i < scores.num_entries; i++)
10094 if (scores.entry[i].score > 0xffff)
10095 has_large_score_values = TRUE;
10097 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10098 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10100 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10101 SaveScore_VERS(file, &scores);
10103 putFileChunkBE(file, "INFO", info_chunk_size);
10104 SaveScore_INFO(file, &scores);
10106 putFileChunkBE(file, "NAME", name_chunk_size);
10107 SaveScore_NAME(file, &scores);
10109 if (has_large_score_values)
10111 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10112 SaveScore_SC4R(file, &scores);
10116 putFileChunkBE(file, "SCOR", scor_chunk_size);
10117 SaveScore_SCOR(file, &scores);
10120 putFileChunkBE(file, "TIME", time_chunk_size);
10121 SaveScore_TIME(file, &scores);
10123 putFileChunkBE(file, "TAPE", tape_chunk_size);
10124 SaveScore_TAPE(file, &scores);
10128 SetFilePermissions(filename, PERMS_PRIVATE);
10131 void SaveScore(int nr)
10133 char *filename = getScoreFilename(nr);
10136 // used instead of "leveldir_current->subdir" (for network games)
10137 InitScoreDirectory(levelset.identifier);
10139 scores.file_version = FILE_VERSION_ACTUAL;
10140 scores.game_version = GAME_VERSION_ACTUAL;
10142 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10143 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10144 scores.level_nr = level_nr;
10146 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10147 if (scores.entry[i].score == 0 &&
10148 scores.entry[i].time == 0 &&
10149 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10152 scores.num_entries = i;
10154 if (scores.num_entries == 0)
10157 SaveScoreToFilename(filename);
10160 static void LoadServerScoreFromCache(int nr)
10162 struct ScoreEntry score_entry;
10171 { &score_entry.score, FALSE, 0 },
10172 { &score_entry.time, FALSE, 0 },
10173 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10174 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10175 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10176 { &score_entry.id, FALSE, 0 },
10177 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10178 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10179 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10180 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10184 char *filename = getScoreCacheFilename(nr);
10185 SetupFileHash *score_hash = loadSetupFileHash(filename);
10188 server_scores.num_entries = 0;
10190 if (score_hash == NULL)
10193 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10195 score_entry = server_scores.entry[i];
10197 for (j = 0; score_mapping[j].value != NULL; j++)
10201 sprintf(token, "%02d.%d", i, j);
10203 char *value = getHashEntry(score_hash, token);
10208 if (score_mapping[j].is_string)
10210 char *score_value = (char *)score_mapping[j].value;
10211 int value_size = score_mapping[j].string_size;
10213 strncpy(score_value, value, value_size);
10214 score_value[value_size] = '\0';
10218 int *score_value = (int *)score_mapping[j].value;
10220 *score_value = atoi(value);
10223 server_scores.num_entries = i + 1;
10226 server_scores.entry[i] = score_entry;
10229 freeSetupFileHash(score_hash);
10232 void LoadServerScore(int nr, boolean download_score)
10234 if (!setup.use_api_server)
10237 // always start with reliable default values
10238 setServerScoreInfoToDefaults();
10240 // 1st step: load server scores from cache file (which may not exist)
10241 // (this should prevent reading it while the thread is writing to it)
10242 LoadServerScoreFromCache(nr);
10244 if (download_score && runtime.use_api_server)
10246 // 2nd step: download server scores from score server to cache file
10247 // (as thread, as it might time out if the server is not reachable)
10248 ApiGetScoreAsThread(nr);
10252 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10254 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10256 // if score tape not uploaded, ask for uploading missing tapes later
10257 if (!setup.has_remaining_tapes)
10258 setup.ask_for_remaining_tapes = TRUE;
10260 setup.provide_uploading_tapes = TRUE;
10261 setup.has_remaining_tapes = TRUE;
10263 SaveSetup_ServerSetup();
10266 void SaveServerScore(int nr, boolean tape_saved)
10268 if (!runtime.use_api_server)
10270 PrepareScoreTapesForUpload(leveldir_current->subdir);
10275 ApiAddScoreAsThread(nr, tape_saved, NULL);
10278 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10279 char *score_tape_filename)
10281 if (!runtime.use_api_server)
10284 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10287 void LoadLocalAndServerScore(int nr, boolean download_score)
10289 int last_added_local = scores.last_added_local;
10290 boolean force_last_added = scores.force_last_added;
10292 // needed if only showing server scores
10293 setScoreInfoToDefaults();
10295 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10298 // restore last added local score entry (before merging server scores)
10299 scores.last_added = scores.last_added_local = last_added_local;
10301 if (setup.use_api_server &&
10302 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10304 // load server scores from cache file and trigger update from server
10305 LoadServerScore(nr, download_score);
10307 // merge local scores with scores from server
10308 MergeServerScore();
10311 if (force_last_added)
10312 scores.force_last_added = force_last_added;
10316 // ============================================================================
10317 // setup file functions
10318 // ============================================================================
10320 #define TOKEN_STR_PLAYER_PREFIX "player_"
10323 static struct TokenInfo global_setup_tokens[] =
10327 &setup.player_name, "player_name"
10331 &setup.multiple_users, "multiple_users"
10335 &setup.sound, "sound"
10339 &setup.sound_loops, "repeating_sound_loops"
10343 &setup.sound_music, "background_music"
10347 &setup.sound_simple, "simple_sound_effects"
10351 &setup.toons, "toons"
10355 &setup.global_animations, "global_animations"
10359 &setup.scroll_delay, "scroll_delay"
10363 &setup.forced_scroll_delay, "forced_scroll_delay"
10367 &setup.scroll_delay_value, "scroll_delay_value"
10371 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10375 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10379 &setup.fade_screens, "fade_screens"
10383 &setup.autorecord, "automatic_tape_recording"
10387 &setup.autorecord_after_replay, "autorecord_after_replay"
10391 &setup.auto_pause_on_start, "auto_pause_on_start"
10395 &setup.show_titlescreen, "show_titlescreen"
10399 &setup.quick_doors, "quick_doors"
10403 &setup.team_mode, "team_mode"
10407 &setup.handicap, "handicap"
10411 &setup.skip_levels, "skip_levels"
10415 &setup.increment_levels, "increment_levels"
10419 &setup.auto_play_next_level, "auto_play_next_level"
10423 &setup.count_score_after_game, "count_score_after_game"
10427 &setup.show_scores_after_game, "show_scores_after_game"
10431 &setup.time_limit, "time_limit"
10435 &setup.fullscreen, "fullscreen"
10439 &setup.window_scaling_percent, "window_scaling_percent"
10443 &setup.window_scaling_quality, "window_scaling_quality"
10447 &setup.screen_rendering_mode, "screen_rendering_mode"
10451 &setup.vsync_mode, "vsync_mode"
10455 &setup.ask_on_escape, "ask_on_escape"
10459 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10463 &setup.ask_on_game_over, "ask_on_game_over"
10467 &setup.ask_on_quit_game, "ask_on_quit_game"
10471 &setup.ask_on_quit_program, "ask_on_quit_program"
10475 &setup.quick_switch, "quick_player_switch"
10479 &setup.input_on_focus, "input_on_focus"
10483 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10487 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10491 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10495 &setup.game_speed_extended, "game_speed_extended"
10499 &setup.game_frame_delay, "game_frame_delay"
10503 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10507 &setup.bd_skip_hatching, "bd_skip_hatching"
10511 &setup.bd_scroll_delay, "bd_scroll_delay"
10515 &setup.bd_smooth_movements, "bd_smooth_movements"
10519 &setup.sp_show_border_elements, "sp_show_border_elements"
10523 &setup.small_game_graphics, "small_game_graphics"
10527 &setup.show_load_save_buttons, "show_load_save_buttons"
10531 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10535 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10539 &setup.graphics_set, "graphics_set"
10543 &setup.sounds_set, "sounds_set"
10547 &setup.music_set, "music_set"
10551 &setup.override_level_graphics, "override_level_graphics"
10555 &setup.override_level_sounds, "override_level_sounds"
10559 &setup.override_level_music, "override_level_music"
10563 &setup.volume_simple, "volume_simple"
10567 &setup.volume_loops, "volume_loops"
10571 &setup.volume_music, "volume_music"
10575 &setup.network_mode, "network_mode"
10579 &setup.network_player_nr, "network_player"
10583 &setup.network_server_hostname, "network_server_hostname"
10587 &setup.touch.control_type, "touch.control_type"
10591 &setup.touch.move_distance, "touch.move_distance"
10595 &setup.touch.drop_distance, "touch.drop_distance"
10599 &setup.touch.transparency, "touch.transparency"
10603 &setup.touch.draw_outlined, "touch.draw_outlined"
10607 &setup.touch.draw_pressed, "touch.draw_pressed"
10611 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10615 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10619 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10623 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10627 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10631 static struct TokenInfo auto_setup_tokens[] =
10635 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10639 static struct TokenInfo server_setup_tokens[] =
10643 &setup.player_uuid, "player_uuid"
10647 &setup.player_version, "player_version"
10651 &setup.use_api_server, TEST_PREFIX "use_api_server"
10655 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10659 &setup.api_server_password, TEST_PREFIX "api_server_password"
10663 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10667 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10671 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10675 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10679 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10683 static struct TokenInfo editor_setup_tokens[] =
10687 &setup.editor.el_classic, "editor.el_classic"
10691 &setup.editor.el_custom, "editor.el_custom"
10695 &setup.editor.el_user_defined, "editor.el_user_defined"
10699 &setup.editor.el_dynamic, "editor.el_dynamic"
10703 &setup.editor.el_headlines, "editor.el_headlines"
10707 &setup.editor.show_element_token, "editor.show_element_token"
10711 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10715 static struct TokenInfo editor_cascade_setup_tokens[] =
10719 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10723 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10727 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10731 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10735 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10739 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10743 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10747 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10751 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10755 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10759 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10763 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10767 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10771 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10775 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10779 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10783 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10787 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10791 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10795 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10799 static struct TokenInfo shortcut_setup_tokens[] =
10803 &setup.shortcut.save_game, "shortcut.save_game"
10807 &setup.shortcut.load_game, "shortcut.load_game"
10811 &setup.shortcut.restart_game, "shortcut.restart_game"
10815 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10819 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10823 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10827 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10831 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10835 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10839 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10843 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10847 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10851 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10855 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10859 &setup.shortcut.tape_record, "shortcut.tape_record"
10863 &setup.shortcut.tape_play, "shortcut.tape_play"
10867 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10871 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10875 &setup.shortcut.sound_music, "shortcut.sound_music"
10879 &setup.shortcut.snap_left, "shortcut.snap_left"
10883 &setup.shortcut.snap_right, "shortcut.snap_right"
10887 &setup.shortcut.snap_up, "shortcut.snap_up"
10891 &setup.shortcut.snap_down, "shortcut.snap_down"
10895 static struct SetupInputInfo setup_input;
10896 static struct TokenInfo player_setup_tokens[] =
10900 &setup_input.use_joystick, ".use_joystick"
10904 &setup_input.joy.device_name, ".joy.device_name"
10908 &setup_input.joy.xleft, ".joy.xleft"
10912 &setup_input.joy.xmiddle, ".joy.xmiddle"
10916 &setup_input.joy.xright, ".joy.xright"
10920 &setup_input.joy.yupper, ".joy.yupper"
10924 &setup_input.joy.ymiddle, ".joy.ymiddle"
10928 &setup_input.joy.ylower, ".joy.ylower"
10932 &setup_input.joy.snap, ".joy.snap_field"
10936 &setup_input.joy.drop, ".joy.place_bomb"
10940 &setup_input.key.left, ".key.move_left"
10944 &setup_input.key.right, ".key.move_right"
10948 &setup_input.key.up, ".key.move_up"
10952 &setup_input.key.down, ".key.move_down"
10956 &setup_input.key.snap, ".key.snap_field"
10960 &setup_input.key.drop, ".key.place_bomb"
10964 static struct TokenInfo system_setup_tokens[] =
10968 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10972 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10976 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10980 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10984 static struct TokenInfo internal_setup_tokens[] =
10988 &setup.internal.program_title, "program_title"
10992 &setup.internal.program_version, "program_version"
10996 &setup.internal.program_author, "program_author"
11000 &setup.internal.program_email, "program_email"
11004 &setup.internal.program_website, "program_website"
11008 &setup.internal.program_copyright, "program_copyright"
11012 &setup.internal.program_company, "program_company"
11016 &setup.internal.program_icon_file, "program_icon_file"
11020 &setup.internal.default_graphics_set, "default_graphics_set"
11024 &setup.internal.default_sounds_set, "default_sounds_set"
11028 &setup.internal.default_music_set, "default_music_set"
11032 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11036 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11040 &setup.internal.fallback_music_file, "fallback_music_file"
11044 &setup.internal.default_level_series, "default_level_series"
11048 &setup.internal.default_window_width, "default_window_width"
11052 &setup.internal.default_window_height, "default_window_height"
11056 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11060 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11064 &setup.internal.create_user_levelset, "create_user_levelset"
11068 &setup.internal.info_screens_from_main, "info_screens_from_main"
11072 &setup.internal.menu_game, "menu_game"
11076 &setup.internal.menu_engines, "menu_engines"
11080 &setup.internal.menu_editor, "menu_editor"
11084 &setup.internal.menu_graphics, "menu_graphics"
11088 &setup.internal.menu_sound, "menu_sound"
11092 &setup.internal.menu_artwork, "menu_artwork"
11096 &setup.internal.menu_input, "menu_input"
11100 &setup.internal.menu_touch, "menu_touch"
11104 &setup.internal.menu_shortcuts, "menu_shortcuts"
11108 &setup.internal.menu_exit, "menu_exit"
11112 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11116 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11120 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11124 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11128 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11132 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11136 &setup.internal.info_title, "info_title"
11140 &setup.internal.info_elements, "info_elements"
11144 &setup.internal.info_music, "info_music"
11148 &setup.internal.info_credits, "info_credits"
11152 &setup.internal.info_program, "info_program"
11156 &setup.internal.info_version, "info_version"
11160 &setup.internal.info_levelset, "info_levelset"
11164 &setup.internal.info_exit, "info_exit"
11168 static struct TokenInfo debug_setup_tokens[] =
11172 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11176 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11180 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11184 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11188 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11192 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11196 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11200 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11204 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11208 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11212 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11216 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11220 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11224 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11228 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11232 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11236 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11240 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11244 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11248 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11252 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11255 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11259 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11263 &setup.debug.xsn_mode, "debug.xsn_mode"
11267 &setup.debug.xsn_percent, "debug.xsn_percent"
11271 static struct TokenInfo options_setup_tokens[] =
11275 &setup.options.verbose, "options.verbose"
11279 &setup.options.debug, "options.debug"
11283 &setup.options.debug_mode, "options.debug_mode"
11287 static void setSetupInfoToDefaults(struct SetupInfo *si)
11291 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11293 si->multiple_users = TRUE;
11296 si->sound_loops = TRUE;
11297 si->sound_music = TRUE;
11298 si->sound_simple = TRUE;
11300 si->global_animations = TRUE;
11301 si->scroll_delay = TRUE;
11302 si->forced_scroll_delay = FALSE;
11303 si->scroll_delay_value = STD_SCROLL_DELAY;
11304 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11305 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11306 si->fade_screens = TRUE;
11307 si->autorecord = TRUE;
11308 si->autorecord_after_replay = TRUE;
11309 si->auto_pause_on_start = FALSE;
11310 si->show_titlescreen = TRUE;
11311 si->quick_doors = FALSE;
11312 si->team_mode = FALSE;
11313 si->handicap = TRUE;
11314 si->skip_levels = TRUE;
11315 si->increment_levels = TRUE;
11316 si->auto_play_next_level = TRUE;
11317 si->count_score_after_game = TRUE;
11318 si->show_scores_after_game = TRUE;
11319 si->time_limit = TRUE;
11320 si->fullscreen = FALSE;
11321 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11322 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11323 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11324 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11325 si->ask_on_escape = TRUE;
11326 si->ask_on_escape_editor = TRUE;
11327 si->ask_on_game_over = TRUE;
11328 si->ask_on_quit_game = TRUE;
11329 si->ask_on_quit_program = TRUE;
11330 si->quick_switch = FALSE;
11331 si->input_on_focus = FALSE;
11332 si->prefer_aga_graphics = TRUE;
11333 si->prefer_lowpass_sounds = FALSE;
11334 si->prefer_extra_panel_items = TRUE;
11335 si->game_speed_extended = FALSE;
11336 si->game_frame_delay = GAME_FRAME_DELAY;
11337 si->bd_skip_uncovering = FALSE;
11338 si->bd_skip_hatching = FALSE;
11339 si->bd_scroll_delay = TRUE;
11340 si->bd_smooth_movements = AUTO;
11341 si->sp_show_border_elements = FALSE;
11342 si->small_game_graphics = FALSE;
11343 si->show_load_save_buttons = FALSE;
11344 si->show_undo_redo_buttons = FALSE;
11345 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11347 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11348 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11349 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11351 si->override_level_graphics = FALSE;
11352 si->override_level_sounds = FALSE;
11353 si->override_level_music = FALSE;
11355 si->volume_simple = 100; // percent
11356 si->volume_loops = 100; // percent
11357 si->volume_music = 100; // percent
11359 si->network_mode = FALSE;
11360 si->network_player_nr = 0; // first player
11361 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11363 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11364 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11365 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11366 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11367 si->touch.draw_outlined = TRUE;
11368 si->touch.draw_pressed = TRUE;
11370 for (i = 0; i < 2; i++)
11372 char *default_grid_button[6][2] =
11378 { "111222", " vv " },
11379 { "111222", " vv " }
11381 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11382 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11383 int min_xsize = MIN(6, grid_xsize);
11384 int min_ysize = MIN(6, grid_ysize);
11385 int startx = grid_xsize - min_xsize;
11386 int starty = grid_ysize - min_ysize;
11389 // virtual buttons grid can only be set to defaults if video is initialized
11390 // (this will be repeated if virtual buttons are not loaded from setup file)
11391 if (video.initialized)
11393 si->touch.grid_xsize[i] = grid_xsize;
11394 si->touch.grid_ysize[i] = grid_ysize;
11398 si->touch.grid_xsize[i] = -1;
11399 si->touch.grid_ysize[i] = -1;
11402 for (x = 0; x < MAX_GRID_XSIZE; x++)
11403 for (y = 0; y < MAX_GRID_YSIZE; y++)
11404 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11406 for (x = 0; x < min_xsize; x++)
11407 for (y = 0; y < min_ysize; y++)
11408 si->touch.grid_button[i][x][starty + y] =
11409 default_grid_button[y][0][x];
11411 for (x = 0; x < min_xsize; x++)
11412 for (y = 0; y < min_ysize; y++)
11413 si->touch.grid_button[i][startx + x][starty + y] =
11414 default_grid_button[y][1][x];
11417 si->touch.grid_initialized = video.initialized;
11419 si->touch.overlay_buttons = FALSE;
11421 si->editor.el_boulderdash = TRUE;
11422 si->editor.el_boulderdash_native = TRUE;
11423 si->editor.el_boulderdash_effects = TRUE;
11424 si->editor.el_emerald_mine = TRUE;
11425 si->editor.el_emerald_mine_club = TRUE;
11426 si->editor.el_more = TRUE;
11427 si->editor.el_sokoban = TRUE;
11428 si->editor.el_supaplex = TRUE;
11429 si->editor.el_diamond_caves = TRUE;
11430 si->editor.el_dx_boulderdash = TRUE;
11432 si->editor.el_mirror_magic = TRUE;
11433 si->editor.el_deflektor = TRUE;
11435 si->editor.el_chars = TRUE;
11436 si->editor.el_steel_chars = TRUE;
11438 si->editor.el_classic = TRUE;
11439 si->editor.el_custom = TRUE;
11441 si->editor.el_user_defined = FALSE;
11442 si->editor.el_dynamic = TRUE;
11444 si->editor.el_headlines = TRUE;
11446 si->editor.show_element_token = FALSE;
11448 si->editor.show_read_only_warning = TRUE;
11450 si->editor.use_template_for_new_levels = TRUE;
11452 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11453 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11454 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11455 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11456 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11458 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11459 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11460 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11461 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11462 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11464 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11465 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11466 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11467 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11468 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11469 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11471 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11472 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11473 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11475 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11476 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11477 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11478 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11480 for (i = 0; i < MAX_PLAYERS; i++)
11482 si->input[i].use_joystick = FALSE;
11483 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11484 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11485 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11486 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11487 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11488 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11489 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11490 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11491 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11492 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11493 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11494 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11495 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11496 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11497 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11500 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11501 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11502 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11503 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11505 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11506 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11507 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11508 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11509 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11510 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11511 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11513 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11515 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11516 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11517 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11519 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11520 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11521 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11523 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11524 si->internal.choose_from_top_leveldir = FALSE;
11525 si->internal.show_scaling_in_title = TRUE;
11526 si->internal.create_user_levelset = TRUE;
11527 si->internal.info_screens_from_main = FALSE;
11529 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11530 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11532 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11533 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11534 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11535 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11536 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11537 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11538 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11539 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11540 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11541 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11543 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11544 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11545 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11546 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11547 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11548 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11549 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11550 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11551 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11552 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11554 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11555 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11557 si->debug.show_frames_per_second = FALSE;
11559 si->debug.xsn_mode = AUTO;
11560 si->debug.xsn_percent = 0;
11562 si->options.verbose = FALSE;
11563 si->options.debug = FALSE;
11564 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11566 #if defined(PLATFORM_ANDROID)
11567 si->fullscreen = TRUE;
11568 si->touch.overlay_buttons = TRUE;
11571 setHideSetupEntry(&setup.debug.xsn_mode);
11574 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11576 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11579 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11581 si->player_uuid = NULL; // (will be set later)
11582 si->player_version = 1; // (will be set later)
11584 si->use_api_server = TRUE;
11585 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11586 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11587 si->ask_for_uploading_tapes = TRUE;
11588 si->ask_for_remaining_tapes = FALSE;
11589 si->provide_uploading_tapes = TRUE;
11590 si->ask_for_using_api_server = TRUE;
11591 si->has_remaining_tapes = FALSE;
11594 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11596 si->editor_cascade.el_bd = TRUE;
11597 si->editor_cascade.el_bd_native = TRUE;
11598 si->editor_cascade.el_bd_effects = FALSE;
11599 si->editor_cascade.el_em = TRUE;
11600 si->editor_cascade.el_emc = TRUE;
11601 si->editor_cascade.el_rnd = TRUE;
11602 si->editor_cascade.el_sb = TRUE;
11603 si->editor_cascade.el_sp = TRUE;
11604 si->editor_cascade.el_dc = TRUE;
11605 si->editor_cascade.el_dx = TRUE;
11607 si->editor_cascade.el_mm = TRUE;
11608 si->editor_cascade.el_df = TRUE;
11610 si->editor_cascade.el_chars = FALSE;
11611 si->editor_cascade.el_steel_chars = FALSE;
11612 si->editor_cascade.el_ce = FALSE;
11613 si->editor_cascade.el_ge = FALSE;
11614 si->editor_cascade.el_es = FALSE;
11615 si->editor_cascade.el_ref = FALSE;
11616 si->editor_cascade.el_user = FALSE;
11617 si->editor_cascade.el_dynamic = FALSE;
11620 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11622 static char *getHideSetupToken(void *setup_value)
11624 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11626 if (setup_value != NULL)
11627 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11629 return hide_setup_token;
11632 void setHideSetupEntry(void *setup_value)
11634 char *hide_setup_token = getHideSetupToken(setup_value);
11636 if (hide_setup_hash == NULL)
11637 hide_setup_hash = newSetupFileHash();
11639 if (setup_value != NULL)
11640 setHashEntry(hide_setup_hash, hide_setup_token, "");
11643 void removeHideSetupEntry(void *setup_value)
11645 char *hide_setup_token = getHideSetupToken(setup_value);
11647 if (setup_value != NULL)
11648 removeHashEntry(hide_setup_hash, hide_setup_token);
11651 boolean hideSetupEntry(void *setup_value)
11653 char *hide_setup_token = getHideSetupToken(setup_value);
11655 return (setup_value != NULL &&
11656 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11659 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11660 struct TokenInfo *token_info,
11661 int token_nr, char *token_text)
11663 char *token_hide_text = getStringCat2(token_text, ".hide");
11664 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11666 // set the value of this setup option in the setup option structure
11667 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11669 // check if this setup option should be hidden in the setup menu
11670 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11671 setHideSetupEntry(token_info[token_nr].value);
11673 free(token_hide_text);
11676 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11677 struct TokenInfo *token_info,
11680 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11681 token_info[token_nr].text);
11684 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11688 if (!setup_file_hash)
11691 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11692 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11694 setup.touch.grid_initialized = TRUE;
11695 for (i = 0; i < 2; i++)
11697 int grid_xsize = setup.touch.grid_xsize[i];
11698 int grid_ysize = setup.touch.grid_ysize[i];
11701 // if virtual buttons are not loaded from setup file, repeat initializing
11702 // virtual buttons grid with default values later when video is initialized
11703 if (grid_xsize == -1 ||
11706 setup.touch.grid_initialized = FALSE;
11711 for (y = 0; y < grid_ysize; y++)
11713 char token_string[MAX_LINE_LEN];
11715 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11717 char *value_string = getHashEntry(setup_file_hash, token_string);
11719 if (value_string == NULL)
11722 for (x = 0; x < grid_xsize; x++)
11724 char c = value_string[x];
11726 setup.touch.grid_button[i][x][y] =
11727 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11732 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11733 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11735 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11736 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11738 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11742 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11744 setup_input = setup.input[pnr];
11745 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11747 char full_token[100];
11749 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11750 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11753 setup.input[pnr] = setup_input;
11756 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11757 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11759 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11760 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11762 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11763 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11765 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11766 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11768 setHideRelatedSetupEntries();
11771 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11775 if (!setup_file_hash)
11778 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11779 setSetupInfo(auto_setup_tokens, i,
11780 getHashEntry(setup_file_hash,
11781 auto_setup_tokens[i].text));
11784 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11788 if (!setup_file_hash)
11791 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11792 setSetupInfo(server_setup_tokens, i,
11793 getHashEntry(setup_file_hash,
11794 server_setup_tokens[i].text));
11797 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11801 if (!setup_file_hash)
11804 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11805 setSetupInfo(editor_cascade_setup_tokens, i,
11806 getHashEntry(setup_file_hash,
11807 editor_cascade_setup_tokens[i].text));
11810 void LoadUserNames(void)
11812 int last_user_nr = user.nr;
11815 if (global.user_names != NULL)
11817 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11818 checked_free(global.user_names[i]);
11820 checked_free(global.user_names);
11823 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11825 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11829 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11831 if (setup_file_hash)
11833 char *player_name = getHashEntry(setup_file_hash, "player_name");
11835 global.user_names[i] = getFixedUserName(player_name);
11837 freeSetupFileHash(setup_file_hash);
11840 if (global.user_names[i] == NULL)
11841 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11844 user.nr = last_user_nr;
11847 void LoadSetupFromFilename(char *filename)
11849 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11851 if (setup_file_hash)
11853 decodeSetupFileHash_Default(setup_file_hash);
11855 freeSetupFileHash(setup_file_hash);
11859 Debug("setup", "using default setup values");
11863 static void LoadSetup_SpecialPostProcessing(void)
11865 char *player_name_new;
11867 // needed to work around problems with fixed length strings
11868 player_name_new = getFixedUserName(setup.player_name);
11869 free(setup.player_name);
11870 setup.player_name = player_name_new;
11872 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11873 if (setup.scroll_delay == FALSE)
11875 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11876 setup.scroll_delay = TRUE; // now always "on"
11879 // make sure that scroll delay value stays inside valid range
11880 setup.scroll_delay_value =
11881 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11884 void LoadSetup_Default(void)
11888 // always start with reliable default values
11889 setSetupInfoToDefaults(&setup);
11891 // try to load setup values from default setup file
11892 filename = getDefaultSetupFilename();
11894 if (fileExists(filename))
11895 LoadSetupFromFilename(filename);
11897 // try to load setup values from platform setup file
11898 filename = getPlatformSetupFilename();
11900 if (fileExists(filename))
11901 LoadSetupFromFilename(filename);
11903 // try to load setup values from user setup file
11904 filename = getSetupFilename();
11906 LoadSetupFromFilename(filename);
11908 LoadSetup_SpecialPostProcessing();
11911 void LoadSetup_AutoSetup(void)
11913 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11914 SetupFileHash *setup_file_hash = NULL;
11916 // always start with reliable default values
11917 setSetupInfoToDefaults_AutoSetup(&setup);
11919 setup_file_hash = loadSetupFileHash(filename);
11921 if (setup_file_hash)
11923 decodeSetupFileHash_AutoSetup(setup_file_hash);
11925 freeSetupFileHash(setup_file_hash);
11931 void LoadSetup_ServerSetup(void)
11933 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11934 SetupFileHash *setup_file_hash = NULL;
11936 // always start with reliable default values
11937 setSetupInfoToDefaults_ServerSetup(&setup);
11939 setup_file_hash = loadSetupFileHash(filename);
11941 if (setup_file_hash)
11943 decodeSetupFileHash_ServerSetup(setup_file_hash);
11945 freeSetupFileHash(setup_file_hash);
11950 if (setup.player_uuid == NULL)
11952 // player UUID does not yet exist in setup file
11953 setup.player_uuid = getStringCopy(getUUID());
11954 setup.player_version = 2;
11956 SaveSetup_ServerSetup();
11960 void LoadSetup_EditorCascade(void)
11962 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11963 SetupFileHash *setup_file_hash = NULL;
11965 // always start with reliable default values
11966 setSetupInfoToDefaults_EditorCascade(&setup);
11968 setup_file_hash = loadSetupFileHash(filename);
11970 if (setup_file_hash)
11972 decodeSetupFileHash_EditorCascade(setup_file_hash);
11974 freeSetupFileHash(setup_file_hash);
11980 void LoadSetup(void)
11982 LoadSetup_Default();
11983 LoadSetup_AutoSetup();
11984 LoadSetup_ServerSetup();
11985 LoadSetup_EditorCascade();
11988 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11989 char *mapping_line)
11991 char mapping_guid[MAX_LINE_LEN];
11992 char *mapping_start, *mapping_end;
11994 // get GUID from game controller mapping line: copy complete line
11995 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11996 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11998 // get GUID from game controller mapping line: cut after GUID part
11999 mapping_start = strchr(mapping_guid, ',');
12000 if (mapping_start != NULL)
12001 *mapping_start = '\0';
12003 // cut newline from game controller mapping line
12004 mapping_end = strchr(mapping_line, '\n');
12005 if (mapping_end != NULL)
12006 *mapping_end = '\0';
12008 // add mapping entry to game controller mappings hash
12009 setHashEntry(mappings_hash, mapping_guid, mapping_line);
12012 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12017 if (!(file = fopen(filename, MODE_READ)))
12019 Warn("cannot read game controller mappings file '%s'", filename);
12024 while (!feof(file))
12026 char line[MAX_LINE_LEN];
12028 if (!fgets(line, MAX_LINE_LEN, file))
12031 addGameControllerMappingToHash(mappings_hash, line);
12037 void SaveSetup_Default(void)
12039 char *filename = getSetupFilename();
12043 InitUserDataDirectory();
12045 if (!(file = fopen(filename, MODE_WRITE)))
12047 Warn("cannot write setup file '%s'", filename);
12052 fprintFileHeader(file, SETUP_FILENAME);
12054 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12056 // just to make things nicer :)
12057 if (global_setup_tokens[i].value == &setup.multiple_users ||
12058 global_setup_tokens[i].value == &setup.sound ||
12059 global_setup_tokens[i].value == &setup.graphics_set ||
12060 global_setup_tokens[i].value == &setup.volume_simple ||
12061 global_setup_tokens[i].value == &setup.network_mode ||
12062 global_setup_tokens[i].value == &setup.touch.control_type ||
12063 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12064 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12065 fprintf(file, "\n");
12067 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12070 for (i = 0; i < 2; i++)
12072 int grid_xsize = setup.touch.grid_xsize[i];
12073 int grid_ysize = setup.touch.grid_ysize[i];
12076 fprintf(file, "\n");
12078 for (y = 0; y < grid_ysize; y++)
12080 char token_string[MAX_LINE_LEN];
12081 char value_string[MAX_LINE_LEN];
12083 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12085 for (x = 0; x < grid_xsize; x++)
12087 char c = setup.touch.grid_button[i][x][y];
12089 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12092 value_string[grid_xsize] = '\0';
12094 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12098 fprintf(file, "\n");
12099 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12100 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12102 fprintf(file, "\n");
12103 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12104 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12106 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12110 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12111 fprintf(file, "\n");
12113 setup_input = setup.input[pnr];
12114 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12115 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12118 fprintf(file, "\n");
12119 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12120 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12122 // (internal setup values not saved to user setup file)
12124 fprintf(file, "\n");
12125 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12126 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12127 setup.debug.xsn_mode != AUTO)
12128 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12130 fprintf(file, "\n");
12131 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12132 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12136 SetFilePermissions(filename, PERMS_PRIVATE);
12139 void SaveSetup_AutoSetup(void)
12141 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12145 InitUserDataDirectory();
12147 if (!(file = fopen(filename, MODE_WRITE)))
12149 Warn("cannot write auto setup file '%s'", filename);
12156 fprintFileHeader(file, AUTOSETUP_FILENAME);
12158 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12159 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12163 SetFilePermissions(filename, PERMS_PRIVATE);
12168 void SaveSetup_ServerSetup(void)
12170 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12174 InitUserDataDirectory();
12176 if (!(file = fopen(filename, MODE_WRITE)))
12178 Warn("cannot write server setup file '%s'", filename);
12185 fprintFileHeader(file, SERVERSETUP_FILENAME);
12187 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12189 // just to make things nicer :)
12190 if (server_setup_tokens[i].value == &setup.use_api_server)
12191 fprintf(file, "\n");
12193 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12198 SetFilePermissions(filename, PERMS_PRIVATE);
12203 void SaveSetup_EditorCascade(void)
12205 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12209 InitUserDataDirectory();
12211 if (!(file = fopen(filename, MODE_WRITE)))
12213 Warn("cannot write editor cascade state file '%s'", filename);
12220 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12222 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12223 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12227 SetFilePermissions(filename, PERMS_PRIVATE);
12232 void SaveSetup(void)
12234 SaveSetup_Default();
12235 SaveSetup_AutoSetup();
12236 SaveSetup_ServerSetup();
12237 SaveSetup_EditorCascade();
12240 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12245 if (!(file = fopen(filename, MODE_WRITE)))
12247 Warn("cannot write game controller mappings file '%s'", filename);
12252 BEGIN_HASH_ITERATION(mappings_hash, itr)
12254 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12256 END_HASH_ITERATION(mappings_hash, itr)
12261 void SaveSetup_AddGameControllerMapping(char *mapping)
12263 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12264 SetupFileHash *mappings_hash = newSetupFileHash();
12266 InitUserDataDirectory();
12268 // load existing personal game controller mappings
12269 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12271 // add new mapping to personal game controller mappings
12272 addGameControllerMappingToHash(mappings_hash, mapping);
12274 // save updated personal game controller mappings
12275 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12277 freeSetupFileHash(mappings_hash);
12281 void LoadCustomElementDescriptions(void)
12283 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12284 SetupFileHash *setup_file_hash;
12287 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12289 if (element_info[i].custom_description != NULL)
12291 free(element_info[i].custom_description);
12292 element_info[i].custom_description = NULL;
12296 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12299 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12301 char *token = getStringCat2(element_info[i].token_name, ".name");
12302 char *value = getHashEntry(setup_file_hash, token);
12305 element_info[i].custom_description = getStringCopy(value);
12310 freeSetupFileHash(setup_file_hash);
12313 static int getElementFromToken(char *token)
12315 char *value = getHashEntry(element_token_hash, token);
12318 return atoi(value);
12320 Warn("unknown element token '%s'", token);
12322 return EL_UNDEFINED;
12325 void FreeGlobalAnimEventInfo(void)
12327 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12329 if (gaei->event_list == NULL)
12334 for (i = 0; i < gaei->num_event_lists; i++)
12336 checked_free(gaei->event_list[i]->event_value);
12337 checked_free(gaei->event_list[i]);
12340 checked_free(gaei->event_list);
12342 gaei->event_list = NULL;
12343 gaei->num_event_lists = 0;
12346 static int AddGlobalAnimEventList(void)
12348 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12349 int list_pos = gaei->num_event_lists++;
12351 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12352 sizeof(struct GlobalAnimEventListInfo *));
12354 gaei->event_list[list_pos] =
12355 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12357 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12359 gaeli->event_value = NULL;
12360 gaeli->num_event_values = 0;
12365 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12367 // do not add empty global animation events
12368 if (event_value == ANIM_EVENT_NONE)
12371 // if list position is undefined, create new list
12372 if (list_pos == ANIM_EVENT_UNDEFINED)
12373 list_pos = AddGlobalAnimEventList();
12375 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12376 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12377 int value_pos = gaeli->num_event_values++;
12379 gaeli->event_value = checked_realloc(gaeli->event_value,
12380 gaeli->num_event_values * sizeof(int *));
12382 gaeli->event_value[value_pos] = event_value;
12387 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12389 if (list_pos == ANIM_EVENT_UNDEFINED)
12390 return ANIM_EVENT_NONE;
12392 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12393 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12395 return gaeli->event_value[value_pos];
12398 int GetGlobalAnimEventValueCount(int list_pos)
12400 if (list_pos == ANIM_EVENT_UNDEFINED)
12403 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12404 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12406 return gaeli->num_event_values;
12409 // This function checks if a string <s> of the format "string1, string2, ..."
12410 // exactly contains a string <s_contained>.
12412 static boolean string_has_parameter(char *s, char *s_contained)
12416 if (s == NULL || s_contained == NULL)
12419 if (strlen(s_contained) > strlen(s))
12422 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12424 char next_char = s[strlen(s_contained)];
12426 // check if next character is delimiter or whitespace
12427 if (next_char == ',' || next_char == '\0' ||
12428 next_char == ' ' || next_char == '\t')
12432 // check if string contains another parameter string after a comma
12433 substring = strchr(s, ',');
12434 if (substring == NULL) // string does not contain a comma
12437 // advance string pointer to next character after the comma
12440 // skip potential whitespaces after the comma
12441 while (*substring == ' ' || *substring == '\t')
12444 return string_has_parameter(substring, s_contained);
12447 static int get_anim_parameter_value_ce(char *s)
12450 char *pattern_1 = "ce_change:custom_";
12451 char *pattern_2 = ".page_";
12452 int pattern_1_len = strlen(pattern_1);
12453 char *matching_char = strstr(s_ptr, pattern_1);
12454 int result = ANIM_EVENT_NONE;
12456 if (matching_char == NULL)
12457 return ANIM_EVENT_NONE;
12459 result = ANIM_EVENT_CE_CHANGE;
12461 s_ptr = matching_char + pattern_1_len;
12463 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12464 if (*s_ptr >= '0' && *s_ptr <= '9')
12466 int gic_ce_nr = (*s_ptr++ - '0');
12468 if (*s_ptr >= '0' && *s_ptr <= '9')
12470 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12472 if (*s_ptr >= '0' && *s_ptr <= '9')
12473 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12476 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12477 return ANIM_EVENT_NONE;
12479 // custom element stored as 0 to 255
12482 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12486 // invalid custom element number specified
12488 return ANIM_EVENT_NONE;
12491 // check for change page number ("page_X" or "page_XX") (optional)
12492 if (strPrefix(s_ptr, pattern_2))
12494 s_ptr += strlen(pattern_2);
12496 if (*s_ptr >= '0' && *s_ptr <= '9')
12498 int gic_page_nr = (*s_ptr++ - '0');
12500 if (*s_ptr >= '0' && *s_ptr <= '9')
12501 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12503 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12504 return ANIM_EVENT_NONE;
12506 // change page stored as 1 to 32 (0 means "all change pages")
12508 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12512 // invalid animation part number specified
12514 return ANIM_EVENT_NONE;
12518 // discard result if next character is neither delimiter nor whitespace
12519 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12520 *s_ptr == ' ' || *s_ptr == '\t'))
12521 return ANIM_EVENT_NONE;
12526 static int get_anim_parameter_value(char *s)
12528 int event_value[] =
12536 char *pattern_1[] =
12544 char *pattern_2 = ".part_";
12545 char *matching_char = NULL;
12547 int pattern_1_len = 0;
12548 int result = ANIM_EVENT_NONE;
12551 result = get_anim_parameter_value_ce(s);
12553 if (result != ANIM_EVENT_NONE)
12556 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12558 matching_char = strstr(s_ptr, pattern_1[i]);
12559 pattern_1_len = strlen(pattern_1[i]);
12560 result = event_value[i];
12562 if (matching_char != NULL)
12566 if (matching_char == NULL)
12567 return ANIM_EVENT_NONE;
12569 s_ptr = matching_char + pattern_1_len;
12571 // check for main animation number ("anim_X" or "anim_XX")
12572 if (*s_ptr >= '0' && *s_ptr <= '9')
12574 int gic_anim_nr = (*s_ptr++ - '0');
12576 if (*s_ptr >= '0' && *s_ptr <= '9')
12577 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12579 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12580 return ANIM_EVENT_NONE;
12582 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12586 // invalid main animation number specified
12588 return ANIM_EVENT_NONE;
12591 // check for animation part number ("part_X" or "part_XX") (optional)
12592 if (strPrefix(s_ptr, pattern_2))
12594 s_ptr += strlen(pattern_2);
12596 if (*s_ptr >= '0' && *s_ptr <= '9')
12598 int gic_part_nr = (*s_ptr++ - '0');
12600 if (*s_ptr >= '0' && *s_ptr <= '9')
12601 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12603 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12604 return ANIM_EVENT_NONE;
12606 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12610 // invalid animation part number specified
12612 return ANIM_EVENT_NONE;
12616 // discard result if next character is neither delimiter nor whitespace
12617 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12618 *s_ptr == ' ' || *s_ptr == '\t'))
12619 return ANIM_EVENT_NONE;
12624 static int get_anim_parameter_values(char *s)
12626 int list_pos = ANIM_EVENT_UNDEFINED;
12627 int event_value = ANIM_EVENT_DEFAULT;
12629 if (string_has_parameter(s, "any"))
12630 event_value |= ANIM_EVENT_ANY;
12632 if (string_has_parameter(s, "click:self") ||
12633 string_has_parameter(s, "click") ||
12634 string_has_parameter(s, "self"))
12635 event_value |= ANIM_EVENT_SELF;
12637 if (string_has_parameter(s, "unclick:any"))
12638 event_value |= ANIM_EVENT_UNCLICK_ANY;
12640 // if animation event found, add it to global animation event list
12641 if (event_value != ANIM_EVENT_NONE)
12642 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12646 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12647 event_value = get_anim_parameter_value(s);
12649 // if animation event found, add it to global animation event list
12650 if (event_value != ANIM_EVENT_NONE)
12651 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12653 // continue with next part of the string, starting with next comma
12654 s = strchr(s + 1, ',');
12660 static int get_anim_action_parameter_value(char *token)
12662 // check most common default case first to massively speed things up
12663 if (strEqual(token, ARG_UNDEFINED))
12664 return ANIM_EVENT_ACTION_NONE;
12666 int result = getImageIDFromToken(token);
12670 char *gfx_token = getStringCat2("gfx.", token);
12672 result = getImageIDFromToken(gfx_token);
12674 checked_free(gfx_token);
12679 Key key = getKeyFromX11KeyName(token);
12681 if (key != KSYM_UNDEFINED)
12682 result = -(int)key;
12689 result = get_hash_from_string(token); // unsigned int => int
12690 result = ABS(result); // may be negative now
12691 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12693 setHashEntry(anim_url_hash, int2str(result, 0), token);
12698 result = ANIM_EVENT_ACTION_NONE;
12703 int get_parameter_value(char *value_raw, char *suffix, int type)
12705 char *value = getStringToLower(value_raw);
12706 int result = 0; // probably a save default value
12708 if (strEqual(suffix, ".direction"))
12710 result = (strEqual(value, "left") ? MV_LEFT :
12711 strEqual(value, "right") ? MV_RIGHT :
12712 strEqual(value, "up") ? MV_UP :
12713 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12715 else if (strEqual(suffix, ".position"))
12717 result = (strEqual(value, "left") ? POS_LEFT :
12718 strEqual(value, "right") ? POS_RIGHT :
12719 strEqual(value, "top") ? POS_TOP :
12720 strEqual(value, "upper") ? POS_UPPER :
12721 strEqual(value, "middle") ? POS_MIDDLE :
12722 strEqual(value, "lower") ? POS_LOWER :
12723 strEqual(value, "bottom") ? POS_BOTTOM :
12724 strEqual(value, "any") ? POS_ANY :
12725 strEqual(value, "ce") ? POS_CE :
12726 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12727 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12729 else if (strEqual(suffix, ".align"))
12731 result = (strEqual(value, "left") ? ALIGN_LEFT :
12732 strEqual(value, "right") ? ALIGN_RIGHT :
12733 strEqual(value, "center") ? ALIGN_CENTER :
12734 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12736 else if (strEqual(suffix, ".valign"))
12738 result = (strEqual(value, "top") ? VALIGN_TOP :
12739 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12740 strEqual(value, "middle") ? VALIGN_MIDDLE :
12741 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12743 else if (strEqual(suffix, ".anim_mode"))
12745 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12746 string_has_parameter(value, "loop") ? ANIM_LOOP :
12747 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12748 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12749 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12750 string_has_parameter(value, "random") ? ANIM_RANDOM :
12751 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12752 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12753 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12754 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12755 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12756 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12757 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12758 string_has_parameter(value, "all") ? ANIM_ALL :
12759 string_has_parameter(value, "tiled") ? ANIM_TILED :
12760 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12763 if (string_has_parameter(value, "once"))
12764 result |= ANIM_ONCE;
12766 if (string_has_parameter(value, "reverse"))
12767 result |= ANIM_REVERSE;
12769 if (string_has_parameter(value, "opaque_player"))
12770 result |= ANIM_OPAQUE_PLAYER;
12772 if (string_has_parameter(value, "static_panel"))
12773 result |= ANIM_STATIC_PANEL;
12775 else if (strEqual(suffix, ".init_event") ||
12776 strEqual(suffix, ".anim_event"))
12778 result = get_anim_parameter_values(value);
12780 else if (strEqual(suffix, ".init_delay_action") ||
12781 strEqual(suffix, ".anim_delay_action") ||
12782 strEqual(suffix, ".post_delay_action") ||
12783 strEqual(suffix, ".init_event_action") ||
12784 strEqual(suffix, ".anim_event_action"))
12786 result = get_anim_action_parameter_value(value_raw);
12788 else if (strEqual(suffix, ".class"))
12790 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12791 get_hash_from_string(value));
12793 else if (strEqual(suffix, ".style"))
12795 result = STYLE_DEFAULT;
12797 if (string_has_parameter(value, "accurate_borders"))
12798 result |= STYLE_ACCURATE_BORDERS;
12800 if (string_has_parameter(value, "inner_corners"))
12801 result |= STYLE_INNER_CORNERS;
12803 if (string_has_parameter(value, "reverse"))
12804 result |= STYLE_REVERSE;
12806 if (string_has_parameter(value, "leftmost_position"))
12807 result |= STYLE_LEFTMOST_POSITION;
12809 if (string_has_parameter(value, "block_clicks"))
12810 result |= STYLE_BLOCK;
12812 if (string_has_parameter(value, "passthrough_clicks"))
12813 result |= STYLE_PASSTHROUGH;
12815 if (string_has_parameter(value, "multiple_actions"))
12816 result |= STYLE_MULTIPLE_ACTIONS;
12818 if (string_has_parameter(value, "consume_ce_event"))
12819 result |= STYLE_CONSUME_CE_EVENT;
12821 else if (strEqual(suffix, ".fade_mode"))
12823 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12824 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12825 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12826 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12827 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12828 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12829 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12830 FADE_MODE_DEFAULT);
12832 else if (strEqual(suffix, ".auto_delay_unit"))
12834 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12835 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12836 AUTO_DELAY_UNIT_DEFAULT);
12838 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12840 result = gfx.get_font_from_token_function(value);
12842 else // generic parameter of type integer or boolean
12844 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12845 type == TYPE_INTEGER ? get_integer_from_string(value) :
12846 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12847 ARG_UNDEFINED_VALUE);
12855 static int get_token_parameter_value(char *token, char *value_raw)
12859 if (token == NULL || value_raw == NULL)
12860 return ARG_UNDEFINED_VALUE;
12862 suffix = strrchr(token, '.');
12863 if (suffix == NULL)
12866 if (strEqual(suffix, ".element"))
12867 return getElementFromToken(value_raw);
12869 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12870 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12873 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12874 boolean ignore_defaults)
12878 for (i = 0; image_config_vars[i].token != NULL; i++)
12880 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12882 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12883 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12887 *image_config_vars[i].value =
12888 get_token_parameter_value(image_config_vars[i].token, value);
12892 void InitMenuDesignSettings_Static(void)
12894 // always start with reliable default values from static default config
12895 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12898 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12902 // the following initializes hierarchical values from static configuration
12904 // special case: initialize "ARG_DEFAULT" values in static default config
12905 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12906 titlescreen_initial_first_default.fade_mode =
12907 title_initial_first_default.fade_mode;
12908 titlescreen_initial_first_default.fade_delay =
12909 title_initial_first_default.fade_delay;
12910 titlescreen_initial_first_default.post_delay =
12911 title_initial_first_default.post_delay;
12912 titlescreen_initial_first_default.auto_delay =
12913 title_initial_first_default.auto_delay;
12914 titlescreen_initial_first_default.auto_delay_unit =
12915 title_initial_first_default.auto_delay_unit;
12916 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12917 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12918 titlescreen_first_default.post_delay = title_first_default.post_delay;
12919 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12920 titlescreen_first_default.auto_delay_unit =
12921 title_first_default.auto_delay_unit;
12922 titlemessage_initial_first_default.fade_mode =
12923 title_initial_first_default.fade_mode;
12924 titlemessage_initial_first_default.fade_delay =
12925 title_initial_first_default.fade_delay;
12926 titlemessage_initial_first_default.post_delay =
12927 title_initial_first_default.post_delay;
12928 titlemessage_initial_first_default.auto_delay =
12929 title_initial_first_default.auto_delay;
12930 titlemessage_initial_first_default.auto_delay_unit =
12931 title_initial_first_default.auto_delay_unit;
12932 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12933 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12934 titlemessage_first_default.post_delay = title_first_default.post_delay;
12935 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12936 titlemessage_first_default.auto_delay_unit =
12937 title_first_default.auto_delay_unit;
12939 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12940 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12941 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12942 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12943 titlescreen_initial_default.auto_delay_unit =
12944 title_initial_default.auto_delay_unit;
12945 titlescreen_default.fade_mode = title_default.fade_mode;
12946 titlescreen_default.fade_delay = title_default.fade_delay;
12947 titlescreen_default.post_delay = title_default.post_delay;
12948 titlescreen_default.auto_delay = title_default.auto_delay;
12949 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12950 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12951 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12952 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12953 titlemessage_initial_default.auto_delay_unit =
12954 title_initial_default.auto_delay_unit;
12955 titlemessage_default.fade_mode = title_default.fade_mode;
12956 titlemessage_default.fade_delay = title_default.fade_delay;
12957 titlemessage_default.post_delay = title_default.post_delay;
12958 titlemessage_default.auto_delay = title_default.auto_delay;
12959 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12961 // special case: initialize "ARG_DEFAULT" values in static default config
12962 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12963 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12965 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12966 titlescreen_first[i] = titlescreen_first_default;
12967 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12968 titlemessage_first[i] = titlemessage_first_default;
12970 titlescreen_initial[i] = titlescreen_initial_default;
12971 titlescreen[i] = titlescreen_default;
12972 titlemessage_initial[i] = titlemessage_initial_default;
12973 titlemessage[i] = titlemessage_default;
12976 // special case: initialize "ARG_DEFAULT" values in static default config
12977 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12978 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12980 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12983 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12984 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12985 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12988 // special case: initialize "ARG_DEFAULT" values in static default config
12989 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12990 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12992 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12993 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12994 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12996 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12999 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13003 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13007 struct XY *dst, *src;
13009 game_buttons_xy[] =
13011 { &game.button.save, &game.button.stop },
13012 { &game.button.pause2, &game.button.pause },
13013 { &game.button.load, &game.button.play },
13014 { &game.button.undo, &game.button.stop },
13015 { &game.button.redo, &game.button.play },
13021 // special case: initialize later added SETUP list size from LEVELS value
13022 if (menu.list_size[GAME_MODE_SETUP] == -1)
13023 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13025 // set default position for snapshot buttons to stop/pause/play buttons
13026 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13027 if ((*game_buttons_xy[i].dst).x == -1 &&
13028 (*game_buttons_xy[i].dst).y == -1)
13029 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13031 // --------------------------------------------------------------------------
13032 // dynamic viewports (including playfield margins, borders and alignments)
13033 // --------------------------------------------------------------------------
13035 // dynamic viewports currently only supported for landscape mode
13036 int display_width = MAX(video.display_width, video.display_height);
13037 int display_height = MIN(video.display_width, video.display_height);
13039 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13041 struct RectWithBorder *vp_window = &viewport.window[i];
13042 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13043 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13044 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13045 boolean dynamic_window_width = (vp_window->min_width != -1);
13046 boolean dynamic_window_height = (vp_window->min_height != -1);
13047 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13048 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13050 // adjust window size if min/max width/height is specified
13052 if (vp_window->min_width != -1)
13054 int window_width = display_width;
13056 // when using static window height, use aspect ratio of display
13057 if (vp_window->min_height == -1)
13058 window_width = vp_window->height * display_width / display_height;
13060 vp_window->width = MAX(vp_window->min_width, window_width);
13063 if (vp_window->min_height != -1)
13065 int window_height = display_height;
13067 // when using static window width, use aspect ratio of display
13068 if (vp_window->min_width == -1)
13069 window_height = vp_window->width * display_height / display_width;
13071 vp_window->height = MAX(vp_window->min_height, window_height);
13074 if (vp_window->max_width != -1)
13075 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13077 if (vp_window->max_height != -1)
13078 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13080 int playfield_width = vp_window->width;
13081 int playfield_height = vp_window->height;
13083 // adjust playfield size and position according to specified margins
13085 playfield_width -= vp_playfield->margin_left;
13086 playfield_width -= vp_playfield->margin_right;
13088 playfield_height -= vp_playfield->margin_top;
13089 playfield_height -= vp_playfield->margin_bottom;
13091 // adjust playfield size if min/max width/height is specified
13093 if (vp_playfield->min_width != -1)
13094 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13096 if (vp_playfield->min_height != -1)
13097 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13099 if (vp_playfield->max_width != -1)
13100 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13102 if (vp_playfield->max_height != -1)
13103 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13105 // adjust playfield position according to specified alignment
13107 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13108 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13109 else if (vp_playfield->align == ALIGN_CENTER)
13110 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13111 else if (vp_playfield->align == ALIGN_RIGHT)
13112 vp_playfield->x += playfield_width - vp_playfield->width;
13114 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13115 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13116 else if (vp_playfield->valign == VALIGN_MIDDLE)
13117 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13118 else if (vp_playfield->valign == VALIGN_BOTTOM)
13119 vp_playfield->y += playfield_height - vp_playfield->height;
13121 vp_playfield->x += vp_playfield->margin_left;
13122 vp_playfield->y += vp_playfield->margin_top;
13124 // adjust individual playfield borders if only default border is specified
13126 if (vp_playfield->border_left == -1)
13127 vp_playfield->border_left = vp_playfield->border_size;
13128 if (vp_playfield->border_right == -1)
13129 vp_playfield->border_right = vp_playfield->border_size;
13130 if (vp_playfield->border_top == -1)
13131 vp_playfield->border_top = vp_playfield->border_size;
13132 if (vp_playfield->border_bottom == -1)
13133 vp_playfield->border_bottom = vp_playfield->border_size;
13135 // set dynamic playfield borders if borders are specified as undefined
13136 // (but only if window size was dynamic and playfield size was static)
13138 if (dynamic_window_width && !dynamic_playfield_width)
13140 if (vp_playfield->border_left == -1)
13142 vp_playfield->border_left = (vp_playfield->x -
13143 vp_playfield->margin_left);
13144 vp_playfield->x -= vp_playfield->border_left;
13145 vp_playfield->width += vp_playfield->border_left;
13148 if (vp_playfield->border_right == -1)
13150 vp_playfield->border_right = (vp_window->width -
13152 vp_playfield->width -
13153 vp_playfield->margin_right);
13154 vp_playfield->width += vp_playfield->border_right;
13158 if (dynamic_window_height && !dynamic_playfield_height)
13160 if (vp_playfield->border_top == -1)
13162 vp_playfield->border_top = (vp_playfield->y -
13163 vp_playfield->margin_top);
13164 vp_playfield->y -= vp_playfield->border_top;
13165 vp_playfield->height += vp_playfield->border_top;
13168 if (vp_playfield->border_bottom == -1)
13170 vp_playfield->border_bottom = (vp_window->height -
13172 vp_playfield->height -
13173 vp_playfield->margin_bottom);
13174 vp_playfield->height += vp_playfield->border_bottom;
13178 // adjust playfield size to be a multiple of a defined alignment tile size
13180 int align_size = vp_playfield->align_size;
13181 int playfield_xtiles = vp_playfield->width / align_size;
13182 int playfield_ytiles = vp_playfield->height / align_size;
13183 int playfield_width_corrected = playfield_xtiles * align_size;
13184 int playfield_height_corrected = playfield_ytiles * align_size;
13185 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13186 i == GFX_SPECIAL_ARG_EDITOR);
13188 if (is_playfield_mode &&
13189 dynamic_playfield_width &&
13190 vp_playfield->width != playfield_width_corrected)
13192 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13194 vp_playfield->width = playfield_width_corrected;
13196 if (vp_playfield->align == ALIGN_LEFT)
13198 vp_playfield->border_left += playfield_xdiff;
13200 else if (vp_playfield->align == ALIGN_RIGHT)
13202 vp_playfield->border_right += playfield_xdiff;
13204 else if (vp_playfield->align == ALIGN_CENTER)
13206 int border_left_diff = playfield_xdiff / 2;
13207 int border_right_diff = playfield_xdiff - border_left_diff;
13209 vp_playfield->border_left += border_left_diff;
13210 vp_playfield->border_right += border_right_diff;
13214 if (is_playfield_mode &&
13215 dynamic_playfield_height &&
13216 vp_playfield->height != playfield_height_corrected)
13218 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13220 vp_playfield->height = playfield_height_corrected;
13222 if (vp_playfield->valign == VALIGN_TOP)
13224 vp_playfield->border_top += playfield_ydiff;
13226 else if (vp_playfield->align == VALIGN_BOTTOM)
13228 vp_playfield->border_right += playfield_ydiff;
13230 else if (vp_playfield->align == VALIGN_MIDDLE)
13232 int border_top_diff = playfield_ydiff / 2;
13233 int border_bottom_diff = playfield_ydiff - border_top_diff;
13235 vp_playfield->border_top += border_top_diff;
13236 vp_playfield->border_bottom += border_bottom_diff;
13240 // adjust door positions according to specified alignment
13242 for (j = 0; j < 2; j++)
13244 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13246 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13247 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13248 else if (vp_door->align == ALIGN_CENTER)
13249 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13250 else if (vp_door->align == ALIGN_RIGHT)
13251 vp_door->x += vp_window->width - vp_door->width;
13253 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13254 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13255 else if (vp_door->valign == VALIGN_MIDDLE)
13256 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13257 else if (vp_door->valign == VALIGN_BOTTOM)
13258 vp_door->y += vp_window->height - vp_door->height;
13263 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13267 struct XYTileSize *dst, *src;
13270 editor_buttons_xy[] =
13273 &editor.button.element_left, &editor.palette.element_left,
13274 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13277 &editor.button.element_middle, &editor.palette.element_middle,
13278 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13281 &editor.button.element_right, &editor.palette.element_right,
13282 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13289 // set default position for element buttons to element graphics
13290 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13292 if ((*editor_buttons_xy[i].dst).x == -1 &&
13293 (*editor_buttons_xy[i].dst).y == -1)
13295 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13297 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13299 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13303 // adjust editor palette rows and columns if specified to be dynamic
13305 if (editor.palette.cols == -1)
13307 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13308 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13309 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13311 editor.palette.cols = (vp_width - sc_width) / bt_width;
13313 if (editor.palette.x == -1)
13315 int palette_width = editor.palette.cols * bt_width + sc_width;
13317 editor.palette.x = (vp_width - palette_width) / 2;
13321 if (editor.palette.rows == -1)
13323 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13324 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13325 int tx_height = getFontHeight(FONT_TEXT_2);
13327 editor.palette.rows = (vp_height - tx_height) / bt_height;
13329 if (editor.palette.y == -1)
13331 int palette_height = editor.palette.rows * bt_height + tx_height;
13333 editor.palette.y = (vp_height - palette_height) / 2;
13338 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13339 boolean initialize)
13341 // special case: check if network and preview player positions are redefined,
13342 // to compare this later against the main menu level preview being redefined
13343 struct TokenIntPtrInfo menu_config_players[] =
13345 { "main.network_players.x", &menu.main.network_players.redefined },
13346 { "main.network_players.y", &menu.main.network_players.redefined },
13347 { "main.preview_players.x", &menu.main.preview_players.redefined },
13348 { "main.preview_players.y", &menu.main.preview_players.redefined },
13349 { "preview.x", &preview.redefined },
13350 { "preview.y", &preview.redefined }
13356 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13357 *menu_config_players[i].value = FALSE;
13361 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13362 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13363 *menu_config_players[i].value = TRUE;
13367 static void InitMenuDesignSettings_PreviewPlayers(void)
13369 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13372 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13374 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13377 static void LoadMenuDesignSettingsFromFilename(char *filename)
13379 static struct TitleFadingInfo tfi;
13380 static struct TitleMessageInfo tmi;
13381 static struct TokenInfo title_tokens[] =
13383 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13384 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13385 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13386 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13387 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13391 static struct TokenInfo titlemessage_tokens[] =
13393 { TYPE_INTEGER, &tmi.x, ".x" },
13394 { TYPE_INTEGER, &tmi.y, ".y" },
13395 { TYPE_INTEGER, &tmi.width, ".width" },
13396 { TYPE_INTEGER, &tmi.height, ".height" },
13397 { TYPE_INTEGER, &tmi.chars, ".chars" },
13398 { TYPE_INTEGER, &tmi.lines, ".lines" },
13399 { TYPE_INTEGER, &tmi.align, ".align" },
13400 { TYPE_INTEGER, &tmi.valign, ".valign" },
13401 { TYPE_INTEGER, &tmi.font, ".font" },
13402 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13403 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13404 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13405 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13406 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13407 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13408 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13409 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13410 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13416 struct TitleFadingInfo *info;
13421 // initialize first titles from "enter screen" definitions, if defined
13422 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13423 { &title_first_default, "menu.enter_screen.TITLE" },
13425 // initialize title screens from "next screen" definitions, if defined
13426 { &title_initial_default, "menu.next_screen.TITLE" },
13427 { &title_default, "menu.next_screen.TITLE" },
13433 struct TitleMessageInfo *array;
13436 titlemessage_arrays[] =
13438 // initialize first titles from "enter screen" definitions, if defined
13439 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13440 { titlescreen_first, "menu.enter_screen.TITLE" },
13441 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13442 { titlemessage_first, "menu.enter_screen.TITLE" },
13444 // initialize titles from "next screen" definitions, if defined
13445 { titlescreen_initial, "menu.next_screen.TITLE" },
13446 { titlescreen, "menu.next_screen.TITLE" },
13447 { titlemessage_initial, "menu.next_screen.TITLE" },
13448 { titlemessage, "menu.next_screen.TITLE" },
13450 // overwrite titles with title definitions, if defined
13451 { titlescreen_initial_first, "[title_initial]" },
13452 { titlescreen_first, "[title]" },
13453 { titlemessage_initial_first, "[title_initial]" },
13454 { titlemessage_first, "[title]" },
13456 { titlescreen_initial, "[title_initial]" },
13457 { titlescreen, "[title]" },
13458 { titlemessage_initial, "[title_initial]" },
13459 { titlemessage, "[title]" },
13461 // overwrite titles with title screen/message definitions, if defined
13462 { titlescreen_initial_first, "[titlescreen_initial]" },
13463 { titlescreen_first, "[titlescreen]" },
13464 { titlemessage_initial_first, "[titlemessage_initial]" },
13465 { titlemessage_first, "[titlemessage]" },
13467 { titlescreen_initial, "[titlescreen_initial]" },
13468 { titlescreen, "[titlescreen]" },
13469 { titlemessage_initial, "[titlemessage_initial]" },
13470 { titlemessage, "[titlemessage]" },
13474 SetupFileHash *setup_file_hash;
13477 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13480 // the following initializes hierarchical values from dynamic configuration
13482 // special case: initialize with default values that may be overwritten
13483 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13484 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13486 struct TokenIntPtrInfo menu_config[] =
13488 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13489 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13490 { "menu.list_size", &menu.list_size[i] }
13493 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13495 char *token = menu_config[j].token;
13496 char *value = getHashEntry(setup_file_hash, token);
13499 *menu_config[j].value = get_integer_from_string(value);
13503 // special case: initialize with default values that may be overwritten
13504 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13505 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13507 struct TokenIntPtrInfo menu_config[] =
13509 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13510 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13511 { "menu.list_size.INFO", &menu.list_size_info[i] },
13512 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13513 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13516 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13518 char *token = menu_config[j].token;
13519 char *value = getHashEntry(setup_file_hash, token);
13522 *menu_config[j].value = get_integer_from_string(value);
13526 // special case: initialize with default values that may be overwritten
13527 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13528 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13530 struct TokenIntPtrInfo menu_config[] =
13532 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13533 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13536 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13538 char *token = menu_config[j].token;
13539 char *value = getHashEntry(setup_file_hash, token);
13542 *menu_config[j].value = get_integer_from_string(value);
13546 // special case: initialize with default values that may be overwritten
13547 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13548 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13550 struct TokenIntPtrInfo menu_config[] =
13552 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13553 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13554 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13555 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13556 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13557 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13558 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13559 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13560 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13561 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13564 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13566 char *token = menu_config[j].token;
13567 char *value = getHashEntry(setup_file_hash, token);
13570 *menu_config[j].value = get_integer_from_string(value);
13574 // special case: initialize with default values that may be overwritten
13575 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13576 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13578 struct TokenIntPtrInfo menu_config[] =
13580 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13581 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13582 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13583 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13584 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13585 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13586 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13587 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13588 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13591 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13593 char *token = menu_config[j].token;
13594 char *value = getHashEntry(setup_file_hash, token);
13597 *menu_config[j].value = get_token_parameter_value(token, value);
13601 // special case: initialize with default values that may be overwritten
13602 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13603 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13607 char *token_prefix;
13608 struct RectWithBorder *struct_ptr;
13612 { "viewport.window", &viewport.window[i] },
13613 { "viewport.playfield", &viewport.playfield[i] },
13614 { "viewport.door_1", &viewport.door_1[i] },
13615 { "viewport.door_2", &viewport.door_2[i] }
13618 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13620 struct TokenIntPtrInfo vp_config[] =
13622 { ".x", &vp_struct[j].struct_ptr->x },
13623 { ".y", &vp_struct[j].struct_ptr->y },
13624 { ".width", &vp_struct[j].struct_ptr->width },
13625 { ".height", &vp_struct[j].struct_ptr->height },
13626 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13627 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13628 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13629 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13630 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13631 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13632 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13633 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13634 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13635 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13636 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13637 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13638 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13639 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13640 { ".align", &vp_struct[j].struct_ptr->align },
13641 { ".valign", &vp_struct[j].struct_ptr->valign }
13644 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13646 char *token = getStringCat2(vp_struct[j].token_prefix,
13647 vp_config[k].token);
13648 char *value = getHashEntry(setup_file_hash, token);
13651 *vp_config[k].value = get_token_parameter_value(token, value);
13658 // special case: initialize with default values that may be overwritten
13659 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13660 for (i = 0; title_info[i].info != NULL; i++)
13662 struct TitleFadingInfo *info = title_info[i].info;
13663 char *base_token = title_info[i].text;
13665 for (j = 0; title_tokens[j].type != -1; j++)
13667 char *token = getStringCat2(base_token, title_tokens[j].text);
13668 char *value = getHashEntry(setup_file_hash, token);
13672 int parameter_value = get_token_parameter_value(token, value);
13676 *(int *)title_tokens[j].value = (int)parameter_value;
13685 // special case: initialize with default values that may be overwritten
13686 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13687 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13689 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13690 char *base_token = titlemessage_arrays[i].text;
13692 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13694 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13695 char *value = getHashEntry(setup_file_hash, token);
13699 int parameter_value = get_token_parameter_value(token, value);
13701 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13705 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13706 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13708 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13718 // read (and overwrite with) values that may be specified in config file
13719 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13721 // special case: check if network and preview player positions are redefined
13722 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13724 freeSetupFileHash(setup_file_hash);
13727 void LoadMenuDesignSettings(void)
13729 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13731 InitMenuDesignSettings_Static();
13732 InitMenuDesignSettings_SpecialPreProcessing();
13733 InitMenuDesignSettings_PreviewPlayers();
13735 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13737 // first look for special settings configured in level series config
13738 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13740 if (fileExists(filename_base))
13741 LoadMenuDesignSettingsFromFilename(filename_base);
13744 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13746 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13747 LoadMenuDesignSettingsFromFilename(filename_local);
13749 InitMenuDesignSettings_SpecialPostProcessing();
13752 void LoadMenuDesignSettings_AfterGraphics(void)
13754 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13757 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13758 boolean ignore_defaults)
13762 for (i = 0; sound_config_vars[i].token != NULL; i++)
13764 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13766 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13767 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13771 *sound_config_vars[i].value =
13772 get_token_parameter_value(sound_config_vars[i].token, value);
13776 void InitSoundSettings_Static(void)
13778 // always start with reliable default values from static default config
13779 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13782 static void LoadSoundSettingsFromFilename(char *filename)
13784 SetupFileHash *setup_file_hash;
13786 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13789 // read (and overwrite with) values that may be specified in config file
13790 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13792 freeSetupFileHash(setup_file_hash);
13795 void LoadSoundSettings(void)
13797 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13799 InitSoundSettings_Static();
13801 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13803 // first look for special settings configured in level series config
13804 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13806 if (fileExists(filename_base))
13807 LoadSoundSettingsFromFilename(filename_base);
13810 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13812 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13813 LoadSoundSettingsFromFilename(filename_local);
13816 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13818 char *filename = getEditorSetupFilename();
13819 SetupFileList *setup_file_list, *list;
13820 SetupFileHash *element_hash;
13821 int num_unknown_tokens = 0;
13824 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13827 element_hash = newSetupFileHash();
13829 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13830 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13832 // determined size may be larger than needed (due to unknown elements)
13834 for (list = setup_file_list; list != NULL; list = list->next)
13837 // add space for up to 3 more elements for padding that may be needed
13838 *num_elements += 3;
13840 // free memory for old list of elements, if needed
13841 checked_free(*elements);
13843 // allocate memory for new list of elements
13844 *elements = checked_malloc(*num_elements * sizeof(int));
13847 for (list = setup_file_list; list != NULL; list = list->next)
13849 char *value = getHashEntry(element_hash, list->token);
13851 if (value == NULL) // try to find obsolete token mapping
13853 char *mapped_token = get_mapped_token(list->token);
13855 if (mapped_token != NULL)
13857 value = getHashEntry(element_hash, mapped_token);
13859 free(mapped_token);
13865 (*elements)[(*num_elements)++] = atoi(value);
13869 if (num_unknown_tokens == 0)
13872 Warn("unknown token(s) found in config file:");
13873 Warn("- config file: '%s'", filename);
13875 num_unknown_tokens++;
13878 Warn("- token: '%s'", list->token);
13882 if (num_unknown_tokens > 0)
13885 while (*num_elements % 4) // pad with empty elements, if needed
13886 (*elements)[(*num_elements)++] = EL_EMPTY;
13888 freeSetupFileList(setup_file_list);
13889 freeSetupFileHash(element_hash);
13892 for (i = 0; i < *num_elements; i++)
13893 Debug("editor", "element '%s' [%d]\n",
13894 element_info[(*elements)[i]].token_name, (*elements)[i]);
13898 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13901 SetupFileHash *setup_file_hash = NULL;
13902 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13903 char *filename_music, *filename_prefix, *filename_info;
13909 token_to_value_ptr[] =
13911 { "title_header", &tmp_music_file_info.title_header },
13912 { "artist_header", &tmp_music_file_info.artist_header },
13913 { "album_header", &tmp_music_file_info.album_header },
13914 { "year_header", &tmp_music_file_info.year_header },
13915 { "played_header", &tmp_music_file_info.played_header },
13917 { "title", &tmp_music_file_info.title },
13918 { "artist", &tmp_music_file_info.artist },
13919 { "album", &tmp_music_file_info.album },
13920 { "year", &tmp_music_file_info.year },
13921 { "played", &tmp_music_file_info.played },
13927 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13928 getCustomMusicFilename(basename));
13930 if (filename_music == NULL)
13933 // ---------- try to replace file extension ----------
13935 filename_prefix = getStringCopy(filename_music);
13936 if (strrchr(filename_prefix, '.') != NULL)
13937 *strrchr(filename_prefix, '.') = '\0';
13938 filename_info = getStringCat2(filename_prefix, ".txt");
13940 if (fileExists(filename_info))
13941 setup_file_hash = loadSetupFileHash(filename_info);
13943 free(filename_prefix);
13944 free(filename_info);
13946 if (setup_file_hash == NULL)
13948 // ---------- try to add file extension ----------
13950 filename_prefix = getStringCopy(filename_music);
13951 filename_info = getStringCat2(filename_prefix, ".txt");
13953 if (fileExists(filename_info))
13954 setup_file_hash = loadSetupFileHash(filename_info);
13956 free(filename_prefix);
13957 free(filename_info);
13960 if (setup_file_hash == NULL)
13963 // ---------- music file info found ----------
13965 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13967 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13969 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13971 *token_to_value_ptr[i].value_ptr =
13972 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13975 tmp_music_file_info.basename = getStringCopy(basename);
13976 tmp_music_file_info.music = music;
13977 tmp_music_file_info.is_sound = is_sound;
13979 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13980 *new_music_file_info = tmp_music_file_info;
13982 return new_music_file_info;
13985 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13987 return get_music_file_info_ext(basename, music, FALSE);
13990 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13992 return get_music_file_info_ext(basename, sound, TRUE);
13995 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13996 char *basename, boolean is_sound)
13998 for (; list != NULL; list = list->next)
13999 if (list->is_sound == is_sound && strEqual(list->basename, basename))
14005 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14007 return music_info_listed_ext(list, basename, FALSE);
14010 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14012 return music_info_listed_ext(list, basename, TRUE);
14015 void LoadMusicInfo(void)
14017 int num_music_noconf = getMusicListSize_NoConf();
14018 int num_music = getMusicListSize();
14019 int num_sounds = getSoundListSize();
14020 struct FileInfo *music, *sound;
14021 struct MusicFileInfo *next, **new;
14025 while (music_file_info != NULL)
14027 next = music_file_info->next;
14029 checked_free(music_file_info->basename);
14031 checked_free(music_file_info->title_header);
14032 checked_free(music_file_info->artist_header);
14033 checked_free(music_file_info->album_header);
14034 checked_free(music_file_info->year_header);
14035 checked_free(music_file_info->played_header);
14037 checked_free(music_file_info->title);
14038 checked_free(music_file_info->artist);
14039 checked_free(music_file_info->album);
14040 checked_free(music_file_info->year);
14041 checked_free(music_file_info->played);
14043 free(music_file_info);
14045 music_file_info = next;
14048 new = &music_file_info;
14050 // get (configured or unconfigured) music file info for all levels
14051 for (i = leveldir_current->first_level;
14052 i <= leveldir_current->last_level; i++)
14056 if (levelset.music[i] != MUS_UNDEFINED)
14058 // get music file info for configured level music
14059 music_nr = levelset.music[i];
14061 else if (num_music_noconf > 0)
14063 // get music file info for unconfigured level music
14064 int level_pos = i - leveldir_current->first_level;
14066 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14073 char *basename = getMusicInfoEntryFilename(music_nr);
14075 if (basename == NULL)
14078 if (!music_info_listed(music_file_info, basename))
14080 *new = get_music_file_info(basename, music_nr);
14083 new = &(*new)->next;
14087 // get music file info for all remaining configured music files
14088 for (i = 0; i < num_music; i++)
14090 music = getMusicListEntry(i);
14092 if (music->filename == NULL)
14095 if (strEqual(music->filename, UNDEFINED_FILENAME))
14098 // a configured file may be not recognized as music
14099 if (!FileIsMusic(music->filename))
14102 if (!music_info_listed(music_file_info, music->filename))
14104 *new = get_music_file_info(music->filename, i);
14107 new = &(*new)->next;
14111 // get sound file info for all configured sound files
14112 for (i = 0; i < num_sounds; i++)
14114 sound = getSoundListEntry(i);
14116 if (sound->filename == NULL)
14119 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14122 // a configured file may be not recognized as sound
14123 if (!FileIsSound(sound->filename))
14126 if (!sound_info_listed(music_file_info, sound->filename))
14128 *new = get_sound_file_info(sound->filename, i);
14130 new = &(*new)->next;
14134 // add pointers to previous list nodes
14136 struct MusicFileInfo *node = music_file_info;
14138 while (node != NULL)
14141 node->next->prev = node;
14147 static void add_helpanim_entry(int element, int action, int direction,
14148 int delay, int *num_list_entries)
14150 struct HelpAnimInfo *new_list_entry;
14151 (*num_list_entries)++;
14154 checked_realloc(helpanim_info,
14155 *num_list_entries * sizeof(struct HelpAnimInfo));
14156 new_list_entry = &helpanim_info[*num_list_entries - 1];
14158 new_list_entry->element = element;
14159 new_list_entry->action = action;
14160 new_list_entry->direction = direction;
14161 new_list_entry->delay = delay;
14164 static void print_unknown_token(char *filename, char *token, int token_nr)
14169 Warn("unknown token(s) found in config file:");
14170 Warn("- config file: '%s'", filename);
14173 Warn("- token: '%s'", token);
14176 static void print_unknown_token_end(int token_nr)
14182 void LoadHelpAnimInfo(void)
14184 char *filename = getHelpAnimFilename();
14185 SetupFileList *setup_file_list = NULL, *list;
14186 SetupFileHash *element_hash, *action_hash, *direction_hash;
14187 int num_list_entries = 0;
14188 int num_unknown_tokens = 0;
14191 if (fileExists(filename))
14192 setup_file_list = loadSetupFileList(filename);
14194 if (setup_file_list == NULL)
14196 // use reliable default values from static configuration
14197 SetupFileList *insert_ptr;
14199 insert_ptr = setup_file_list =
14200 newSetupFileList(helpanim_config[0].token,
14201 helpanim_config[0].value);
14203 for (i = 1; helpanim_config[i].token; i++)
14204 insert_ptr = addListEntry(insert_ptr,
14205 helpanim_config[i].token,
14206 helpanim_config[i].value);
14209 element_hash = newSetupFileHash();
14210 action_hash = newSetupFileHash();
14211 direction_hash = newSetupFileHash();
14213 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14214 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14216 for (i = 0; i < NUM_ACTIONS; i++)
14217 setHashEntry(action_hash, element_action_info[i].suffix,
14218 i_to_a(element_action_info[i].value));
14220 // do not store direction index (bit) here, but direction value!
14221 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14222 setHashEntry(direction_hash, element_direction_info[i].suffix,
14223 i_to_a(1 << element_direction_info[i].value));
14225 for (list = setup_file_list; list != NULL; list = list->next)
14227 char *element_token, *action_token, *direction_token;
14228 char *element_value, *action_value, *direction_value;
14229 int delay = atoi(list->value);
14231 if (strEqual(list->token, "end"))
14233 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14238 /* first try to break element into element/action/direction parts;
14239 if this does not work, also accept combined "element[.act][.dir]"
14240 elements (like "dynamite.active"), which are unique elements */
14242 if (strchr(list->token, '.') == NULL) // token contains no '.'
14244 element_value = getHashEntry(element_hash, list->token);
14245 if (element_value != NULL) // element found
14246 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14247 &num_list_entries);
14250 // no further suffixes found -- this is not an element
14251 print_unknown_token(filename, list->token, num_unknown_tokens++);
14257 // token has format "<prefix>.<something>"
14259 action_token = strchr(list->token, '.'); // suffix may be action ...
14260 direction_token = action_token; // ... or direction
14262 element_token = getStringCopy(list->token);
14263 *strchr(element_token, '.') = '\0';
14265 element_value = getHashEntry(element_hash, element_token);
14267 if (element_value == NULL) // this is no element
14269 element_value = getHashEntry(element_hash, list->token);
14270 if (element_value != NULL) // combined element found
14271 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14272 &num_list_entries);
14274 print_unknown_token(filename, list->token, num_unknown_tokens++);
14276 free(element_token);
14281 action_value = getHashEntry(action_hash, action_token);
14283 if (action_value != NULL) // action found
14285 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14286 &num_list_entries);
14288 free(element_token);
14293 direction_value = getHashEntry(direction_hash, direction_token);
14295 if (direction_value != NULL) // direction found
14297 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14298 &num_list_entries);
14300 free(element_token);
14305 if (strchr(action_token + 1, '.') == NULL)
14307 // no further suffixes found -- this is not an action nor direction
14309 element_value = getHashEntry(element_hash, list->token);
14310 if (element_value != NULL) // combined element found
14311 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14312 &num_list_entries);
14314 print_unknown_token(filename, list->token, num_unknown_tokens++);
14316 free(element_token);
14321 // token has format "<prefix>.<suffix>.<something>"
14323 direction_token = strchr(action_token + 1, '.');
14325 action_token = getStringCopy(action_token);
14326 *strchr(action_token + 1, '.') = '\0';
14328 action_value = getHashEntry(action_hash, action_token);
14330 if (action_value == NULL) // this is no action
14332 element_value = getHashEntry(element_hash, list->token);
14333 if (element_value != NULL) // combined element found
14334 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14335 &num_list_entries);
14337 print_unknown_token(filename, list->token, num_unknown_tokens++);
14339 free(element_token);
14340 free(action_token);
14345 direction_value = getHashEntry(direction_hash, direction_token);
14347 if (direction_value != NULL) // direction found
14349 add_helpanim_entry(atoi(element_value), atoi(action_value),
14350 atoi(direction_value), delay, &num_list_entries);
14352 free(element_token);
14353 free(action_token);
14358 // this is no direction
14360 element_value = getHashEntry(element_hash, list->token);
14361 if (element_value != NULL) // combined element found
14362 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14363 &num_list_entries);
14365 print_unknown_token(filename, list->token, num_unknown_tokens++);
14367 free(element_token);
14368 free(action_token);
14371 print_unknown_token_end(num_unknown_tokens);
14373 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14374 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14376 freeSetupFileList(setup_file_list);
14377 freeSetupFileHash(element_hash);
14378 freeSetupFileHash(action_hash);
14379 freeSetupFileHash(direction_hash);
14382 for (i = 0; i < num_list_entries; i++)
14383 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14384 EL_NAME(helpanim_info[i].element),
14385 helpanim_info[i].element,
14386 helpanim_info[i].action,
14387 helpanim_info[i].direction,
14388 helpanim_info[i].delay);
14392 void LoadHelpTextInfo(void)
14394 char *filename = getHelpTextFilename();
14397 if (helptext_info != NULL)
14399 freeSetupFileHash(helptext_info);
14400 helptext_info = NULL;
14403 if (fileExists(filename))
14404 helptext_info = loadSetupFileHash(filename);
14406 if (helptext_info == NULL)
14408 // use reliable default values from static configuration
14409 helptext_info = newSetupFileHash();
14411 for (i = 0; helptext_config[i].token; i++)
14412 setHashEntry(helptext_info,
14413 helptext_config[i].token,
14414 helptext_config[i].value);
14418 BEGIN_HASH_ITERATION(helptext_info, itr)
14420 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14421 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14423 END_HASH_ITERATION(hash, itr)
14428 // ----------------------------------------------------------------------------
14430 // ----------------------------------------------------------------------------
14432 #define MAX_NUM_CONVERT_LEVELS 1000
14434 void ConvertLevels(void)
14436 static LevelDirTree *convert_leveldir = NULL;
14437 static int convert_level_nr = -1;
14438 static int num_levels_handled = 0;
14439 static int num_levels_converted = 0;
14440 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14443 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14444 global.convert_leveldir);
14446 if (convert_leveldir == NULL)
14447 Fail("no such level identifier: '%s'", global.convert_leveldir);
14449 leveldir_current = convert_leveldir;
14451 if (global.convert_level_nr != -1)
14453 convert_leveldir->first_level = global.convert_level_nr;
14454 convert_leveldir->last_level = global.convert_level_nr;
14457 convert_level_nr = convert_leveldir->first_level;
14459 PrintLine("=", 79);
14460 Print("Converting levels\n");
14461 PrintLine("-", 79);
14462 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14463 Print("Level series name: '%s'\n", convert_leveldir->name);
14464 Print("Level series author: '%s'\n", convert_leveldir->author);
14465 Print("Number of levels: %d\n", convert_leveldir->levels);
14466 PrintLine("=", 79);
14469 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14470 levels_failed[i] = FALSE;
14472 while (convert_level_nr <= convert_leveldir->last_level)
14474 char *level_filename;
14477 level_nr = convert_level_nr++;
14479 Print("Level %03d: ", level_nr);
14481 LoadLevel(level_nr);
14482 if (level.no_level_file || level.no_valid_file)
14484 Print("(no level)\n");
14488 Print("converting level ... ");
14491 // special case: conversion of some EMC levels as requested by ACME
14492 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14495 level_filename = getDefaultLevelFilename(level_nr);
14496 new_level = !fileExists(level_filename);
14500 SaveLevel(level_nr);
14502 num_levels_converted++;
14504 Print("converted.\n");
14508 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14509 levels_failed[level_nr] = TRUE;
14511 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14514 num_levels_handled++;
14518 PrintLine("=", 79);
14519 Print("Number of levels handled: %d\n", num_levels_handled);
14520 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14521 (num_levels_handled ?
14522 num_levels_converted * 100 / num_levels_handled : 0));
14523 PrintLine("-", 79);
14524 Print("Summary (for automatic parsing by scripts):\n");
14525 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14526 convert_leveldir->identifier, num_levels_converted,
14527 num_levels_handled,
14528 (num_levels_handled ?
14529 num_levels_converted * 100 / num_levels_handled : 0));
14531 if (num_levels_handled != num_levels_converted)
14533 Print(", FAILED:");
14534 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14535 if (levels_failed[i])
14540 PrintLine("=", 79);
14542 CloseAllAndExit(0);
14546 // ----------------------------------------------------------------------------
14547 // create and save images for use in level sketches (raw BMP format)
14548 // ----------------------------------------------------------------------------
14550 void CreateLevelSketchImages(void)
14556 InitElementPropertiesGfxElement();
14558 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14559 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14561 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14563 int element = getMappedElement(i);
14564 char basename1[16];
14565 char basename2[16];
14569 sprintf(basename1, "%04d.bmp", i);
14570 sprintf(basename2, "%04ds.bmp", i);
14572 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14573 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14575 DrawSizedElement(0, 0, element, TILESIZE);
14576 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14578 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14579 Fail("cannot save level sketch image file '%s'", filename1);
14581 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14582 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14584 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14585 Fail("cannot save level sketch image file '%s'", filename2);
14590 // create corresponding SQL statements (for normal and small images)
14593 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14594 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14597 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14598 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14600 // optional: create content for forum level sketch demonstration post
14602 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14605 FreeBitmap(bitmap1);
14606 FreeBitmap(bitmap2);
14609 fprintf(stderr, "\n");
14611 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14613 CloseAllAndExit(0);
14617 // ----------------------------------------------------------------------------
14618 // create and save images for element collecting animations (raw BMP format)
14619 // ----------------------------------------------------------------------------
14621 static boolean createCollectImage(int element)
14623 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14626 void CreateCollectElementImages(void)
14630 int anim_frames = num_steps - 1;
14631 int tile_size = TILESIZE;
14632 int anim_width = tile_size * anim_frames;
14633 int anim_height = tile_size;
14634 int num_collect_images = 0;
14635 int pos_collect_images = 0;
14637 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14638 if (createCollectImage(i))
14639 num_collect_images++;
14641 Info("Creating %d element collecting animation images ...",
14642 num_collect_images);
14644 int dst_width = anim_width * 2;
14645 int dst_height = anim_height * num_collect_images / 2;
14646 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14647 char *basename_bmp = "RocksCollect.bmp";
14648 char *basename_png = "RocksCollect.png";
14649 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14650 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14651 int len_filename_bmp = strlen(filename_bmp);
14652 int len_filename_png = strlen(filename_png);
14653 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14654 char cmd_convert[max_command_len];
14656 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14660 // force using RGBA surface for destination bitmap
14661 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14662 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14664 dst_bitmap->surface =
14665 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14667 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14669 if (!createCollectImage(i))
14672 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14673 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14674 int graphic = el2img(i);
14675 char *token_name = element_info[i].token_name;
14676 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14677 Bitmap *src_bitmap;
14680 Info("- creating collecting image for '%s' ...", token_name);
14682 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14684 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14685 tile_size, tile_size, 0, 0);
14687 // force using RGBA surface for temporary bitmap (using transparent black)
14688 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14689 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14691 tmp_bitmap->surface =
14692 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14694 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14696 for (j = 0; j < anim_frames; j++)
14698 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14699 int frame_size = frame_size_final * num_steps;
14700 int offset = (tile_size - frame_size_final) / 2;
14701 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14703 while (frame_size > frame_size_final)
14707 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14709 FreeBitmap(frame_bitmap);
14711 frame_bitmap = half_bitmap;
14714 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14715 frame_size_final, frame_size_final,
14716 dst_x + j * tile_size + offset, dst_y + offset);
14718 FreeBitmap(frame_bitmap);
14721 tmp_bitmap->surface_masked = NULL;
14723 FreeBitmap(tmp_bitmap);
14725 pos_collect_images++;
14728 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14729 Fail("cannot save element collecting image file '%s'", filename_bmp);
14731 FreeBitmap(dst_bitmap);
14733 Info("Converting image file from BMP to PNG ...");
14735 if (system(cmd_convert) != 0)
14736 Fail("converting image file failed");
14738 unlink(filename_bmp);
14742 CloseAllAndExit(0);
14746 // ----------------------------------------------------------------------------
14747 // create and save images for custom and group elements (raw BMP format)
14748 // ----------------------------------------------------------------------------
14750 void CreateCustomElementImages(char *directory)
14752 char *src_basename = "RocksCE-template.ilbm";
14753 char *dst_basename = "RocksCE.bmp";
14754 char *src_filename = getPath2(directory, src_basename);
14755 char *dst_filename = getPath2(directory, dst_basename);
14756 Bitmap *src_bitmap;
14758 int yoffset_ce = 0;
14759 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14762 InitVideoDefaults();
14764 ReCreateBitmap(&backbuffer, video.width, video.height);
14766 src_bitmap = LoadImage(src_filename);
14768 bitmap = CreateBitmap(TILEX * 16 * 2,
14769 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14772 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14779 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14780 TILEX * x, TILEY * y + yoffset_ce);
14782 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14784 TILEX * x + TILEX * 16,
14785 TILEY * y + yoffset_ce);
14787 for (j = 2; j >= 0; j--)
14791 BlitBitmap(src_bitmap, bitmap,
14792 TILEX + c * 7, 0, 6, 10,
14793 TILEX * x + 6 + j * 7,
14794 TILEY * y + 11 + yoffset_ce);
14796 BlitBitmap(src_bitmap, bitmap,
14797 TILEX + c * 8, TILEY, 6, 10,
14798 TILEX * 16 + TILEX * x + 6 + j * 8,
14799 TILEY * y + 10 + yoffset_ce);
14805 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14812 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14813 TILEX * x, TILEY * y + yoffset_ge);
14815 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14817 TILEX * x + TILEX * 16,
14818 TILEY * y + yoffset_ge);
14820 for (j = 1; j >= 0; j--)
14824 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14825 TILEX * x + 6 + j * 10,
14826 TILEY * y + 11 + yoffset_ge);
14828 BlitBitmap(src_bitmap, bitmap,
14829 TILEX + c * 8, TILEY + 12, 6, 10,
14830 TILEX * 16 + TILEX * x + 10 + j * 8,
14831 TILEY * y + 10 + yoffset_ge);
14837 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14838 Fail("cannot save CE graphics file '%s'", dst_filename);
14840 FreeBitmap(bitmap);
14842 CloseAllAndExit(0);