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
884 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
885 &li.bd_sand_looks_like, EL_BD_SAND
888 // (the following values are related to various game elements)
892 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
893 &li.score[SC_EMERALD], 10
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
899 &li.score[SC_DIAMOND], 10
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
905 &li.score[SC_BUG], 10
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
911 &li.score[SC_SPACESHIP], 10
916 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
917 &li.score[SC_PACMAN], 10
922 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
923 &li.score[SC_NUT], 10
928 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
929 &li.score[SC_DYNAMITE], 10
934 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
935 &li.score[SC_KEY], 10
940 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
941 &li.score[SC_PEARL], 10
946 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
947 &li.score[SC_CRYSTAL], 10
950 // (amoeba values used by R'n'D game engine only)
953 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
954 &li.amoeba_content, EL_DIAMOND
958 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
963 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
964 &li.grow_into_diggable, TRUE
966 // (amoeba values used by BD game engine only)
969 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
970 &li.bd_amoeba_wait_for_hatching, FALSE
974 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
975 &li.bd_amoeba_start_immediately, TRUE
979 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
980 &li.bd_amoeba_2_explode_by_amoeba, TRUE
984 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
985 &li.bd_amoeba_threshold_too_big, 200
989 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
990 &li.bd_amoeba_slow_growth_time, 200
994 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
995 &li.bd_amoeba_slow_growth_rate, 3
999 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1000 &li.bd_amoeba_fast_growth_rate, 25
1004 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1005 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1009 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1010 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1016 &li.bd_amoeba_2_threshold_too_big, 200
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1021 &li.bd_amoeba_2_slow_growth_time, 200
1025 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1026 &li.bd_amoeba_2_slow_growth_rate, 3
1030 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1031 &li.bd_amoeba_2_fast_growth_rate, 25
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1036 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1040 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1041 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1045 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1046 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1050 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1051 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1056 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1057 &li.yamyam_content, EL_ROCK, NULL,
1058 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 &li.score[SC_YAMYAM], 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1069 &li.score[SC_ROBOT], 10
1073 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1079 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1085 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1086 &li.time_magic_wall, 10
1090 EL_GAME_OF_LIFE, -1,
1091 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1092 &li.game_of_life[0], 2
1095 EL_GAME_OF_LIFE, -1,
1096 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1097 &li.game_of_life[1], 3
1100 EL_GAME_OF_LIFE, -1,
1101 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1102 &li.game_of_life[2], 3
1105 EL_GAME_OF_LIFE, -1,
1106 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1107 &li.game_of_life[3], 3
1110 EL_GAME_OF_LIFE, -1,
1111 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1112 &li.use_life_bugs, FALSE
1117 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1122 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1127 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1137 EL_TIMEGATE_SWITCH, -1,
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1139 &li.time_timegate, 10
1143 EL_LIGHT_SWITCH_ACTIVE, -1,
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1149 EL_SHIELD_NORMAL, -1,
1150 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1151 &li.shield_normal_time, 10
1154 EL_SHIELD_NORMAL, -1,
1155 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1156 &li.score[SC_SHIELD], 10
1160 EL_SHIELD_DEADLY, -1,
1161 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1162 &li.shield_deadly_time, 10
1165 EL_SHIELD_DEADLY, -1,
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1167 &li.score[SC_SHIELD], 10
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1178 &li.extra_time_score, 10
1182 EL_TIME_ORB_FULL, -1,
1183 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1184 &li.time_orb_time, 10
1187 EL_TIME_ORB_FULL, -1,
1188 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1189 &li.use_time_orb_bug, FALSE
1194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1195 &li.use_spring_bug, FALSE
1200 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1201 &li.android_move_time, 10
1205 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1206 &li.android_clone_time, 10
1209 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1210 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1211 &li.android_clone_element[0], EL_EMPTY, NULL,
1212 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1216 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1217 &li.android_clone_element[0], EL_EMPTY, NULL,
1218 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1223 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1224 &li.lenses_score, 10
1228 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1233 EL_EMC_MAGNIFIER, -1,
1234 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1235 &li.magnify_score, 10
1238 EL_EMC_MAGNIFIER, -1,
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1240 &li.magnify_time, 10
1244 EL_EMC_MAGIC_BALL, -1,
1245 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1249 EL_EMC_MAGIC_BALL, -1,
1250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1251 &li.ball_random, FALSE
1254 EL_EMC_MAGIC_BALL, -1,
1255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1256 &li.ball_active_initial, FALSE
1259 EL_EMC_MAGIC_BALL, -1,
1260 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1261 &li.ball_content, EL_EMPTY, NULL,
1262 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1266 EL_SOKOBAN_FIELD_EMPTY, -1,
1267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1268 &li.sb_fields_needed, TRUE
1272 EL_SOKOBAN_OBJECT, -1,
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1274 &li.sb_objects_needed, TRUE
1279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1280 &li.mm_laser_red, FALSE
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1285 &li.mm_laser_green, FALSE
1289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1290 &li.mm_laser_blue, TRUE
1295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1296 &li.df_laser_red, TRUE
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1301 &li.df_laser_green, TRUE
1305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1306 &li.df_laser_blue, FALSE
1310 EL_MM_FUSE_ACTIVE, -1,
1311 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1312 &li.mm_time_fuse, 25
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1317 &li.mm_time_bomb, 75
1321 EL_MM_GRAY_BALL, -1,
1322 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1323 &li.mm_time_ball, 75
1326 EL_MM_GRAY_BALL, -1,
1327 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1328 &li.mm_ball_choice_mode, ANIM_RANDOM
1331 EL_MM_GRAY_BALL, -1,
1332 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1333 &li.mm_ball_content, EL_EMPTY, NULL,
1334 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1337 EL_MM_GRAY_BALL, -1,
1338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1339 &li.rotate_mm_ball_content, TRUE
1342 EL_MM_GRAY_BALL, -1,
1343 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1344 &li.explode_mm_ball, FALSE
1348 EL_MM_STEEL_BLOCK, -1,
1349 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1350 &li.mm_time_block, 75
1353 EL_MM_LIGHTBALL, -1,
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1355 &li.score[SC_ELEM_BONUS], 10
1365 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1369 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1370 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1374 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1375 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1381 &xx_envelope.autowrap, FALSE
1385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1386 &xx_envelope.centered, FALSE
1391 TYPE_STRING, CONF_VALUE_BYTES(1),
1392 &xx_envelope.text, -1, NULL,
1393 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1394 &xx_default_string_empty[0]
1404 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1408 TYPE_STRING, CONF_VALUE_BYTES(1),
1409 &xx_ei.description[0], -1,
1410 &yy_ei.description[0],
1411 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1412 &xx_default_description[0]
1417 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1418 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1419 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1421 #if ENABLE_RESERVED_CODE
1422 // (reserved for later use)
1425 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1426 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1427 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1434 &xx_ei.use_gfx_element, FALSE,
1435 &yy_ei.use_gfx_element
1439 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1440 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1441 &yy_ei.gfx_element_initial
1446 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1447 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1448 &yy_ei.access_direction
1453 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1454 &xx_ei.collect_score_initial, 10,
1455 &yy_ei.collect_score_initial
1459 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1460 &xx_ei.collect_count_initial, 1,
1461 &yy_ei.collect_count_initial
1466 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1467 &xx_ei.ce_value_fixed_initial, 0,
1468 &yy_ei.ce_value_fixed_initial
1472 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1473 &xx_ei.ce_value_random_initial, 0,
1474 &yy_ei.ce_value_random_initial
1478 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1479 &xx_ei.use_last_ce_value, FALSE,
1480 &yy_ei.use_last_ce_value
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1486 &xx_ei.push_delay_fixed, 8,
1487 &yy_ei.push_delay_fixed
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1492 &xx_ei.push_delay_random, 8,
1493 &yy_ei.push_delay_random
1497 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1498 &xx_ei.drop_delay_fixed, 0,
1499 &yy_ei.drop_delay_fixed
1503 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1504 &xx_ei.drop_delay_random, 0,
1505 &yy_ei.drop_delay_random
1509 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1510 &xx_ei.move_delay_fixed, 0,
1511 &yy_ei.move_delay_fixed
1515 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1516 &xx_ei.move_delay_random, 0,
1517 &yy_ei.move_delay_random
1521 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1522 &xx_ei.step_delay_fixed, 0,
1523 &yy_ei.step_delay_fixed
1527 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1528 &xx_ei.step_delay_random, 0,
1529 &yy_ei.step_delay_random
1534 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1535 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1540 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1541 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1542 &yy_ei.move_direction_initial
1546 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1547 &xx_ei.move_stepsize, TILEX / 8,
1548 &yy_ei.move_stepsize
1553 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1554 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1555 &yy_ei.move_enter_element
1559 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1560 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1561 &yy_ei.move_leave_element
1565 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1566 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1567 &yy_ei.move_leave_type
1572 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1573 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1574 &yy_ei.slippery_type
1579 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1580 &xx_ei.explosion_type, EXPLODES_3X3,
1581 &yy_ei.explosion_type
1585 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1586 &xx_ei.explosion_delay, 16,
1587 &yy_ei.explosion_delay
1591 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1592 &xx_ei.ignition_delay, 8,
1593 &yy_ei.ignition_delay
1598 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1599 &xx_ei.content, EL_EMPTY_SPACE,
1601 &xx_num_contents, 1, 1
1604 // ---------- "num_change_pages" must be the last entry ---------------------
1607 -1, SAVE_CONF_ALWAYS,
1608 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1609 &xx_ei.num_change_pages, 1,
1610 &yy_ei.num_change_pages
1621 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1623 // ---------- "current_change_page" must be the first entry -----------------
1626 -1, SAVE_CONF_ALWAYS,
1627 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1628 &xx_current_change_page, -1
1631 // ---------- (the remaining entries can be in any order) -------------------
1635 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1636 &xx_change.can_change, FALSE
1641 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1642 &xx_event_bits[0], 0
1646 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1647 &xx_event_bits[1], 0
1652 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1653 &xx_change.trigger_player, CH_PLAYER_ANY
1657 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1658 &xx_change.trigger_side, CH_SIDE_ANY
1662 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1663 &xx_change.trigger_page, CH_PAGE_ANY
1668 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1669 &xx_change.target_element, EL_EMPTY_SPACE
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1675 &xx_change.delay_fixed, 0
1679 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1680 &xx_change.delay_random, 0
1684 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1685 &xx_change.delay_frames, FRAMES_PER_SECOND
1690 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1691 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1696 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1697 &xx_change.explode, FALSE
1701 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1702 &xx_change.use_target_content, FALSE
1706 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1707 &xx_change.only_if_complete, FALSE
1711 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1712 &xx_change.use_random_replace, FALSE
1716 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1717 &xx_change.random_percentage, 100
1721 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1722 &xx_change.replace_when, CP_WHEN_EMPTY
1727 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1728 &xx_change.has_action, FALSE
1732 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1733 &xx_change.action_type, CA_NO_ACTION
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1738 &xx_change.action_mode, CA_MODE_UNDEFINED
1742 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1743 &xx_change.action_arg, CA_ARG_UNDEFINED
1748 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1749 &xx_change.action_element, EL_EMPTY_SPACE
1754 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1755 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1756 &xx_num_contents, 1, 1
1766 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1770 TYPE_STRING, CONF_VALUE_BYTES(1),
1771 &xx_ei.description[0], -1, NULL,
1772 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1773 &xx_default_description[0]
1778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1779 &xx_ei.use_gfx_element, FALSE
1783 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1784 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1789 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1790 &xx_group.choice_mode, ANIM_RANDOM
1795 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1796 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1797 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1807 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1812 &xx_ei.use_gfx_element, FALSE
1816 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1817 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1827 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1831 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1832 &li.block_snap_field, TRUE
1836 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1837 &li.continuous_snapping, TRUE
1841 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1842 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1846 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1847 &li.use_start_element[0], FALSE
1851 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1852 &li.start_element[0], EL_PLAYER_1
1856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1857 &li.use_artwork_element[0], FALSE
1861 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1862 &li.artwork_element[0], EL_PLAYER_1
1866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1867 &li.use_explosion_element[0], FALSE
1871 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1872 &li.explosion_element[0], EL_PLAYER_1
1887 filetype_id_list[] =
1889 { LEVEL_FILE_TYPE_RND, "RND" },
1890 { LEVEL_FILE_TYPE_BD, "BD" },
1891 { LEVEL_FILE_TYPE_EM, "EM" },
1892 { LEVEL_FILE_TYPE_SP, "SP" },
1893 { LEVEL_FILE_TYPE_DX, "DX" },
1894 { LEVEL_FILE_TYPE_SB, "SB" },
1895 { LEVEL_FILE_TYPE_DC, "DC" },
1896 { LEVEL_FILE_TYPE_MM, "MM" },
1897 { LEVEL_FILE_TYPE_MM, "DF" },
1902 // ============================================================================
1903 // level file functions
1904 // ============================================================================
1906 static boolean check_special_flags(char *flag)
1908 if (strEqual(options.special_flags, flag) ||
1909 strEqual(leveldir_current->special_flags, flag))
1915 static struct DateInfo getCurrentDate(void)
1917 time_t epoch_seconds = time(NULL);
1918 struct tm *now = localtime(&epoch_seconds);
1919 struct DateInfo date;
1921 date.year = now->tm_year + 1900;
1922 date.month = now->tm_mon + 1;
1923 date.day = now->tm_mday;
1925 date.src = DATE_SRC_CLOCK;
1930 static void resetEventFlags(struct ElementChangeInfo *change)
1934 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1935 change->has_event[i] = FALSE;
1938 static void resetEventBits(void)
1942 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1943 xx_event_bits[i] = 0;
1946 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1950 /* important: only change event flag if corresponding event bit is set
1951 (this is because all xx_event_bits[] values are loaded separately,
1952 and all xx_event_bits[] values are set back to zero before loading
1953 another value xx_event_bits[x] (each value representing 32 flags)) */
1955 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1956 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1957 change->has_event[i] = TRUE;
1960 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1964 /* in contrast to the above function setEventFlagsFromEventBits(), it
1965 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1966 depending on the corresponding change->has_event[i] values here, as
1967 all xx_event_bits[] values are reset in resetEventBits() before */
1969 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1970 if (change->has_event[i])
1971 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1974 static char *getDefaultElementDescription(struct ElementInfo *ei)
1976 static char description[MAX_ELEMENT_NAME_LEN + 1];
1977 char *default_description = (ei->custom_description != NULL ?
1978 ei->custom_description :
1979 ei->editor_description);
1982 // always start with reliable default values
1983 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1984 description[i] = '\0';
1986 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1987 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1989 return &description[0];
1992 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1994 char *default_description = getDefaultElementDescription(ei);
1997 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1998 ei->description[i] = default_description[i];
2001 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2005 for (i = 0; conf[i].data_type != -1; i++)
2007 int default_value = conf[i].default_value;
2008 int data_type = conf[i].data_type;
2009 int conf_type = conf[i].conf_type;
2010 int byte_mask = conf_type & CONF_MASK_BYTES;
2012 if (byte_mask == CONF_MASK_MULTI_BYTES)
2014 int default_num_entities = conf[i].default_num_entities;
2015 int max_num_entities = conf[i].max_num_entities;
2017 *(int *)(conf[i].num_entities) = default_num_entities;
2019 if (data_type == TYPE_STRING)
2021 char *default_string = conf[i].default_string;
2022 char *string = (char *)(conf[i].value);
2024 strncpy(string, default_string, max_num_entities);
2026 else if (data_type == TYPE_ELEMENT_LIST)
2028 int *element_array = (int *)(conf[i].value);
2031 for (j = 0; j < max_num_entities; j++)
2032 element_array[j] = default_value;
2034 else if (data_type == TYPE_CONTENT_LIST)
2036 struct Content *content = (struct Content *)(conf[i].value);
2039 for (c = 0; c < max_num_entities; c++)
2040 for (y = 0; y < 3; y++)
2041 for (x = 0; x < 3; x++)
2042 content[c].e[x][y] = default_value;
2045 else // constant size configuration data (1, 2 or 4 bytes)
2047 if (data_type == TYPE_BOOLEAN)
2048 *(boolean *)(conf[i].value) = default_value;
2050 *(int *) (conf[i].value) = default_value;
2055 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2059 for (i = 0; conf[i].data_type != -1; i++)
2061 int data_type = conf[i].data_type;
2062 int conf_type = conf[i].conf_type;
2063 int byte_mask = conf_type & CONF_MASK_BYTES;
2065 if (byte_mask == CONF_MASK_MULTI_BYTES)
2067 int max_num_entities = conf[i].max_num_entities;
2069 if (data_type == TYPE_STRING)
2071 char *string = (char *)(conf[i].value);
2072 char *string_copy = (char *)(conf[i].value_copy);
2074 strncpy(string_copy, string, max_num_entities);
2076 else if (data_type == TYPE_ELEMENT_LIST)
2078 int *element_array = (int *)(conf[i].value);
2079 int *element_array_copy = (int *)(conf[i].value_copy);
2082 for (j = 0; j < max_num_entities; j++)
2083 element_array_copy[j] = element_array[j];
2085 else if (data_type == TYPE_CONTENT_LIST)
2087 struct Content *content = (struct Content *)(conf[i].value);
2088 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2091 for (c = 0; c < max_num_entities; c++)
2092 for (y = 0; y < 3; y++)
2093 for (x = 0; x < 3; x++)
2094 content_copy[c].e[x][y] = content[c].e[x][y];
2097 else // constant size configuration data (1, 2 or 4 bytes)
2099 if (data_type == TYPE_BOOLEAN)
2100 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2102 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2107 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2111 xx_ei = *ei_from; // copy element data into temporary buffer
2112 yy_ei = *ei_to; // copy element data into temporary buffer
2114 copyConfigFromConfigList(chunk_config_CUSX_base);
2119 // ---------- reinitialize and copy change pages ----------
2121 ei_to->num_change_pages = ei_from->num_change_pages;
2122 ei_to->current_change_page = ei_from->current_change_page;
2124 setElementChangePages(ei_to, ei_to->num_change_pages);
2126 for (i = 0; i < ei_to->num_change_pages; i++)
2127 ei_to->change_page[i] = ei_from->change_page[i];
2129 // ---------- copy group element info ----------
2130 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2131 *ei_to->group = *ei_from->group;
2133 // mark this custom element as modified
2134 ei_to->modified_settings = TRUE;
2137 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2139 int change_page_size = sizeof(struct ElementChangeInfo);
2141 ei->num_change_pages = MAX(1, change_pages);
2144 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2146 if (ei->current_change_page >= ei->num_change_pages)
2147 ei->current_change_page = ei->num_change_pages - 1;
2149 ei->change = &ei->change_page[ei->current_change_page];
2152 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2154 xx_change = *change; // copy change data into temporary buffer
2156 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2158 *change = xx_change;
2160 resetEventFlags(change);
2162 change->direct_action = 0;
2163 change->other_action = 0;
2165 change->pre_change_function = NULL;
2166 change->change_function = NULL;
2167 change->post_change_function = NULL;
2170 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2174 li = *level; // copy level data into temporary buffer
2175 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2176 *level = li; // copy temporary buffer back to level data
2178 setLevelInfoToDefaults_BD();
2179 setLevelInfoToDefaults_EM();
2180 setLevelInfoToDefaults_SP();
2181 setLevelInfoToDefaults_MM();
2183 level->native_bd_level = &native_bd_level;
2184 level->native_em_level = &native_em_level;
2185 level->native_sp_level = &native_sp_level;
2186 level->native_mm_level = &native_mm_level;
2188 level->file_version = FILE_VERSION_ACTUAL;
2189 level->game_version = GAME_VERSION_ACTUAL;
2191 level->creation_date = getCurrentDate();
2193 level->encoding_16bit_field = TRUE;
2194 level->encoding_16bit_yamyam = TRUE;
2195 level->encoding_16bit_amoeba = TRUE;
2197 // clear level name and level author string buffers
2198 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2199 level->name[i] = '\0';
2200 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2201 level->author[i] = '\0';
2203 // set level name and level author to default values
2204 strcpy(level->name, NAMELESS_LEVEL_NAME);
2205 strcpy(level->author, ANONYMOUS_NAME);
2207 // set level playfield to playable default level with player and exit
2208 for (x = 0; x < MAX_LEV_FIELDX; x++)
2209 for (y = 0; y < MAX_LEV_FIELDY; y++)
2210 level->field[x][y] = EL_SAND;
2212 level->field[0][0] = EL_PLAYER_1;
2213 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2215 BorderElement = EL_STEELWALL;
2217 // detect custom elements when loading them
2218 level->file_has_custom_elements = FALSE;
2220 // set all bug compatibility flags to "false" => do not emulate this bug
2221 level->use_action_after_change_bug = FALSE;
2223 if (leveldir_current)
2225 // try to determine better author name than 'anonymous'
2226 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2228 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2229 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2233 switch (LEVELCLASS(leveldir_current))
2235 case LEVELCLASS_TUTORIAL:
2236 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2239 case LEVELCLASS_CONTRIB:
2240 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2241 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2244 case LEVELCLASS_PRIVATE:
2245 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2246 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2250 // keep default value
2257 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2259 static boolean clipboard_elements_initialized = FALSE;
2262 InitElementPropertiesStatic();
2264 li = *level; // copy level data into temporary buffer
2265 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2266 *level = li; // copy temporary buffer back to level data
2268 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2271 struct ElementInfo *ei = &element_info[element];
2273 if (element == EL_MM_GRAY_BALL)
2275 struct LevelInfo_MM *level_mm = level->native_mm_level;
2278 for (j = 0; j < level->num_mm_ball_contents; j++)
2279 level->mm_ball_content[j] =
2280 map_element_MM_to_RND(level_mm->ball_content[j]);
2283 // never initialize clipboard elements after the very first time
2284 // (to be able to use clipboard elements between several levels)
2285 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2288 if (IS_ENVELOPE(element))
2290 int envelope_nr = element - EL_ENVELOPE_1;
2292 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2294 level->envelope[envelope_nr] = xx_envelope;
2297 if (IS_CUSTOM_ELEMENT(element) ||
2298 IS_GROUP_ELEMENT(element) ||
2299 IS_INTERNAL_ELEMENT(element))
2301 xx_ei = *ei; // copy element data into temporary buffer
2303 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2308 setElementChangePages(ei, 1);
2309 setElementChangeInfoToDefaults(ei->change);
2311 if (IS_CUSTOM_ELEMENT(element) ||
2312 IS_GROUP_ELEMENT(element))
2314 setElementDescriptionToDefault(ei);
2316 ei->modified_settings = FALSE;
2319 if (IS_CUSTOM_ELEMENT(element) ||
2320 IS_INTERNAL_ELEMENT(element))
2322 // internal values used in level editor
2324 ei->access_type = 0;
2325 ei->access_layer = 0;
2326 ei->access_protected = 0;
2327 ei->walk_to_action = 0;
2328 ei->smash_targets = 0;
2331 ei->can_explode_by_fire = FALSE;
2332 ei->can_explode_smashed = FALSE;
2333 ei->can_explode_impact = FALSE;
2335 ei->current_change_page = 0;
2338 if (IS_GROUP_ELEMENT(element) ||
2339 IS_INTERNAL_ELEMENT(element))
2341 struct ElementGroupInfo *group;
2343 // initialize memory for list of elements in group
2344 if (ei->group == NULL)
2345 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2349 xx_group = *group; // copy group data into temporary buffer
2351 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2356 if (IS_EMPTY_ELEMENT(element) ||
2357 IS_INTERNAL_ELEMENT(element))
2359 xx_ei = *ei; // copy element data into temporary buffer
2361 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2367 clipboard_elements_initialized = TRUE;
2370 static void setLevelInfoToDefaults(struct LevelInfo *level,
2371 boolean level_info_only,
2372 boolean reset_file_status)
2374 setLevelInfoToDefaults_Level(level);
2376 if (!level_info_only)
2377 setLevelInfoToDefaults_Elements(level);
2379 if (reset_file_status)
2381 level->no_valid_file = FALSE;
2382 level->no_level_file = FALSE;
2385 level->changed = FALSE;
2388 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2390 level_file_info->nr = 0;
2391 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2392 level_file_info->packed = FALSE;
2394 setString(&level_file_info->basename, NULL);
2395 setString(&level_file_info->filename, NULL);
2398 int getMappedElement_SB(int, boolean);
2400 static void ActivateLevelTemplate(void)
2404 if (check_special_flags("load_xsb_to_ces"))
2406 // fill smaller playfields with padding "beyond border wall" elements
2407 if (level.fieldx < level_template.fieldx ||
2408 level.fieldy < level_template.fieldy)
2410 short field[level.fieldx][level.fieldy];
2411 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2412 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2413 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2414 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2416 // copy old playfield (which is smaller than the visible area)
2417 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2418 field[x][y] = level.field[x][y];
2420 // fill new, larger playfield with "beyond border wall" elements
2421 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2422 level.field[x][y] = getMappedElement_SB('_', TRUE);
2424 // copy the old playfield to the middle of the new playfield
2425 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2426 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2428 level.fieldx = new_fieldx;
2429 level.fieldy = new_fieldy;
2433 // Currently there is no special action needed to activate the template
2434 // data, because 'element_info' property settings overwrite the original
2435 // level data, while all other variables do not change.
2437 // Exception: 'from_level_template' elements in the original level playfield
2438 // are overwritten with the corresponding elements at the same position in
2439 // playfield from the level template.
2441 for (x = 0; x < level.fieldx; x++)
2442 for (y = 0; y < level.fieldy; y++)
2443 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2444 level.field[x][y] = level_template.field[x][y];
2446 if (check_special_flags("load_xsb_to_ces"))
2448 struct LevelInfo level_backup = level;
2450 // overwrite all individual level settings from template level settings
2451 level = level_template;
2453 // restore level file info
2454 level.file_info = level_backup.file_info;
2456 // restore playfield size
2457 level.fieldx = level_backup.fieldx;
2458 level.fieldy = level_backup.fieldy;
2460 // restore playfield content
2461 for (x = 0; x < level.fieldx; x++)
2462 for (y = 0; y < level.fieldy; y++)
2463 level.field[x][y] = level_backup.field[x][y];
2465 // restore name and author from individual level
2466 strcpy(level.name, level_backup.name);
2467 strcpy(level.author, level_backup.author);
2469 // restore flag "use_custom_template"
2470 level.use_custom_template = level_backup.use_custom_template;
2474 static boolean checkForPackageFromBasename_BD(char *basename)
2476 // check for native BD level file extensions
2477 if (!strSuffixLower(basename, ".bd") &&
2478 !strSuffixLower(basename, ".bdr") &&
2479 !strSuffixLower(basename, ".brc") &&
2480 !strSuffixLower(basename, ".gds"))
2483 // check for standard single-level BD files (like "001.bd")
2484 if (strSuffixLower(basename, ".bd") &&
2485 strlen(basename) == 6 &&
2486 basename[0] >= '0' && basename[0] <= '9' &&
2487 basename[1] >= '0' && basename[1] <= '9' &&
2488 basename[2] >= '0' && basename[2] <= '9')
2491 // this is a level package in native BD file format
2495 static char *getLevelFilenameFromBasename(char *basename)
2497 static char *filename = NULL;
2499 checked_free(filename);
2501 filename = getPath2(getCurrentLevelDir(), basename);
2506 static int getFileTypeFromBasename(char *basename)
2508 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2510 static char *filename = NULL;
2511 struct stat file_status;
2513 // ---------- try to determine file type from filename ----------
2515 // check for typical filename of a Supaplex level package file
2516 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2517 return LEVEL_FILE_TYPE_SP;
2519 // check for typical filename of a Diamond Caves II level package file
2520 if (strSuffixLower(basename, ".dc") ||
2521 strSuffixLower(basename, ".dc2"))
2522 return LEVEL_FILE_TYPE_DC;
2524 // check for typical filename of a Sokoban level package file
2525 if (strSuffixLower(basename, ".xsb") &&
2526 strchr(basename, '%') == NULL)
2527 return LEVEL_FILE_TYPE_SB;
2529 // check for typical filename of a Boulder Dash (GDash) level package file
2530 if (checkForPackageFromBasename_BD(basename))
2531 return LEVEL_FILE_TYPE_BD;
2533 // ---------- try to determine file type from filesize ----------
2535 checked_free(filename);
2536 filename = getPath2(getCurrentLevelDir(), basename);
2538 if (stat(filename, &file_status) == 0)
2540 // check for typical filesize of a Supaplex level package file
2541 if (file_status.st_size == 170496)
2542 return LEVEL_FILE_TYPE_SP;
2545 return LEVEL_FILE_TYPE_UNKNOWN;
2548 static int getFileTypeFromMagicBytes(char *filename, int type)
2552 if ((file = openFile(filename, MODE_READ)))
2554 char chunk_name[CHUNK_ID_LEN + 1];
2556 getFileChunkBE(file, chunk_name, NULL);
2558 if (strEqual(chunk_name, "MMII") ||
2559 strEqual(chunk_name, "MIRR"))
2560 type = LEVEL_FILE_TYPE_MM;
2568 static boolean checkForPackageFromBasename(char *basename)
2570 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2571 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2573 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2576 static char *getSingleLevelBasenameExt(int nr, char *extension)
2578 static char basename[MAX_FILENAME_LEN];
2581 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2583 sprintf(basename, "%03d.%s", nr, extension);
2588 static char *getSingleLevelBasename(int nr)
2590 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2593 static char *getPackedLevelBasename(int type)
2595 static char basename[MAX_FILENAME_LEN];
2596 char *directory = getCurrentLevelDir();
2598 DirectoryEntry *dir_entry;
2600 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2602 if ((dir = openDirectory(directory)) == NULL)
2604 Warn("cannot read current level directory '%s'", directory);
2609 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2611 char *entry_basename = dir_entry->basename;
2612 int entry_type = getFileTypeFromBasename(entry_basename);
2614 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2616 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2619 strcpy(basename, entry_basename);
2626 closeDirectory(dir);
2631 static char *getSingleLevelFilename(int nr)
2633 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2636 #if ENABLE_UNUSED_CODE
2637 static char *getPackedLevelFilename(int type)
2639 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2643 char *getDefaultLevelFilename(int nr)
2645 return getSingleLevelFilename(nr);
2648 #if ENABLE_UNUSED_CODE
2649 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2653 lfi->packed = FALSE;
2655 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2656 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2660 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2661 int type, char *format, ...)
2663 static char basename[MAX_FILENAME_LEN];
2666 va_start(ap, format);
2667 vsprintf(basename, format, ap);
2671 lfi->packed = FALSE;
2673 setString(&lfi->basename, basename);
2674 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2677 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2683 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2684 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2687 static int getFiletypeFromID(char *filetype_id)
2689 char *filetype_id_lower;
2690 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2693 if (filetype_id == NULL)
2694 return LEVEL_FILE_TYPE_UNKNOWN;
2696 filetype_id_lower = getStringToLower(filetype_id);
2698 for (i = 0; filetype_id_list[i].id != NULL; i++)
2700 char *id_lower = getStringToLower(filetype_id_list[i].id);
2702 if (strEqual(filetype_id_lower, id_lower))
2703 filetype = filetype_id_list[i].filetype;
2707 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2711 free(filetype_id_lower);
2716 char *getLocalLevelTemplateFilename(void)
2718 return getDefaultLevelFilename(-1);
2721 char *getGlobalLevelTemplateFilename(void)
2723 // global variable "leveldir_current" must be modified in the loop below
2724 LevelDirTree *leveldir_current_last = leveldir_current;
2725 char *filename = NULL;
2727 // check for template level in path from current to topmost tree node
2729 while (leveldir_current != NULL)
2731 filename = getDefaultLevelFilename(-1);
2733 if (fileExists(filename))
2736 leveldir_current = leveldir_current->node_parent;
2739 // restore global variable "leveldir_current" modified in above loop
2740 leveldir_current = leveldir_current_last;
2745 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2749 // special case: level number is negative => check for level template file
2752 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2753 getSingleLevelBasename(-1));
2755 // replace local level template filename with global template filename
2756 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2758 // no fallback if template file not existing
2762 // special case: check for file name/pattern specified in "levelinfo.conf"
2763 if (leveldir_current->level_filename != NULL)
2765 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2767 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2768 leveldir_current->level_filename, nr);
2770 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2772 if (fileExists(lfi->filename))
2775 else if (leveldir_current->level_filetype != NULL)
2777 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2779 // check for specified native level file with standard file name
2780 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2781 "%03d.%s", nr, LEVELFILE_EXTENSION);
2782 if (fileExists(lfi->filename))
2786 // check for native Rocks'n'Diamonds level file
2787 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2788 "%03d.%s", nr, LEVELFILE_EXTENSION);
2789 if (fileExists(lfi->filename))
2792 // check for native Boulder Dash level file
2793 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2794 if (fileExists(lfi->filename))
2797 // check for Emerald Mine level file (V1)
2798 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2799 'a' + (nr / 10) % 26, '0' + nr % 10);
2800 if (fileExists(lfi->filename))
2802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2803 'A' + (nr / 10) % 26, '0' + nr % 10);
2804 if (fileExists(lfi->filename))
2807 // check for Emerald Mine level file (V2 to V5)
2808 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2809 if (fileExists(lfi->filename))
2812 // check for Emerald Mine level file (V6 / single mode)
2813 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2814 if (fileExists(lfi->filename))
2816 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2817 if (fileExists(lfi->filename))
2820 // check for Emerald Mine level file (V6 / teamwork mode)
2821 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2822 if (fileExists(lfi->filename))
2824 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2825 if (fileExists(lfi->filename))
2828 // check for various packed level file formats
2829 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2830 if (fileExists(lfi->filename))
2833 // no known level file found -- use default values (and fail later)
2834 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2835 "%03d.%s", nr, LEVELFILE_EXTENSION);
2838 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2840 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2841 lfi->type = getFileTypeFromBasename(lfi->basename);
2843 if (lfi->type == LEVEL_FILE_TYPE_RND)
2844 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2847 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2849 // always start with reliable default values
2850 setFileInfoToDefaults(level_file_info);
2852 level_file_info->nr = nr; // set requested level number
2854 determineLevelFileInfo_Filename(level_file_info);
2855 determineLevelFileInfo_Filetype(level_file_info);
2858 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2859 struct LevelFileInfo *lfi_to)
2861 lfi_to->nr = lfi_from->nr;
2862 lfi_to->type = lfi_from->type;
2863 lfi_to->packed = lfi_from->packed;
2865 setString(&lfi_to->basename, lfi_from->basename);
2866 setString(&lfi_to->filename, lfi_from->filename);
2869 // ----------------------------------------------------------------------------
2870 // functions for loading R'n'D level
2871 // ----------------------------------------------------------------------------
2873 int getMappedElement(int element)
2875 // remap some (historic, now obsolete) elements
2879 case EL_PLAYER_OBSOLETE:
2880 element = EL_PLAYER_1;
2883 case EL_KEY_OBSOLETE:
2887 case EL_EM_KEY_1_FILE_OBSOLETE:
2888 element = EL_EM_KEY_1;
2891 case EL_EM_KEY_2_FILE_OBSOLETE:
2892 element = EL_EM_KEY_2;
2895 case EL_EM_KEY_3_FILE_OBSOLETE:
2896 element = EL_EM_KEY_3;
2899 case EL_EM_KEY_4_FILE_OBSOLETE:
2900 element = EL_EM_KEY_4;
2903 case EL_ENVELOPE_OBSOLETE:
2904 element = EL_ENVELOPE_1;
2912 if (element >= NUM_FILE_ELEMENTS)
2914 Warn("invalid level element %d", element);
2916 element = EL_UNKNOWN;
2924 static int getMappedElementByVersion(int element, int game_version)
2926 // remap some elements due to certain game version
2928 if (game_version <= VERSION_IDENT(2,2,0,0))
2930 // map game font elements
2931 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2932 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2933 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2934 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2937 if (game_version < VERSION_IDENT(3,0,0,0))
2939 // map Supaplex gravity tube elements
2940 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2941 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2942 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2943 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2950 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2952 level->file_version = getFileVersion(file);
2953 level->game_version = getFileVersion(file);
2958 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2960 level->creation_date.year = getFile16BitBE(file);
2961 level->creation_date.month = getFile8Bit(file);
2962 level->creation_date.day = getFile8Bit(file);
2964 level->creation_date.src = DATE_SRC_LEVELFILE;
2969 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2971 int initial_player_stepsize;
2972 int initial_player_gravity;
2975 level->fieldx = getFile8Bit(file);
2976 level->fieldy = getFile8Bit(file);
2978 level->time = getFile16BitBE(file);
2979 level->gems_needed = getFile16BitBE(file);
2981 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2982 level->name[i] = getFile8Bit(file);
2983 level->name[MAX_LEVEL_NAME_LEN] = 0;
2985 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2986 level->score[i] = getFile8Bit(file);
2988 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2989 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2990 for (y = 0; y < 3; y++)
2991 for (x = 0; x < 3; x++)
2992 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2994 level->amoeba_speed = getFile8Bit(file);
2995 level->time_magic_wall = getFile8Bit(file);
2996 level->time_wheel = getFile8Bit(file);
2997 level->amoeba_content = getMappedElement(getFile8Bit(file));
2999 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3002 for (i = 0; i < MAX_PLAYERS; i++)
3003 level->initial_player_stepsize[i] = initial_player_stepsize;
3005 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3007 for (i = 0; i < MAX_PLAYERS; i++)
3008 level->initial_player_gravity[i] = initial_player_gravity;
3010 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3011 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3013 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3015 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3016 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3017 level->can_move_into_acid_bits = getFile32BitBE(file);
3018 level->dont_collide_with_bits = getFile8Bit(file);
3020 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3021 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3023 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3024 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3025 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3027 level->game_engine_type = getFile8Bit(file);
3029 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3034 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3038 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3039 level->name[i] = getFile8Bit(file);
3040 level->name[MAX_LEVEL_NAME_LEN] = 0;
3045 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3049 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3050 level->author[i] = getFile8Bit(file);
3051 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3056 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3059 int chunk_size_expected = level->fieldx * level->fieldy;
3061 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3062 stored with 16-bit encoding (and should be twice as big then).
3063 Even worse, playfield data was stored 16-bit when only yamyam content
3064 contained 16-bit elements and vice versa. */
3066 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3067 chunk_size_expected *= 2;
3069 if (chunk_size_expected != chunk_size)
3071 ReadUnusedBytesFromFile(file, chunk_size);
3072 return chunk_size_expected;
3075 for (y = 0; y < level->fieldy; y++)
3076 for (x = 0; x < level->fieldx; x++)
3077 level->field[x][y] =
3078 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3083 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3086 int header_size = 4;
3087 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3088 int chunk_size_expected = header_size + content_size;
3090 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3091 stored with 16-bit encoding (and should be twice as big then).
3092 Even worse, playfield data was stored 16-bit when only yamyam content
3093 contained 16-bit elements and vice versa. */
3095 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3096 chunk_size_expected += content_size;
3098 if (chunk_size_expected != chunk_size)
3100 ReadUnusedBytesFromFile(file, chunk_size);
3101 return chunk_size_expected;
3105 level->num_yamyam_contents = getFile8Bit(file);
3109 // correct invalid number of content fields -- should never happen
3110 if (level->num_yamyam_contents < 1 ||
3111 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3112 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3114 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3115 for (y = 0; y < 3; y++)
3116 for (x = 0; x < 3; x++)
3117 level->yamyam_content[i].e[x][y] =
3118 getMappedElement(level->encoding_16bit_field ?
3119 getFile16BitBE(file) : getFile8Bit(file));
3123 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3128 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3130 element = getMappedElement(getFile16BitBE(file));
3131 num_contents = getFile8Bit(file);
3133 getFile8Bit(file); // content x size (unused)
3134 getFile8Bit(file); // content y size (unused)
3136 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3138 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3139 for (y = 0; y < 3; y++)
3140 for (x = 0; x < 3; x++)
3141 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3143 // correct invalid number of content fields -- should never happen
3144 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3145 num_contents = STD_ELEMENT_CONTENTS;
3147 if (element == EL_YAMYAM)
3149 level->num_yamyam_contents = num_contents;
3151 for (i = 0; i < num_contents; i++)
3152 for (y = 0; y < 3; y++)
3153 for (x = 0; x < 3; x++)
3154 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3156 else if (element == EL_BD_AMOEBA)
3158 level->amoeba_content = content_array[0][0][0];
3162 Warn("cannot load content for element '%d'", element);
3168 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3174 int chunk_size_expected;
3176 element = getMappedElement(getFile16BitBE(file));
3177 if (!IS_ENVELOPE(element))
3178 element = EL_ENVELOPE_1;
3180 envelope_nr = element - EL_ENVELOPE_1;
3182 envelope_len = getFile16BitBE(file);
3184 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3185 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3187 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3189 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3190 if (chunk_size_expected != chunk_size)
3192 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3193 return chunk_size_expected;
3196 for (i = 0; i < envelope_len; i++)
3197 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3202 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3204 int num_changed_custom_elements = getFile16BitBE(file);
3205 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3208 if (chunk_size_expected != chunk_size)
3210 ReadUnusedBytesFromFile(file, chunk_size - 2);
3211 return chunk_size_expected;
3214 for (i = 0; i < num_changed_custom_elements; i++)
3216 int element = getMappedElement(getFile16BitBE(file));
3217 int properties = getFile32BitBE(file);
3219 if (IS_CUSTOM_ELEMENT(element))
3220 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3222 Warn("invalid custom element number %d", element);
3224 // older game versions that wrote level files with CUS1 chunks used
3225 // different default push delay values (not yet stored in level file)
3226 element_info[element].push_delay_fixed = 2;
3227 element_info[element].push_delay_random = 8;
3230 level->file_has_custom_elements = TRUE;
3235 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3237 int num_changed_custom_elements = getFile16BitBE(file);
3238 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3241 if (chunk_size_expected != chunk_size)
3243 ReadUnusedBytesFromFile(file, chunk_size - 2);
3244 return chunk_size_expected;
3247 for (i = 0; i < num_changed_custom_elements; i++)
3249 int element = getMappedElement(getFile16BitBE(file));
3250 int custom_target_element = getMappedElement(getFile16BitBE(file));
3252 if (IS_CUSTOM_ELEMENT(element))
3253 element_info[element].change->target_element = custom_target_element;
3255 Warn("invalid custom element number %d", element);
3258 level->file_has_custom_elements = TRUE;
3263 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3265 int num_changed_custom_elements = getFile16BitBE(file);
3266 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3269 if (chunk_size_expected != chunk_size)
3271 ReadUnusedBytesFromFile(file, chunk_size - 2);
3272 return chunk_size_expected;
3275 for (i = 0; i < num_changed_custom_elements; i++)
3277 int element = getMappedElement(getFile16BitBE(file));
3278 struct ElementInfo *ei = &element_info[element];
3279 unsigned int event_bits;
3281 if (!IS_CUSTOM_ELEMENT(element))
3283 Warn("invalid custom element number %d", element);
3285 element = EL_INTERNAL_DUMMY;
3288 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3289 ei->description[j] = getFile8Bit(file);
3290 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3292 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3294 // some free bytes for future properties and padding
3295 ReadUnusedBytesFromFile(file, 7);
3297 ei->use_gfx_element = getFile8Bit(file);
3298 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3300 ei->collect_score_initial = getFile8Bit(file);
3301 ei->collect_count_initial = getFile8Bit(file);
3303 ei->push_delay_fixed = getFile16BitBE(file);
3304 ei->push_delay_random = getFile16BitBE(file);
3305 ei->move_delay_fixed = getFile16BitBE(file);
3306 ei->move_delay_random = getFile16BitBE(file);
3308 ei->move_pattern = getFile16BitBE(file);
3309 ei->move_direction_initial = getFile8Bit(file);
3310 ei->move_stepsize = getFile8Bit(file);
3312 for (y = 0; y < 3; y++)
3313 for (x = 0; x < 3; x++)
3314 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3316 // bits 0 - 31 of "has_event[]"
3317 event_bits = getFile32BitBE(file);
3318 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3319 if (event_bits & (1u << j))
3320 ei->change->has_event[j] = TRUE;
3322 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3324 ei->change->delay_fixed = getFile16BitBE(file);
3325 ei->change->delay_random = getFile16BitBE(file);
3326 ei->change->delay_frames = getFile16BitBE(file);
3328 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3330 ei->change->explode = getFile8Bit(file);
3331 ei->change->use_target_content = getFile8Bit(file);
3332 ei->change->only_if_complete = getFile8Bit(file);
3333 ei->change->use_random_replace = getFile8Bit(file);
3335 ei->change->random_percentage = getFile8Bit(file);
3336 ei->change->replace_when = getFile8Bit(file);
3338 for (y = 0; y < 3; y++)
3339 for (x = 0; x < 3; x++)
3340 ei->change->target_content.e[x][y] =
3341 getMappedElement(getFile16BitBE(file));
3343 ei->slippery_type = getFile8Bit(file);
3345 // some free bytes for future properties and padding
3346 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3348 // mark that this custom element has been modified
3349 ei->modified_settings = TRUE;
3352 level->file_has_custom_elements = TRUE;
3357 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3359 struct ElementInfo *ei;
3360 int chunk_size_expected;
3364 // ---------- custom element base property values (96 bytes) ----------------
3366 element = getMappedElement(getFile16BitBE(file));
3368 if (!IS_CUSTOM_ELEMENT(element))
3370 Warn("invalid custom element number %d", element);
3372 ReadUnusedBytesFromFile(file, chunk_size - 2);
3377 ei = &element_info[element];
3379 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3380 ei->description[i] = getFile8Bit(file);
3381 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3383 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3385 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3387 ei->num_change_pages = getFile8Bit(file);
3389 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3390 if (chunk_size_expected != chunk_size)
3392 ReadUnusedBytesFromFile(file, chunk_size - 43);
3393 return chunk_size_expected;
3396 ei->ce_value_fixed_initial = getFile16BitBE(file);
3397 ei->ce_value_random_initial = getFile16BitBE(file);
3398 ei->use_last_ce_value = getFile8Bit(file);
3400 ei->use_gfx_element = getFile8Bit(file);
3401 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3403 ei->collect_score_initial = getFile8Bit(file);
3404 ei->collect_count_initial = getFile8Bit(file);
3406 ei->drop_delay_fixed = getFile8Bit(file);
3407 ei->push_delay_fixed = getFile8Bit(file);
3408 ei->drop_delay_random = getFile8Bit(file);
3409 ei->push_delay_random = getFile8Bit(file);
3410 ei->move_delay_fixed = getFile16BitBE(file);
3411 ei->move_delay_random = getFile16BitBE(file);
3413 // bits 0 - 15 of "move_pattern" ...
3414 ei->move_pattern = getFile16BitBE(file);
3415 ei->move_direction_initial = getFile8Bit(file);
3416 ei->move_stepsize = getFile8Bit(file);
3418 ei->slippery_type = getFile8Bit(file);
3420 for (y = 0; y < 3; y++)
3421 for (x = 0; x < 3; x++)
3422 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3424 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3425 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3426 ei->move_leave_type = getFile8Bit(file);
3428 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3429 ei->move_pattern |= (getFile16BitBE(file) << 16);
3431 ei->access_direction = getFile8Bit(file);
3433 ei->explosion_delay = getFile8Bit(file);
3434 ei->ignition_delay = getFile8Bit(file);
3435 ei->explosion_type = getFile8Bit(file);
3437 // some free bytes for future custom property values and padding
3438 ReadUnusedBytesFromFile(file, 1);
3440 // ---------- change page property values (48 bytes) ------------------------
3442 setElementChangePages(ei, ei->num_change_pages);
3444 for (i = 0; i < ei->num_change_pages; i++)
3446 struct ElementChangeInfo *change = &ei->change_page[i];
3447 unsigned int event_bits;
3449 // always start with reliable default values
3450 setElementChangeInfoToDefaults(change);
3452 // bits 0 - 31 of "has_event[]" ...
3453 event_bits = getFile32BitBE(file);
3454 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3455 if (event_bits & (1u << j))
3456 change->has_event[j] = TRUE;
3458 change->target_element = getMappedElement(getFile16BitBE(file));
3460 change->delay_fixed = getFile16BitBE(file);
3461 change->delay_random = getFile16BitBE(file);
3462 change->delay_frames = getFile16BitBE(file);
3464 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3466 change->explode = getFile8Bit(file);
3467 change->use_target_content = getFile8Bit(file);
3468 change->only_if_complete = getFile8Bit(file);
3469 change->use_random_replace = getFile8Bit(file);
3471 change->random_percentage = getFile8Bit(file);
3472 change->replace_when = getFile8Bit(file);
3474 for (y = 0; y < 3; y++)
3475 for (x = 0; x < 3; x++)
3476 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3478 change->can_change = getFile8Bit(file);
3480 change->trigger_side = getFile8Bit(file);
3482 change->trigger_player = getFile8Bit(file);
3483 change->trigger_page = getFile8Bit(file);
3485 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3486 CH_PAGE_ANY : (1 << change->trigger_page));
3488 change->has_action = getFile8Bit(file);
3489 change->action_type = getFile8Bit(file);
3490 change->action_mode = getFile8Bit(file);
3491 change->action_arg = getFile16BitBE(file);
3493 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3494 event_bits = getFile8Bit(file);
3495 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3496 if (event_bits & (1u << (j - 32)))
3497 change->has_event[j] = TRUE;
3500 // mark this custom element as modified
3501 ei->modified_settings = TRUE;
3503 level->file_has_custom_elements = TRUE;
3508 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3510 struct ElementInfo *ei;
3511 struct ElementGroupInfo *group;
3515 element = getMappedElement(getFile16BitBE(file));
3517 if (!IS_GROUP_ELEMENT(element))
3519 Warn("invalid group element number %d", element);
3521 ReadUnusedBytesFromFile(file, chunk_size - 2);
3526 ei = &element_info[element];
3528 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3529 ei->description[i] = getFile8Bit(file);
3530 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3532 group = element_info[element].group;
3534 group->num_elements = getFile8Bit(file);
3536 ei->use_gfx_element = getFile8Bit(file);
3537 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3539 group->choice_mode = getFile8Bit(file);
3541 // some free bytes for future values and padding
3542 ReadUnusedBytesFromFile(file, 3);
3544 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3545 group->element[i] = getMappedElement(getFile16BitBE(file));
3547 // mark this group element as modified
3548 element_info[element].modified_settings = TRUE;
3550 level->file_has_custom_elements = TRUE;
3555 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3556 int element, int real_element)
3558 int micro_chunk_size = 0;
3559 int conf_type = getFile8Bit(file);
3560 int byte_mask = conf_type & CONF_MASK_BYTES;
3561 boolean element_found = FALSE;
3564 micro_chunk_size += 1;
3566 if (byte_mask == CONF_MASK_MULTI_BYTES)
3568 int num_bytes = getFile16BitBE(file);
3569 byte *buffer = checked_malloc(num_bytes);
3571 ReadBytesFromFile(file, buffer, num_bytes);
3573 for (i = 0; conf[i].data_type != -1; i++)
3575 if (conf[i].element == element &&
3576 conf[i].conf_type == conf_type)
3578 int data_type = conf[i].data_type;
3579 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3580 int max_num_entities = conf[i].max_num_entities;
3582 if (num_entities > max_num_entities)
3584 Warn("truncating number of entities for element %d from %d to %d",
3585 element, num_entities, max_num_entities);
3587 num_entities = max_num_entities;
3590 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3591 data_type == TYPE_CONTENT_LIST))
3593 // for element and content lists, zero entities are not allowed
3594 Warn("found empty list of entities for element %d", element);
3596 // do not set "num_entities" here to prevent reading behind buffer
3598 *(int *)(conf[i].num_entities) = 1; // at least one is required
3602 *(int *)(conf[i].num_entities) = num_entities;
3605 element_found = TRUE;
3607 if (data_type == TYPE_STRING)
3609 char *string = (char *)(conf[i].value);
3612 for (j = 0; j < max_num_entities; j++)
3613 string[j] = (j < num_entities ? buffer[j] : '\0');
3615 else if (data_type == TYPE_ELEMENT_LIST)
3617 int *element_array = (int *)(conf[i].value);
3620 for (j = 0; j < num_entities; j++)
3622 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3624 else if (data_type == TYPE_CONTENT_LIST)
3626 struct Content *content= (struct Content *)(conf[i].value);
3629 for (c = 0; c < num_entities; c++)
3630 for (y = 0; y < 3; y++)
3631 for (x = 0; x < 3; x++)
3632 content[c].e[x][y] =
3633 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3636 element_found = FALSE;
3642 checked_free(buffer);
3644 micro_chunk_size += 2 + num_bytes;
3646 else // constant size configuration data (1, 2 or 4 bytes)
3648 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3649 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3650 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3652 for (i = 0; conf[i].data_type != -1; i++)
3654 if (conf[i].element == element &&
3655 conf[i].conf_type == conf_type)
3657 int data_type = conf[i].data_type;
3659 if (data_type == TYPE_ELEMENT)
3660 value = getMappedElement(value);
3662 if (data_type == TYPE_BOOLEAN)
3663 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3665 *(int *) (conf[i].value) = value;
3667 element_found = TRUE;
3673 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3678 char *error_conf_chunk_bytes =
3679 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3680 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3681 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3682 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3683 int error_element = real_element;
3685 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3686 error_conf_chunk_bytes, error_conf_chunk_token,
3687 error_element, EL_NAME(error_element));
3690 return micro_chunk_size;
3693 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3695 int real_chunk_size = 0;
3697 li = *level; // copy level data into temporary buffer
3699 while (!checkEndOfFile(file))
3701 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3703 if (real_chunk_size >= chunk_size)
3707 *level = li; // copy temporary buffer back to level data
3709 return real_chunk_size;
3712 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3714 int real_chunk_size = 0;
3716 li = *level; // copy level data into temporary buffer
3718 while (!checkEndOfFile(file))
3720 int element = getMappedElement(getFile16BitBE(file));
3722 real_chunk_size += 2;
3723 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3725 if (real_chunk_size >= chunk_size)
3729 *level = li; // copy temporary buffer back to level data
3731 return real_chunk_size;
3734 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3736 int real_chunk_size = 0;
3738 li = *level; // copy level data into temporary buffer
3740 while (!checkEndOfFile(file))
3742 int element = getMappedElement(getFile16BitBE(file));
3744 real_chunk_size += 2;
3745 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3747 if (real_chunk_size >= chunk_size)
3751 *level = li; // copy temporary buffer back to level data
3753 return real_chunk_size;
3756 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3758 int element = getMappedElement(getFile16BitBE(file));
3759 int envelope_nr = element - EL_ENVELOPE_1;
3760 int real_chunk_size = 2;
3762 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3764 while (!checkEndOfFile(file))
3766 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3769 if (real_chunk_size >= chunk_size)
3773 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3775 return real_chunk_size;
3778 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3780 int element = getMappedElement(getFile16BitBE(file));
3781 int real_chunk_size = 2;
3782 struct ElementInfo *ei = &element_info[element];
3785 xx_ei = *ei; // copy element data into temporary buffer
3787 xx_ei.num_change_pages = -1;
3789 while (!checkEndOfFile(file))
3791 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3793 if (xx_ei.num_change_pages != -1)
3796 if (real_chunk_size >= chunk_size)
3802 if (ei->num_change_pages == -1)
3804 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3807 ei->num_change_pages = 1;
3809 setElementChangePages(ei, 1);
3810 setElementChangeInfoToDefaults(ei->change);
3812 return real_chunk_size;
3815 // initialize number of change pages stored for this custom element
3816 setElementChangePages(ei, ei->num_change_pages);
3817 for (i = 0; i < ei->num_change_pages; i++)
3818 setElementChangeInfoToDefaults(&ei->change_page[i]);
3820 // start with reading properties for the first change page
3821 xx_current_change_page = 0;
3823 while (!checkEndOfFile(file))
3825 // level file might contain invalid change page number
3826 if (xx_current_change_page >= ei->num_change_pages)
3829 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3831 xx_change = *change; // copy change data into temporary buffer
3833 resetEventBits(); // reset bits; change page might have changed
3835 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3838 *change = xx_change;
3840 setEventFlagsFromEventBits(change);
3842 if (real_chunk_size >= chunk_size)
3846 level->file_has_custom_elements = TRUE;
3848 return real_chunk_size;
3851 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3853 int element = getMappedElement(getFile16BitBE(file));
3854 int real_chunk_size = 2;
3855 struct ElementInfo *ei = &element_info[element];
3856 struct ElementGroupInfo *group = ei->group;
3861 xx_ei = *ei; // copy element data into temporary buffer
3862 xx_group = *group; // copy group data into temporary buffer
3864 while (!checkEndOfFile(file))
3866 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3869 if (real_chunk_size >= chunk_size)
3876 level->file_has_custom_elements = TRUE;
3878 return real_chunk_size;
3881 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3883 int element = getMappedElement(getFile16BitBE(file));
3884 int real_chunk_size = 2;
3885 struct ElementInfo *ei = &element_info[element];
3887 xx_ei = *ei; // copy element data into temporary buffer
3889 while (!checkEndOfFile(file))
3891 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3894 if (real_chunk_size >= chunk_size)
3900 level->file_has_custom_elements = TRUE;
3902 return real_chunk_size;
3905 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3906 struct LevelFileInfo *level_file_info,
3907 boolean level_info_only)
3909 char *filename = level_file_info->filename;
3910 char cookie[MAX_LINE_LEN];
3911 char chunk_name[CHUNK_ID_LEN + 1];
3915 if (!(file = openFile(filename, MODE_READ)))
3917 level->no_valid_file = TRUE;
3918 level->no_level_file = TRUE;
3920 if (level_info_only)
3923 Warn("cannot read level '%s' -- using empty level", filename);
3925 if (!setup.editor.use_template_for_new_levels)
3928 // if level file not found, try to initialize level data from template
3929 filename = getGlobalLevelTemplateFilename();
3931 if (!(file = openFile(filename, MODE_READ)))
3934 // default: for empty levels, use level template for custom elements
3935 level->use_custom_template = TRUE;
3937 level->no_valid_file = FALSE;
3940 getFileChunkBE(file, chunk_name, NULL);
3941 if (strEqual(chunk_name, "RND1"))
3943 getFile32BitBE(file); // not used
3945 getFileChunkBE(file, chunk_name, NULL);
3946 if (!strEqual(chunk_name, "CAVE"))
3948 level->no_valid_file = TRUE;
3950 Warn("unknown format of level file '%s'", filename);
3957 else // check for pre-2.0 file format with cookie string
3959 strcpy(cookie, chunk_name);
3960 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3962 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3963 cookie[strlen(cookie) - 1] = '\0';
3965 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3967 level->no_valid_file = TRUE;
3969 Warn("unknown format of level file '%s'", filename);
3976 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3978 level->no_valid_file = TRUE;
3980 Warn("unsupported version of level file '%s'", filename);
3987 // pre-2.0 level files have no game version, so use file version here
3988 level->game_version = level->file_version;
3991 if (level->file_version < FILE_VERSION_1_2)
3993 // level files from versions before 1.2.0 without chunk structure
3994 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3995 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4003 int (*loader)(File *, int, struct LevelInfo *);
4007 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4008 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4009 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4010 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4011 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4012 { "INFO", -1, LoadLevel_INFO },
4013 { "BODY", -1, LoadLevel_BODY },
4014 { "CONT", -1, LoadLevel_CONT },
4015 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4016 { "CNT3", -1, LoadLevel_CNT3 },
4017 { "CUS1", -1, LoadLevel_CUS1 },
4018 { "CUS2", -1, LoadLevel_CUS2 },
4019 { "CUS3", -1, LoadLevel_CUS3 },
4020 { "CUS4", -1, LoadLevel_CUS4 },
4021 { "GRP1", -1, LoadLevel_GRP1 },
4022 { "CONF", -1, LoadLevel_CONF },
4023 { "ELEM", -1, LoadLevel_ELEM },
4024 { "NOTE", -1, LoadLevel_NOTE },
4025 { "CUSX", -1, LoadLevel_CUSX },
4026 { "GRPX", -1, LoadLevel_GRPX },
4027 { "EMPX", -1, LoadLevel_EMPX },
4032 while (getFileChunkBE(file, chunk_name, &chunk_size))
4036 while (chunk_info[i].name != NULL &&
4037 !strEqual(chunk_name, chunk_info[i].name))
4040 if (chunk_info[i].name == NULL)
4042 Warn("unknown chunk '%s' in level file '%s'",
4043 chunk_name, filename);
4045 ReadUnusedBytesFromFile(file, chunk_size);
4047 else if (chunk_info[i].size != -1 &&
4048 chunk_info[i].size != chunk_size)
4050 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4051 chunk_size, chunk_name, filename);
4053 ReadUnusedBytesFromFile(file, chunk_size);
4057 // call function to load this level chunk
4058 int chunk_size_expected =
4059 (chunk_info[i].loader)(file, chunk_size, level);
4061 if (chunk_size_expected < 0)
4063 Warn("error reading chunk '%s' in level file '%s'",
4064 chunk_name, filename);
4069 // the size of some chunks cannot be checked before reading other
4070 // chunks first (like "HEAD" and "BODY") that contain some header
4071 // information, so check them here
4072 if (chunk_size_expected != chunk_size)
4074 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4075 chunk_size, chunk_name, filename);
4087 // ----------------------------------------------------------------------------
4088 // functions for loading BD level
4089 // ----------------------------------------------------------------------------
4091 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4093 struct LevelInfo_BD *level_bd = level->native_bd_level;
4094 GdCave *cave = NULL; // will be changed below
4095 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4096 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4099 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4101 // cave and map newly allocated when set to defaults above
4102 cave = level_bd->cave;
4105 cave->intermission = level->bd_intermission;
4108 cave->level_time[0] = level->time;
4109 cave->level_diamonds[0] = level->gems_needed;
4112 cave->scheduling = level->bd_scheduling_type;
4113 cave->pal_timing = level->bd_pal_timing;
4114 cave->level_speed[0] = level->bd_cycle_delay_ms;
4115 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4116 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4117 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4120 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4121 cave->diamond_value = level->score[SC_EMERALD];
4122 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4124 // compatibility settings
4125 cave->lineshift = level->bd_line_shifting_borders;
4126 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4127 cave->short_explosions = level->bd_short_explosions;
4128 cave->gravity_affects_all = level->bd_gravity_affects_all;
4130 // player properties
4131 cave->diagonal_movements = level->bd_diagonal_movements;
4132 cave->active_is_first_found = level->bd_topmost_player_active;
4133 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4134 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4135 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4136 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4138 // element properties
4139 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4140 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4141 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4142 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4143 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4144 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4145 cave->level_magic_wall_time[0] = level->time_magic_wall;
4146 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4147 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4149 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4150 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4151 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4152 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4153 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4154 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4155 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4157 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4158 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4159 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4160 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4161 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4162 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4163 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4164 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4165 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4166 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4167 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4169 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4170 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4171 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4172 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4173 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4174 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4176 cave->slime_predictable = level->bd_slime_is_predictable;
4177 cave->slime_correct_random = level->bd_slime_correct_random;
4178 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4179 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4180 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4181 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4182 cave->slime_eats_1 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4183 cave->slime_converts_1 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4184 cave->slime_eats_2 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4185 cave->slime_converts_2 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4186 cave->slime_eats_3 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4187 cave->slime_converts_3 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4189 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4190 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4191 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4193 cave->biter_delay_frame = level->bd_biter_move_delay;
4194 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4196 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4198 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4200 cave->replicators_active = level->bd_replicators_active;
4201 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4203 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4204 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4206 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4208 cave->nut_turns_to_when_crushed = map_element_RND_to_BD_cave(level->bd_nut_content);
4210 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4211 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4212 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4214 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4215 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4217 cave->expanding_wall_looks_like = map_element_RND_to_BD_cave(level->bd_expanding_wall_looks_like);
4218 cave->dirt_looks_like = map_element_RND_to_BD_cave(level->bd_sand_looks_like);
4221 strncpy(cave->name, level->name, sizeof(GdString));
4222 cave->name[sizeof(GdString) - 1] = '\0';
4224 // playfield elements
4225 for (x = 0; x < cave->w; x++)
4226 for (y = 0; y < cave->h; y++)
4227 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4230 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4232 struct LevelInfo_BD *level_bd = level->native_bd_level;
4233 GdCave *cave = level_bd->cave;
4234 int bd_level_nr = level_bd->level_nr;
4237 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4238 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4241 level->bd_intermission = cave->intermission;
4244 level->time = cave->level_time[bd_level_nr];
4245 level->gems_needed = cave->level_diamonds[bd_level_nr];
4248 level->bd_scheduling_type = cave->scheduling;
4249 level->bd_pal_timing = cave->pal_timing;
4250 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4251 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4252 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4253 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4256 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4257 level->score[SC_EMERALD] = cave->diamond_value;
4258 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4260 // compatibility settings
4261 level->bd_line_shifting_borders = cave->lineshift;
4262 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4263 level->bd_short_explosions = cave->short_explosions;
4264 level->bd_gravity_affects_all = cave->gravity_affects_all;
4266 // player properties
4267 level->bd_diagonal_movements = cave->diagonal_movements;
4268 level->bd_topmost_player_active = cave->active_is_first_found;
4269 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4270 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4271 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4272 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4274 // element properties
4275 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4276 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4277 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4278 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4279 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4280 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4281 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4282 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4283 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4285 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4286 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4287 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4288 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4289 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4290 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4291 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4293 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4294 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4295 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4296 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4297 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4298 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4299 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4300 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4301 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4302 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4303 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4305 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4306 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4307 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4308 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4309 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4310 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4312 level->bd_slime_is_predictable = cave->slime_predictable;
4313 level->bd_slime_correct_random = cave->slime_correct_random;
4314 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4315 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4316 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4317 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4318 level->bd_slime_eats_element_1 = map_element_BD_to_RND_cave(cave->slime_eats_1);
4319 level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4320 level->bd_slime_eats_element_2 = map_element_BD_to_RND_cave(cave->slime_eats_2);
4321 level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4322 level->bd_slime_eats_element_3 = map_element_BD_to_RND_cave(cave->slime_eats_3);
4323 level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4325 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4326 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4327 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4329 level->bd_biter_move_delay = cave->biter_delay_frame;
4330 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4332 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4334 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4336 level->bd_replicators_active = cave->replicators_active;
4337 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4339 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4340 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4342 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4344 level->bd_nut_content = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4346 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4347 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4348 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4350 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4351 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4353 level->bd_expanding_wall_looks_like = map_element_BD_to_RND_cave(cave->expanding_wall_looks_like);
4354 level->bd_sand_looks_like = map_element_BD_to_RND_cave(cave->dirt_looks_like);
4357 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4359 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4360 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4362 // playfield elements
4363 for (x = 0; x < level->fieldx; x++)
4364 for (y = 0; y < level->fieldy; y++)
4365 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4367 checked_free(cave_name);
4370 static void setTapeInfoToDefaults(void);
4372 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4374 struct LevelInfo_BD *level_bd = level->native_bd_level;
4375 GdCave *cave = level_bd->cave;
4376 GdReplay *replay = level_bd->replay;
4382 // always start with reliable default values
4383 setTapeInfoToDefaults();
4385 tape.level_nr = level_nr; // (currently not used)
4386 tape.random_seed = replay->seed;
4388 TapeSetDateFromIsoDateString(replay->date);
4391 tape.pos[tape.counter].delay = 0;
4393 tape.bd_replay = TRUE;
4395 // all time calculations only used to display approximate tape time
4396 int cave_speed = cave->speed;
4397 int milliseconds_game = 0;
4398 int milliseconds_elapsed = 20;
4400 for (i = 0; i < replay->movements->len; i++)
4402 int replay_action = replay->movements->data[i];
4403 int tape_action = map_action_BD_to_RND(replay_action);
4404 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4405 boolean success = 0;
4409 success = TapeAddAction(action);
4411 milliseconds_game += milliseconds_elapsed;
4413 if (milliseconds_game >= cave_speed)
4415 milliseconds_game -= cave_speed;
4422 tape.pos[tape.counter].delay = 0;
4423 tape.pos[tape.counter].action[0] = 0;
4427 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4433 TapeHaltRecording();
4437 // ----------------------------------------------------------------------------
4438 // functions for loading EM level
4439 // ----------------------------------------------------------------------------
4441 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4443 static int ball_xy[8][2] =
4454 struct LevelInfo_EM *level_em = level->native_em_level;
4455 struct CAVE *cav = level_em->cav;
4458 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4459 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4461 cav->time_seconds = level->time;
4462 cav->gems_needed = level->gems_needed;
4464 cav->emerald_score = level->score[SC_EMERALD];
4465 cav->diamond_score = level->score[SC_DIAMOND];
4466 cav->alien_score = level->score[SC_ROBOT];
4467 cav->tank_score = level->score[SC_SPACESHIP];
4468 cav->bug_score = level->score[SC_BUG];
4469 cav->eater_score = level->score[SC_YAMYAM];
4470 cav->nut_score = level->score[SC_NUT];
4471 cav->dynamite_score = level->score[SC_DYNAMITE];
4472 cav->key_score = level->score[SC_KEY];
4473 cav->exit_score = level->score[SC_TIME_BONUS];
4475 cav->num_eater_arrays = level->num_yamyam_contents;
4477 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4478 for (y = 0; y < 3; y++)
4479 for (x = 0; x < 3; x++)
4480 cav->eater_array[i][y * 3 + x] =
4481 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4483 cav->amoeba_time = level->amoeba_speed;
4484 cav->wonderwall_time = level->time_magic_wall;
4485 cav->wheel_time = level->time_wheel;
4487 cav->android_move_time = level->android_move_time;
4488 cav->android_clone_time = level->android_clone_time;
4489 cav->ball_random = level->ball_random;
4490 cav->ball_active = level->ball_active_initial;
4491 cav->ball_time = level->ball_time;
4492 cav->num_ball_arrays = level->num_ball_contents;
4494 cav->lenses_score = level->lenses_score;
4495 cav->magnify_score = level->magnify_score;
4496 cav->slurp_score = level->slurp_score;
4498 cav->lenses_time = level->lenses_time;
4499 cav->magnify_time = level->magnify_time;
4501 cav->wind_time = 9999;
4502 cav->wind_direction =
4503 map_direction_RND_to_EM(level->wind_direction_initial);
4505 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4506 for (j = 0; j < 8; j++)
4507 cav->ball_array[i][j] =
4508 map_element_RND_to_EM_cave(level->ball_content[i].
4509 e[ball_xy[j][0]][ball_xy[j][1]]);
4511 map_android_clone_elements_RND_to_EM(level);
4513 // first fill the complete playfield with the empty space element
4514 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4515 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4516 cav->cave[x][y] = Cblank;
4518 // then copy the real level contents from level file into the playfield
4519 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4521 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4523 if (level->field[x][y] == EL_AMOEBA_DEAD)
4524 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4526 cav->cave[x][y] = new_element;
4529 for (i = 0; i < MAX_PLAYERS; i++)
4531 cav->player_x[i] = -1;
4532 cav->player_y[i] = -1;
4535 // initialize player positions and delete players from the playfield
4536 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4538 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4540 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4542 cav->player_x[player_nr] = x;
4543 cav->player_y[player_nr] = y;
4545 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4550 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4552 static int ball_xy[8][2] =
4563 struct LevelInfo_EM *level_em = level->native_em_level;
4564 struct CAVE *cav = level_em->cav;
4567 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4568 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4570 level->time = cav->time_seconds;
4571 level->gems_needed = cav->gems_needed;
4573 sprintf(level->name, "Level %d", level->file_info.nr);
4575 level->score[SC_EMERALD] = cav->emerald_score;
4576 level->score[SC_DIAMOND] = cav->diamond_score;
4577 level->score[SC_ROBOT] = cav->alien_score;
4578 level->score[SC_SPACESHIP] = cav->tank_score;
4579 level->score[SC_BUG] = cav->bug_score;
4580 level->score[SC_YAMYAM] = cav->eater_score;
4581 level->score[SC_NUT] = cav->nut_score;
4582 level->score[SC_DYNAMITE] = cav->dynamite_score;
4583 level->score[SC_KEY] = cav->key_score;
4584 level->score[SC_TIME_BONUS] = cav->exit_score;
4586 level->num_yamyam_contents = cav->num_eater_arrays;
4588 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4589 for (y = 0; y < 3; y++)
4590 for (x = 0; x < 3; x++)
4591 level->yamyam_content[i].e[x][y] =
4592 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4594 level->amoeba_speed = cav->amoeba_time;
4595 level->time_magic_wall = cav->wonderwall_time;
4596 level->time_wheel = cav->wheel_time;
4598 level->android_move_time = cav->android_move_time;
4599 level->android_clone_time = cav->android_clone_time;
4600 level->ball_random = cav->ball_random;
4601 level->ball_active_initial = cav->ball_active;
4602 level->ball_time = cav->ball_time;
4603 level->num_ball_contents = cav->num_ball_arrays;
4605 level->lenses_score = cav->lenses_score;
4606 level->magnify_score = cav->magnify_score;
4607 level->slurp_score = cav->slurp_score;
4609 level->lenses_time = cav->lenses_time;
4610 level->magnify_time = cav->magnify_time;
4612 level->wind_direction_initial =
4613 map_direction_EM_to_RND(cav->wind_direction);
4615 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4616 for (j = 0; j < 8; j++)
4617 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4618 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4620 map_android_clone_elements_EM_to_RND(level);
4622 // convert the playfield (some elements need special treatment)
4623 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4625 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4627 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4628 new_element = EL_AMOEBA_DEAD;
4630 level->field[x][y] = new_element;
4633 for (i = 0; i < MAX_PLAYERS; i++)
4635 // in case of all players set to the same field, use the first player
4636 int nr = MAX_PLAYERS - i - 1;
4637 int jx = cav->player_x[nr];
4638 int jy = cav->player_y[nr];
4640 if (jx != -1 && jy != -1)
4641 level->field[jx][jy] = EL_PLAYER_1 + nr;
4644 // time score is counted for each 10 seconds left in Emerald Mine levels
4645 level->time_score_base = 10;
4649 // ----------------------------------------------------------------------------
4650 // functions for loading SP level
4651 // ----------------------------------------------------------------------------
4653 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4655 struct LevelInfo_SP *level_sp = level->native_sp_level;
4656 LevelInfoType *header = &level_sp->header;
4659 level_sp->width = level->fieldx;
4660 level_sp->height = level->fieldy;
4662 for (x = 0; x < level->fieldx; x++)
4663 for (y = 0; y < level->fieldy; y++)
4664 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4666 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4668 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4669 header->LevelTitle[i] = level->name[i];
4670 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4672 header->InfotronsNeeded = level->gems_needed;
4674 header->SpecialPortCount = 0;
4676 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4678 boolean gravity_port_found = FALSE;
4679 boolean gravity_port_valid = FALSE;
4680 int gravity_port_flag;
4681 int gravity_port_base_element;
4682 int element = level->field[x][y];
4684 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4685 element <= EL_SP_GRAVITY_ON_PORT_UP)
4687 gravity_port_found = TRUE;
4688 gravity_port_valid = TRUE;
4689 gravity_port_flag = 1;
4690 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4692 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4693 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4695 gravity_port_found = TRUE;
4696 gravity_port_valid = TRUE;
4697 gravity_port_flag = 0;
4698 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4700 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4701 element <= EL_SP_GRAVITY_PORT_UP)
4703 // change R'n'D style gravity inverting special port to normal port
4704 // (there are no gravity inverting ports in native Supaplex engine)
4706 gravity_port_found = TRUE;
4707 gravity_port_valid = FALSE;
4708 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4711 if (gravity_port_found)
4713 if (gravity_port_valid &&
4714 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4716 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4718 port->PortLocation = (y * level->fieldx + x) * 2;
4719 port->Gravity = gravity_port_flag;
4721 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4723 header->SpecialPortCount++;
4727 // change special gravity port to normal port
4729 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4732 level_sp->playfield[x][y] = element - EL_SP_START;
4737 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4739 struct LevelInfo_SP *level_sp = level->native_sp_level;
4740 LevelInfoType *header = &level_sp->header;
4741 boolean num_invalid_elements = 0;
4744 level->fieldx = level_sp->width;
4745 level->fieldy = level_sp->height;
4747 for (x = 0; x < level->fieldx; x++)
4749 for (y = 0; y < level->fieldy; y++)
4751 int element_old = level_sp->playfield[x][y];
4752 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4754 if (element_new == EL_UNKNOWN)
4756 num_invalid_elements++;
4758 Debug("level:native:SP", "invalid element %d at position %d, %d",
4762 level->field[x][y] = element_new;
4766 if (num_invalid_elements > 0)
4767 Warn("found %d invalid elements%s", num_invalid_elements,
4768 (!options.debug ? " (use '--debug' for more details)" : ""));
4770 for (i = 0; i < MAX_PLAYERS; i++)
4771 level->initial_player_gravity[i] =
4772 (header->InitialGravity == 1 ? TRUE : FALSE);
4774 // skip leading spaces
4775 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4776 if (header->LevelTitle[i] != ' ')
4780 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4781 level->name[j] = header->LevelTitle[i];
4782 level->name[j] = '\0';
4784 // cut trailing spaces
4786 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4787 level->name[j - 1] = '\0';
4789 level->gems_needed = header->InfotronsNeeded;
4791 for (i = 0; i < header->SpecialPortCount; i++)
4793 SpecialPortType *port = &header->SpecialPort[i];
4794 int port_location = port->PortLocation;
4795 int gravity = port->Gravity;
4796 int port_x, port_y, port_element;
4798 port_x = (port_location / 2) % level->fieldx;
4799 port_y = (port_location / 2) / level->fieldx;
4801 if (port_x < 0 || port_x >= level->fieldx ||
4802 port_y < 0 || port_y >= level->fieldy)
4804 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4809 port_element = level->field[port_x][port_y];
4811 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4812 port_element > EL_SP_GRAVITY_PORT_UP)
4814 Warn("no special port at position (%d, %d)", port_x, port_y);
4819 // change previous (wrong) gravity inverting special port to either
4820 // gravity enabling special port or gravity disabling special port
4821 level->field[port_x][port_y] +=
4822 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4823 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4826 // change special gravity ports without database entries to normal ports
4827 for (x = 0; x < level->fieldx; x++)
4828 for (y = 0; y < level->fieldy; y++)
4829 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4830 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4831 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4833 level->time = 0; // no time limit
4834 level->amoeba_speed = 0;
4835 level->time_magic_wall = 0;
4836 level->time_wheel = 0;
4837 level->amoeba_content = EL_EMPTY;
4839 // original Supaplex does not use score values -- rate by playing time
4840 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4841 level->score[i] = 0;
4843 level->rate_time_over_score = TRUE;
4845 // there are no yamyams in supaplex levels
4846 for (i = 0; i < level->num_yamyam_contents; i++)
4847 for (x = 0; x < 3; x++)
4848 for (y = 0; y < 3; y++)
4849 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4852 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4854 struct LevelInfo_SP *level_sp = level->native_sp_level;
4855 struct DemoInfo_SP *demo = &level_sp->demo;
4858 // always start with reliable default values
4859 demo->is_available = FALSE;
4862 if (TAPE_IS_EMPTY(tape))
4865 demo->level_nr = tape.level_nr; // (currently not used)
4867 level_sp->header.DemoRandomSeed = tape.random_seed;
4871 for (i = 0; i < tape.length; i++)
4873 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4874 int demo_repeat = tape.pos[i].delay;
4875 int demo_entries = (demo_repeat + 15) / 16;
4877 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4879 Warn("tape truncated: size exceeds maximum SP demo size %d",
4885 for (j = 0; j < demo_repeat / 16; j++)
4886 demo->data[demo->length++] = 0xf0 | demo_action;
4888 if (demo_repeat % 16)
4889 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4892 demo->is_available = TRUE;
4895 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4897 struct LevelInfo_SP *level_sp = level->native_sp_level;
4898 struct DemoInfo_SP *demo = &level_sp->demo;
4899 char *filename = level->file_info.filename;
4902 // always start with reliable default values
4903 setTapeInfoToDefaults();
4905 if (!demo->is_available)
4908 tape.level_nr = demo->level_nr; // (currently not used)
4909 tape.random_seed = level_sp->header.DemoRandomSeed;
4911 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4914 tape.pos[tape.counter].delay = 0;
4916 for (i = 0; i < demo->length; i++)
4918 int demo_action = demo->data[i] & 0x0f;
4919 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4920 int tape_action = map_key_SP_to_RND(demo_action);
4921 int tape_repeat = demo_repeat + 1;
4922 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4923 boolean success = 0;
4926 for (j = 0; j < tape_repeat; j++)
4927 success = TapeAddAction(action);
4931 Warn("SP demo truncated: size exceeds maximum tape size %d",
4938 TapeHaltRecording();
4942 // ----------------------------------------------------------------------------
4943 // functions for loading MM level
4944 // ----------------------------------------------------------------------------
4946 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4948 struct LevelInfo_MM *level_mm = level->native_mm_level;
4951 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4952 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4954 level_mm->time = level->time;
4955 level_mm->kettles_needed = level->gems_needed;
4956 level_mm->auto_count_kettles = level->auto_count_gems;
4958 level_mm->mm_laser_red = level->mm_laser_red;
4959 level_mm->mm_laser_green = level->mm_laser_green;
4960 level_mm->mm_laser_blue = level->mm_laser_blue;
4962 level_mm->df_laser_red = level->df_laser_red;
4963 level_mm->df_laser_green = level->df_laser_green;
4964 level_mm->df_laser_blue = level->df_laser_blue;
4966 strcpy(level_mm->name, level->name);
4967 strcpy(level_mm->author, level->author);
4969 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4970 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4971 level_mm->score[SC_KEY] = level->score[SC_KEY];
4972 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4973 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4975 level_mm->amoeba_speed = level->amoeba_speed;
4976 level_mm->time_fuse = level->mm_time_fuse;
4977 level_mm->time_bomb = level->mm_time_bomb;
4978 level_mm->time_ball = level->mm_time_ball;
4979 level_mm->time_block = level->mm_time_block;
4981 level_mm->num_ball_contents = level->num_mm_ball_contents;
4982 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4983 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4984 level_mm->explode_ball = level->explode_mm_ball;
4986 for (i = 0; i < level->num_mm_ball_contents; i++)
4987 level_mm->ball_content[i] =
4988 map_element_RND_to_MM(level->mm_ball_content[i]);
4990 for (x = 0; x < level->fieldx; x++)
4991 for (y = 0; y < level->fieldy; y++)
4993 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4996 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4998 struct LevelInfo_MM *level_mm = level->native_mm_level;
5001 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5002 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5004 level->time = level_mm->time;
5005 level->gems_needed = level_mm->kettles_needed;
5006 level->auto_count_gems = level_mm->auto_count_kettles;
5008 level->mm_laser_red = level_mm->mm_laser_red;
5009 level->mm_laser_green = level_mm->mm_laser_green;
5010 level->mm_laser_blue = level_mm->mm_laser_blue;
5012 level->df_laser_red = level_mm->df_laser_red;
5013 level->df_laser_green = level_mm->df_laser_green;
5014 level->df_laser_blue = level_mm->df_laser_blue;
5016 strcpy(level->name, level_mm->name);
5018 // only overwrite author from 'levelinfo.conf' if author defined in level
5019 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5020 strcpy(level->author, level_mm->author);
5022 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5023 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5024 level->score[SC_KEY] = level_mm->score[SC_KEY];
5025 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5026 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5028 level->amoeba_speed = level_mm->amoeba_speed;
5029 level->mm_time_fuse = level_mm->time_fuse;
5030 level->mm_time_bomb = level_mm->time_bomb;
5031 level->mm_time_ball = level_mm->time_ball;
5032 level->mm_time_block = level_mm->time_block;
5034 level->num_mm_ball_contents = level_mm->num_ball_contents;
5035 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5036 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5037 level->explode_mm_ball = level_mm->explode_ball;
5039 for (i = 0; i < level->num_mm_ball_contents; i++)
5040 level->mm_ball_content[i] =
5041 map_element_MM_to_RND(level_mm->ball_content[i]);
5043 for (x = 0; x < level->fieldx; x++)
5044 for (y = 0; y < level->fieldy; y++)
5045 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5049 // ----------------------------------------------------------------------------
5050 // functions for loading DC level
5051 // ----------------------------------------------------------------------------
5053 #define DC_LEVEL_HEADER_SIZE 344
5055 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5058 static int last_data_encoded;
5062 int diff_hi, diff_lo;
5063 int data_hi, data_lo;
5064 unsigned short data_decoded;
5068 last_data_encoded = 0;
5075 diff = data_encoded - last_data_encoded;
5076 diff_hi = diff & ~0xff;
5077 diff_lo = diff & 0xff;
5081 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5082 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5083 data_hi = data_hi & 0xff00;
5085 data_decoded = data_hi | data_lo;
5087 last_data_encoded = data_encoded;
5089 offset1 = (offset1 + 1) % 31;
5090 offset2 = offset2 & 0xff;
5092 return data_decoded;
5095 static int getMappedElement_DC(int element)
5103 // 0x0117 - 0x036e: (?)
5106 // 0x042d - 0x0684: (?)
5122 element = EL_CRYSTAL;
5125 case 0x0e77: // quicksand (boulder)
5126 element = EL_QUICKSAND_FAST_FULL;
5129 case 0x0e99: // slow quicksand (boulder)
5130 element = EL_QUICKSAND_FULL;
5134 element = EL_EM_EXIT_OPEN;
5138 element = EL_EM_EXIT_CLOSED;
5142 element = EL_EM_STEEL_EXIT_OPEN;
5146 element = EL_EM_STEEL_EXIT_CLOSED;
5149 case 0x0f4f: // dynamite (lit 1)
5150 element = EL_EM_DYNAMITE_ACTIVE;
5153 case 0x0f57: // dynamite (lit 2)
5154 element = EL_EM_DYNAMITE_ACTIVE;
5157 case 0x0f5f: // dynamite (lit 3)
5158 element = EL_EM_DYNAMITE_ACTIVE;
5161 case 0x0f67: // dynamite (lit 4)
5162 element = EL_EM_DYNAMITE_ACTIVE;
5169 element = EL_AMOEBA_WET;
5173 element = EL_AMOEBA_DROP;
5177 element = EL_DC_MAGIC_WALL;
5181 element = EL_SPACESHIP_UP;
5185 element = EL_SPACESHIP_DOWN;
5189 element = EL_SPACESHIP_LEFT;
5193 element = EL_SPACESHIP_RIGHT;
5197 element = EL_BUG_UP;
5201 element = EL_BUG_DOWN;
5205 element = EL_BUG_LEFT;
5209 element = EL_BUG_RIGHT;
5213 element = EL_MOLE_UP;
5217 element = EL_MOLE_DOWN;
5221 element = EL_MOLE_LEFT;
5225 element = EL_MOLE_RIGHT;
5233 element = EL_YAMYAM_UP;
5237 element = EL_SWITCHGATE_OPEN;
5241 element = EL_SWITCHGATE_CLOSED;
5245 element = EL_DC_SWITCHGATE_SWITCH_UP;
5249 element = EL_TIMEGATE_CLOSED;
5252 case 0x144c: // conveyor belt switch (green)
5253 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5256 case 0x144f: // conveyor belt switch (red)
5257 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5260 case 0x1452: // conveyor belt switch (blue)
5261 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5265 element = EL_CONVEYOR_BELT_3_MIDDLE;
5269 element = EL_CONVEYOR_BELT_3_LEFT;
5273 element = EL_CONVEYOR_BELT_3_RIGHT;
5277 element = EL_CONVEYOR_BELT_1_MIDDLE;
5281 element = EL_CONVEYOR_BELT_1_LEFT;
5285 element = EL_CONVEYOR_BELT_1_RIGHT;
5289 element = EL_CONVEYOR_BELT_4_MIDDLE;
5293 element = EL_CONVEYOR_BELT_4_LEFT;
5297 element = EL_CONVEYOR_BELT_4_RIGHT;
5301 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5305 element = EL_EXPANDABLE_WALL_VERTICAL;
5309 element = EL_EXPANDABLE_WALL_ANY;
5312 case 0x14ce: // growing steel wall (left/right)
5313 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5316 case 0x14df: // growing steel wall (up/down)
5317 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5320 case 0x14e8: // growing steel wall (up/down/left/right)
5321 element = EL_EXPANDABLE_STEELWALL_ANY;
5325 element = EL_SHIELD_DEADLY;
5329 element = EL_EXTRA_TIME;
5337 element = EL_EMPTY_SPACE;
5340 case 0x1578: // quicksand (empty)
5341 element = EL_QUICKSAND_FAST_EMPTY;
5344 case 0x1579: // slow quicksand (empty)
5345 element = EL_QUICKSAND_EMPTY;
5355 element = EL_EM_DYNAMITE;
5358 case 0x15a1: // key (red)
5359 element = EL_EM_KEY_1;
5362 case 0x15a2: // key (yellow)
5363 element = EL_EM_KEY_2;
5366 case 0x15a3: // key (blue)
5367 element = EL_EM_KEY_4;
5370 case 0x15a4: // key (green)
5371 element = EL_EM_KEY_3;
5374 case 0x15a5: // key (white)
5375 element = EL_DC_KEY_WHITE;
5379 element = EL_WALL_SLIPPERY;
5386 case 0x15a8: // wall (not round)
5390 case 0x15a9: // (blue)
5391 element = EL_CHAR_A;
5394 case 0x15aa: // (blue)
5395 element = EL_CHAR_B;
5398 case 0x15ab: // (blue)
5399 element = EL_CHAR_C;
5402 case 0x15ac: // (blue)
5403 element = EL_CHAR_D;
5406 case 0x15ad: // (blue)
5407 element = EL_CHAR_E;
5410 case 0x15ae: // (blue)
5411 element = EL_CHAR_F;
5414 case 0x15af: // (blue)
5415 element = EL_CHAR_G;
5418 case 0x15b0: // (blue)
5419 element = EL_CHAR_H;
5422 case 0x15b1: // (blue)
5423 element = EL_CHAR_I;
5426 case 0x15b2: // (blue)
5427 element = EL_CHAR_J;
5430 case 0x15b3: // (blue)
5431 element = EL_CHAR_K;
5434 case 0x15b4: // (blue)
5435 element = EL_CHAR_L;
5438 case 0x15b5: // (blue)
5439 element = EL_CHAR_M;
5442 case 0x15b6: // (blue)
5443 element = EL_CHAR_N;
5446 case 0x15b7: // (blue)
5447 element = EL_CHAR_O;
5450 case 0x15b8: // (blue)
5451 element = EL_CHAR_P;
5454 case 0x15b9: // (blue)
5455 element = EL_CHAR_Q;
5458 case 0x15ba: // (blue)
5459 element = EL_CHAR_R;
5462 case 0x15bb: // (blue)
5463 element = EL_CHAR_S;
5466 case 0x15bc: // (blue)
5467 element = EL_CHAR_T;
5470 case 0x15bd: // (blue)
5471 element = EL_CHAR_U;
5474 case 0x15be: // (blue)
5475 element = EL_CHAR_V;
5478 case 0x15bf: // (blue)
5479 element = EL_CHAR_W;
5482 case 0x15c0: // (blue)
5483 element = EL_CHAR_X;
5486 case 0x15c1: // (blue)
5487 element = EL_CHAR_Y;
5490 case 0x15c2: // (blue)
5491 element = EL_CHAR_Z;
5494 case 0x15c3: // (blue)
5495 element = EL_CHAR_AUMLAUT;
5498 case 0x15c4: // (blue)
5499 element = EL_CHAR_OUMLAUT;
5502 case 0x15c5: // (blue)
5503 element = EL_CHAR_UUMLAUT;
5506 case 0x15c6: // (blue)
5507 element = EL_CHAR_0;
5510 case 0x15c7: // (blue)
5511 element = EL_CHAR_1;
5514 case 0x15c8: // (blue)
5515 element = EL_CHAR_2;
5518 case 0x15c9: // (blue)
5519 element = EL_CHAR_3;
5522 case 0x15ca: // (blue)
5523 element = EL_CHAR_4;
5526 case 0x15cb: // (blue)
5527 element = EL_CHAR_5;
5530 case 0x15cc: // (blue)
5531 element = EL_CHAR_6;
5534 case 0x15cd: // (blue)
5535 element = EL_CHAR_7;
5538 case 0x15ce: // (blue)
5539 element = EL_CHAR_8;
5542 case 0x15cf: // (blue)
5543 element = EL_CHAR_9;
5546 case 0x15d0: // (blue)
5547 element = EL_CHAR_PERIOD;
5550 case 0x15d1: // (blue)
5551 element = EL_CHAR_EXCLAM;
5554 case 0x15d2: // (blue)
5555 element = EL_CHAR_COLON;
5558 case 0x15d3: // (blue)
5559 element = EL_CHAR_LESS;
5562 case 0x15d4: // (blue)
5563 element = EL_CHAR_GREATER;
5566 case 0x15d5: // (blue)
5567 element = EL_CHAR_QUESTION;
5570 case 0x15d6: // (blue)
5571 element = EL_CHAR_COPYRIGHT;
5574 case 0x15d7: // (blue)
5575 element = EL_CHAR_UP;
5578 case 0x15d8: // (blue)
5579 element = EL_CHAR_DOWN;
5582 case 0x15d9: // (blue)
5583 element = EL_CHAR_BUTTON;
5586 case 0x15da: // (blue)
5587 element = EL_CHAR_PLUS;
5590 case 0x15db: // (blue)
5591 element = EL_CHAR_MINUS;
5594 case 0x15dc: // (blue)
5595 element = EL_CHAR_APOSTROPHE;
5598 case 0x15dd: // (blue)
5599 element = EL_CHAR_PARENLEFT;
5602 case 0x15de: // (blue)
5603 element = EL_CHAR_PARENRIGHT;
5606 case 0x15df: // (green)
5607 element = EL_CHAR_A;
5610 case 0x15e0: // (green)
5611 element = EL_CHAR_B;
5614 case 0x15e1: // (green)
5615 element = EL_CHAR_C;
5618 case 0x15e2: // (green)
5619 element = EL_CHAR_D;
5622 case 0x15e3: // (green)
5623 element = EL_CHAR_E;
5626 case 0x15e4: // (green)
5627 element = EL_CHAR_F;
5630 case 0x15e5: // (green)
5631 element = EL_CHAR_G;
5634 case 0x15e6: // (green)
5635 element = EL_CHAR_H;
5638 case 0x15e7: // (green)
5639 element = EL_CHAR_I;
5642 case 0x15e8: // (green)
5643 element = EL_CHAR_J;
5646 case 0x15e9: // (green)
5647 element = EL_CHAR_K;
5650 case 0x15ea: // (green)
5651 element = EL_CHAR_L;
5654 case 0x15eb: // (green)
5655 element = EL_CHAR_M;
5658 case 0x15ec: // (green)
5659 element = EL_CHAR_N;
5662 case 0x15ed: // (green)
5663 element = EL_CHAR_O;
5666 case 0x15ee: // (green)
5667 element = EL_CHAR_P;
5670 case 0x15ef: // (green)
5671 element = EL_CHAR_Q;
5674 case 0x15f0: // (green)
5675 element = EL_CHAR_R;
5678 case 0x15f1: // (green)
5679 element = EL_CHAR_S;
5682 case 0x15f2: // (green)
5683 element = EL_CHAR_T;
5686 case 0x15f3: // (green)
5687 element = EL_CHAR_U;
5690 case 0x15f4: // (green)
5691 element = EL_CHAR_V;
5694 case 0x15f5: // (green)
5695 element = EL_CHAR_W;
5698 case 0x15f6: // (green)
5699 element = EL_CHAR_X;
5702 case 0x15f7: // (green)
5703 element = EL_CHAR_Y;
5706 case 0x15f8: // (green)
5707 element = EL_CHAR_Z;
5710 case 0x15f9: // (green)
5711 element = EL_CHAR_AUMLAUT;
5714 case 0x15fa: // (green)
5715 element = EL_CHAR_OUMLAUT;
5718 case 0x15fb: // (green)
5719 element = EL_CHAR_UUMLAUT;
5722 case 0x15fc: // (green)
5723 element = EL_CHAR_0;
5726 case 0x15fd: // (green)
5727 element = EL_CHAR_1;
5730 case 0x15fe: // (green)
5731 element = EL_CHAR_2;
5734 case 0x15ff: // (green)
5735 element = EL_CHAR_3;
5738 case 0x1600: // (green)
5739 element = EL_CHAR_4;
5742 case 0x1601: // (green)
5743 element = EL_CHAR_5;
5746 case 0x1602: // (green)
5747 element = EL_CHAR_6;
5750 case 0x1603: // (green)
5751 element = EL_CHAR_7;
5754 case 0x1604: // (green)
5755 element = EL_CHAR_8;
5758 case 0x1605: // (green)
5759 element = EL_CHAR_9;
5762 case 0x1606: // (green)
5763 element = EL_CHAR_PERIOD;
5766 case 0x1607: // (green)
5767 element = EL_CHAR_EXCLAM;
5770 case 0x1608: // (green)
5771 element = EL_CHAR_COLON;
5774 case 0x1609: // (green)
5775 element = EL_CHAR_LESS;
5778 case 0x160a: // (green)
5779 element = EL_CHAR_GREATER;
5782 case 0x160b: // (green)
5783 element = EL_CHAR_QUESTION;
5786 case 0x160c: // (green)
5787 element = EL_CHAR_COPYRIGHT;
5790 case 0x160d: // (green)
5791 element = EL_CHAR_UP;
5794 case 0x160e: // (green)
5795 element = EL_CHAR_DOWN;
5798 case 0x160f: // (green)
5799 element = EL_CHAR_BUTTON;
5802 case 0x1610: // (green)
5803 element = EL_CHAR_PLUS;
5806 case 0x1611: // (green)
5807 element = EL_CHAR_MINUS;
5810 case 0x1612: // (green)
5811 element = EL_CHAR_APOSTROPHE;
5814 case 0x1613: // (green)
5815 element = EL_CHAR_PARENLEFT;
5818 case 0x1614: // (green)
5819 element = EL_CHAR_PARENRIGHT;
5822 case 0x1615: // (blue steel)
5823 element = EL_STEEL_CHAR_A;
5826 case 0x1616: // (blue steel)
5827 element = EL_STEEL_CHAR_B;
5830 case 0x1617: // (blue steel)
5831 element = EL_STEEL_CHAR_C;
5834 case 0x1618: // (blue steel)
5835 element = EL_STEEL_CHAR_D;
5838 case 0x1619: // (blue steel)
5839 element = EL_STEEL_CHAR_E;
5842 case 0x161a: // (blue steel)
5843 element = EL_STEEL_CHAR_F;
5846 case 0x161b: // (blue steel)
5847 element = EL_STEEL_CHAR_G;
5850 case 0x161c: // (blue steel)
5851 element = EL_STEEL_CHAR_H;
5854 case 0x161d: // (blue steel)
5855 element = EL_STEEL_CHAR_I;
5858 case 0x161e: // (blue steel)
5859 element = EL_STEEL_CHAR_J;
5862 case 0x161f: // (blue steel)
5863 element = EL_STEEL_CHAR_K;
5866 case 0x1620: // (blue steel)
5867 element = EL_STEEL_CHAR_L;
5870 case 0x1621: // (blue steel)
5871 element = EL_STEEL_CHAR_M;
5874 case 0x1622: // (blue steel)
5875 element = EL_STEEL_CHAR_N;
5878 case 0x1623: // (blue steel)
5879 element = EL_STEEL_CHAR_O;
5882 case 0x1624: // (blue steel)
5883 element = EL_STEEL_CHAR_P;
5886 case 0x1625: // (blue steel)
5887 element = EL_STEEL_CHAR_Q;
5890 case 0x1626: // (blue steel)
5891 element = EL_STEEL_CHAR_R;
5894 case 0x1627: // (blue steel)
5895 element = EL_STEEL_CHAR_S;
5898 case 0x1628: // (blue steel)
5899 element = EL_STEEL_CHAR_T;
5902 case 0x1629: // (blue steel)
5903 element = EL_STEEL_CHAR_U;
5906 case 0x162a: // (blue steel)
5907 element = EL_STEEL_CHAR_V;
5910 case 0x162b: // (blue steel)
5911 element = EL_STEEL_CHAR_W;
5914 case 0x162c: // (blue steel)
5915 element = EL_STEEL_CHAR_X;
5918 case 0x162d: // (blue steel)
5919 element = EL_STEEL_CHAR_Y;
5922 case 0x162e: // (blue steel)
5923 element = EL_STEEL_CHAR_Z;
5926 case 0x162f: // (blue steel)
5927 element = EL_STEEL_CHAR_AUMLAUT;
5930 case 0x1630: // (blue steel)
5931 element = EL_STEEL_CHAR_OUMLAUT;
5934 case 0x1631: // (blue steel)
5935 element = EL_STEEL_CHAR_UUMLAUT;
5938 case 0x1632: // (blue steel)
5939 element = EL_STEEL_CHAR_0;
5942 case 0x1633: // (blue steel)
5943 element = EL_STEEL_CHAR_1;
5946 case 0x1634: // (blue steel)
5947 element = EL_STEEL_CHAR_2;
5950 case 0x1635: // (blue steel)
5951 element = EL_STEEL_CHAR_3;
5954 case 0x1636: // (blue steel)
5955 element = EL_STEEL_CHAR_4;
5958 case 0x1637: // (blue steel)
5959 element = EL_STEEL_CHAR_5;
5962 case 0x1638: // (blue steel)
5963 element = EL_STEEL_CHAR_6;
5966 case 0x1639: // (blue steel)
5967 element = EL_STEEL_CHAR_7;
5970 case 0x163a: // (blue steel)
5971 element = EL_STEEL_CHAR_8;
5974 case 0x163b: // (blue steel)
5975 element = EL_STEEL_CHAR_9;
5978 case 0x163c: // (blue steel)
5979 element = EL_STEEL_CHAR_PERIOD;
5982 case 0x163d: // (blue steel)
5983 element = EL_STEEL_CHAR_EXCLAM;
5986 case 0x163e: // (blue steel)
5987 element = EL_STEEL_CHAR_COLON;
5990 case 0x163f: // (blue steel)
5991 element = EL_STEEL_CHAR_LESS;
5994 case 0x1640: // (blue steel)
5995 element = EL_STEEL_CHAR_GREATER;
5998 case 0x1641: // (blue steel)
5999 element = EL_STEEL_CHAR_QUESTION;
6002 case 0x1642: // (blue steel)
6003 element = EL_STEEL_CHAR_COPYRIGHT;
6006 case 0x1643: // (blue steel)
6007 element = EL_STEEL_CHAR_UP;
6010 case 0x1644: // (blue steel)
6011 element = EL_STEEL_CHAR_DOWN;
6014 case 0x1645: // (blue steel)
6015 element = EL_STEEL_CHAR_BUTTON;
6018 case 0x1646: // (blue steel)
6019 element = EL_STEEL_CHAR_PLUS;
6022 case 0x1647: // (blue steel)
6023 element = EL_STEEL_CHAR_MINUS;
6026 case 0x1648: // (blue steel)
6027 element = EL_STEEL_CHAR_APOSTROPHE;
6030 case 0x1649: // (blue steel)
6031 element = EL_STEEL_CHAR_PARENLEFT;
6034 case 0x164a: // (blue steel)
6035 element = EL_STEEL_CHAR_PARENRIGHT;
6038 case 0x164b: // (green steel)
6039 element = EL_STEEL_CHAR_A;
6042 case 0x164c: // (green steel)
6043 element = EL_STEEL_CHAR_B;
6046 case 0x164d: // (green steel)
6047 element = EL_STEEL_CHAR_C;
6050 case 0x164e: // (green steel)
6051 element = EL_STEEL_CHAR_D;
6054 case 0x164f: // (green steel)
6055 element = EL_STEEL_CHAR_E;
6058 case 0x1650: // (green steel)
6059 element = EL_STEEL_CHAR_F;
6062 case 0x1651: // (green steel)
6063 element = EL_STEEL_CHAR_G;
6066 case 0x1652: // (green steel)
6067 element = EL_STEEL_CHAR_H;
6070 case 0x1653: // (green steel)
6071 element = EL_STEEL_CHAR_I;
6074 case 0x1654: // (green steel)
6075 element = EL_STEEL_CHAR_J;
6078 case 0x1655: // (green steel)
6079 element = EL_STEEL_CHAR_K;
6082 case 0x1656: // (green steel)
6083 element = EL_STEEL_CHAR_L;
6086 case 0x1657: // (green steel)
6087 element = EL_STEEL_CHAR_M;
6090 case 0x1658: // (green steel)
6091 element = EL_STEEL_CHAR_N;
6094 case 0x1659: // (green steel)
6095 element = EL_STEEL_CHAR_O;
6098 case 0x165a: // (green steel)
6099 element = EL_STEEL_CHAR_P;
6102 case 0x165b: // (green steel)
6103 element = EL_STEEL_CHAR_Q;
6106 case 0x165c: // (green steel)
6107 element = EL_STEEL_CHAR_R;
6110 case 0x165d: // (green steel)
6111 element = EL_STEEL_CHAR_S;
6114 case 0x165e: // (green steel)
6115 element = EL_STEEL_CHAR_T;
6118 case 0x165f: // (green steel)
6119 element = EL_STEEL_CHAR_U;
6122 case 0x1660: // (green steel)
6123 element = EL_STEEL_CHAR_V;
6126 case 0x1661: // (green steel)
6127 element = EL_STEEL_CHAR_W;
6130 case 0x1662: // (green steel)
6131 element = EL_STEEL_CHAR_X;
6134 case 0x1663: // (green steel)
6135 element = EL_STEEL_CHAR_Y;
6138 case 0x1664: // (green steel)
6139 element = EL_STEEL_CHAR_Z;
6142 case 0x1665: // (green steel)
6143 element = EL_STEEL_CHAR_AUMLAUT;
6146 case 0x1666: // (green steel)
6147 element = EL_STEEL_CHAR_OUMLAUT;
6150 case 0x1667: // (green steel)
6151 element = EL_STEEL_CHAR_UUMLAUT;
6154 case 0x1668: // (green steel)
6155 element = EL_STEEL_CHAR_0;
6158 case 0x1669: // (green steel)
6159 element = EL_STEEL_CHAR_1;
6162 case 0x166a: // (green steel)
6163 element = EL_STEEL_CHAR_2;
6166 case 0x166b: // (green steel)
6167 element = EL_STEEL_CHAR_3;
6170 case 0x166c: // (green steel)
6171 element = EL_STEEL_CHAR_4;
6174 case 0x166d: // (green steel)
6175 element = EL_STEEL_CHAR_5;
6178 case 0x166e: // (green steel)
6179 element = EL_STEEL_CHAR_6;
6182 case 0x166f: // (green steel)
6183 element = EL_STEEL_CHAR_7;
6186 case 0x1670: // (green steel)
6187 element = EL_STEEL_CHAR_8;
6190 case 0x1671: // (green steel)
6191 element = EL_STEEL_CHAR_9;
6194 case 0x1672: // (green steel)
6195 element = EL_STEEL_CHAR_PERIOD;
6198 case 0x1673: // (green steel)
6199 element = EL_STEEL_CHAR_EXCLAM;
6202 case 0x1674: // (green steel)
6203 element = EL_STEEL_CHAR_COLON;
6206 case 0x1675: // (green steel)
6207 element = EL_STEEL_CHAR_LESS;
6210 case 0x1676: // (green steel)
6211 element = EL_STEEL_CHAR_GREATER;
6214 case 0x1677: // (green steel)
6215 element = EL_STEEL_CHAR_QUESTION;
6218 case 0x1678: // (green steel)
6219 element = EL_STEEL_CHAR_COPYRIGHT;
6222 case 0x1679: // (green steel)
6223 element = EL_STEEL_CHAR_UP;
6226 case 0x167a: // (green steel)
6227 element = EL_STEEL_CHAR_DOWN;
6230 case 0x167b: // (green steel)
6231 element = EL_STEEL_CHAR_BUTTON;
6234 case 0x167c: // (green steel)
6235 element = EL_STEEL_CHAR_PLUS;
6238 case 0x167d: // (green steel)
6239 element = EL_STEEL_CHAR_MINUS;
6242 case 0x167e: // (green steel)
6243 element = EL_STEEL_CHAR_APOSTROPHE;
6246 case 0x167f: // (green steel)
6247 element = EL_STEEL_CHAR_PARENLEFT;
6250 case 0x1680: // (green steel)
6251 element = EL_STEEL_CHAR_PARENRIGHT;
6254 case 0x1681: // gate (red)
6255 element = EL_EM_GATE_1;
6258 case 0x1682: // secret gate (red)
6259 element = EL_EM_GATE_1_GRAY;
6262 case 0x1683: // gate (yellow)
6263 element = EL_EM_GATE_2;
6266 case 0x1684: // secret gate (yellow)
6267 element = EL_EM_GATE_2_GRAY;
6270 case 0x1685: // gate (blue)
6271 element = EL_EM_GATE_4;
6274 case 0x1686: // secret gate (blue)
6275 element = EL_EM_GATE_4_GRAY;
6278 case 0x1687: // gate (green)
6279 element = EL_EM_GATE_3;
6282 case 0x1688: // secret gate (green)
6283 element = EL_EM_GATE_3_GRAY;
6286 case 0x1689: // gate (white)
6287 element = EL_DC_GATE_WHITE;
6290 case 0x168a: // secret gate (white)
6291 element = EL_DC_GATE_WHITE_GRAY;
6294 case 0x168b: // secret gate (no key)
6295 element = EL_DC_GATE_FAKE_GRAY;
6299 element = EL_ROBOT_WHEEL;
6303 element = EL_DC_TIMEGATE_SWITCH;
6307 element = EL_ACID_POOL_BOTTOM;
6311 element = EL_ACID_POOL_TOPLEFT;
6315 element = EL_ACID_POOL_TOPRIGHT;
6319 element = EL_ACID_POOL_BOTTOMLEFT;
6323 element = EL_ACID_POOL_BOTTOMRIGHT;
6327 element = EL_STEELWALL;
6331 element = EL_STEELWALL_SLIPPERY;
6334 case 0x1695: // steel wall (not round)
6335 element = EL_STEELWALL;
6338 case 0x1696: // steel wall (left)
6339 element = EL_DC_STEELWALL_1_LEFT;
6342 case 0x1697: // steel wall (bottom)
6343 element = EL_DC_STEELWALL_1_BOTTOM;
6346 case 0x1698: // steel wall (right)
6347 element = EL_DC_STEELWALL_1_RIGHT;
6350 case 0x1699: // steel wall (top)
6351 element = EL_DC_STEELWALL_1_TOP;
6354 case 0x169a: // steel wall (left/bottom)
6355 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6358 case 0x169b: // steel wall (right/bottom)
6359 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6362 case 0x169c: // steel wall (right/top)
6363 element = EL_DC_STEELWALL_1_TOPRIGHT;
6366 case 0x169d: // steel wall (left/top)
6367 element = EL_DC_STEELWALL_1_TOPLEFT;
6370 case 0x169e: // steel wall (right/bottom small)
6371 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6374 case 0x169f: // steel wall (left/bottom small)
6375 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6378 case 0x16a0: // steel wall (right/top small)
6379 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6382 case 0x16a1: // steel wall (left/top small)
6383 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6386 case 0x16a2: // steel wall (left/right)
6387 element = EL_DC_STEELWALL_1_VERTICAL;
6390 case 0x16a3: // steel wall (top/bottom)
6391 element = EL_DC_STEELWALL_1_HORIZONTAL;
6394 case 0x16a4: // steel wall 2 (left end)
6395 element = EL_DC_STEELWALL_2_LEFT;
6398 case 0x16a5: // steel wall 2 (right end)
6399 element = EL_DC_STEELWALL_2_RIGHT;
6402 case 0x16a6: // steel wall 2 (top end)
6403 element = EL_DC_STEELWALL_2_TOP;
6406 case 0x16a7: // steel wall 2 (bottom end)
6407 element = EL_DC_STEELWALL_2_BOTTOM;
6410 case 0x16a8: // steel wall 2 (left/right)
6411 element = EL_DC_STEELWALL_2_HORIZONTAL;
6414 case 0x16a9: // steel wall 2 (up/down)
6415 element = EL_DC_STEELWALL_2_VERTICAL;
6418 case 0x16aa: // steel wall 2 (mid)
6419 element = EL_DC_STEELWALL_2_MIDDLE;
6423 element = EL_SIGN_EXCLAMATION;
6427 element = EL_SIGN_RADIOACTIVITY;
6431 element = EL_SIGN_STOP;
6435 element = EL_SIGN_WHEELCHAIR;
6439 element = EL_SIGN_PARKING;
6443 element = EL_SIGN_NO_ENTRY;
6447 element = EL_SIGN_HEART;
6451 element = EL_SIGN_GIVE_WAY;
6455 element = EL_SIGN_ENTRY_FORBIDDEN;
6459 element = EL_SIGN_EMERGENCY_EXIT;
6463 element = EL_SIGN_YIN_YANG;
6467 element = EL_WALL_EMERALD;
6471 element = EL_WALL_DIAMOND;
6475 element = EL_WALL_PEARL;
6479 element = EL_WALL_CRYSTAL;
6483 element = EL_INVISIBLE_WALL;
6487 element = EL_INVISIBLE_STEELWALL;
6491 // EL_INVISIBLE_SAND
6494 element = EL_LIGHT_SWITCH;
6498 element = EL_ENVELOPE_1;
6502 if (element >= 0x0117 && element <= 0x036e) // (?)
6503 element = EL_DIAMOND;
6504 else if (element >= 0x042d && element <= 0x0684) // (?)
6505 element = EL_EMERALD;
6506 else if (element >= 0x157c && element <= 0x158b)
6508 else if (element >= 0x1590 && element <= 0x159f)
6509 element = EL_DC_LANDMINE;
6510 else if (element >= 0x16bc && element <= 0x16cb)
6511 element = EL_INVISIBLE_SAND;
6514 Warn("unknown Diamond Caves element 0x%04x", element);
6516 element = EL_UNKNOWN;
6521 return getMappedElement(element);
6524 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6526 byte header[DC_LEVEL_HEADER_SIZE];
6528 int envelope_header_pos = 62;
6529 int envelope_content_pos = 94;
6530 int level_name_pos = 251;
6531 int level_author_pos = 292;
6532 int envelope_header_len;
6533 int envelope_content_len;
6535 int level_author_len;
6537 int num_yamyam_contents;
6540 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6542 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6544 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6546 header[i * 2 + 0] = header_word >> 8;
6547 header[i * 2 + 1] = header_word & 0xff;
6550 // read some values from level header to check level decoding integrity
6551 fieldx = header[6] | (header[7] << 8);
6552 fieldy = header[8] | (header[9] << 8);
6553 num_yamyam_contents = header[60] | (header[61] << 8);
6555 // do some simple sanity checks to ensure that level was correctly decoded
6556 if (fieldx < 1 || fieldx > 256 ||
6557 fieldy < 1 || fieldy > 256 ||
6558 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6560 level->no_valid_file = TRUE;
6562 Warn("cannot decode level from stream -- using empty level");
6567 // maximum envelope header size is 31 bytes
6568 envelope_header_len = header[envelope_header_pos];
6569 // maximum envelope content size is 110 (156?) bytes
6570 envelope_content_len = header[envelope_content_pos];
6572 // maximum level title size is 40 bytes
6573 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6574 // maximum level author size is 30 (51?) bytes
6575 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6579 for (i = 0; i < envelope_header_len; i++)
6580 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6581 level->envelope[0].text[envelope_size++] =
6582 header[envelope_header_pos + 1 + i];
6584 if (envelope_header_len > 0 && envelope_content_len > 0)
6586 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6587 level->envelope[0].text[envelope_size++] = '\n';
6588 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6589 level->envelope[0].text[envelope_size++] = '\n';
6592 for (i = 0; i < envelope_content_len; i++)
6593 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6594 level->envelope[0].text[envelope_size++] =
6595 header[envelope_content_pos + 1 + i];
6597 level->envelope[0].text[envelope_size] = '\0';
6599 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6600 level->envelope[0].ysize = 10;
6601 level->envelope[0].autowrap = TRUE;
6602 level->envelope[0].centered = TRUE;
6604 for (i = 0; i < level_name_len; i++)
6605 level->name[i] = header[level_name_pos + 1 + i];
6606 level->name[level_name_len] = '\0';
6608 for (i = 0; i < level_author_len; i++)
6609 level->author[i] = header[level_author_pos + 1 + i];
6610 level->author[level_author_len] = '\0';
6612 num_yamyam_contents = header[60] | (header[61] << 8);
6613 level->num_yamyam_contents =
6614 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6616 for (i = 0; i < num_yamyam_contents; i++)
6618 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6620 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6621 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6623 if (i < MAX_ELEMENT_CONTENTS)
6624 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6628 fieldx = header[6] | (header[7] << 8);
6629 fieldy = header[8] | (header[9] << 8);
6630 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6631 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6633 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6635 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6636 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6638 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6639 level->field[x][y] = getMappedElement_DC(element_dc);
6642 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6643 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6644 level->field[x][y] = EL_PLAYER_1;
6646 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6647 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6648 level->field[x][y] = EL_PLAYER_2;
6650 level->gems_needed = header[18] | (header[19] << 8);
6652 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6653 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6654 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6655 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6656 level->score[SC_NUT] = header[28] | (header[29] << 8);
6657 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6658 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6659 level->score[SC_BUG] = header[34] | (header[35] << 8);
6660 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6661 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6662 level->score[SC_KEY] = header[40] | (header[41] << 8);
6663 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6665 level->time = header[44] | (header[45] << 8);
6667 level->amoeba_speed = header[46] | (header[47] << 8);
6668 level->time_light = header[48] | (header[49] << 8);
6669 level->time_timegate = header[50] | (header[51] << 8);
6670 level->time_wheel = header[52] | (header[53] << 8);
6671 level->time_magic_wall = header[54] | (header[55] << 8);
6672 level->extra_time = header[56] | (header[57] << 8);
6673 level->shield_normal_time = header[58] | (header[59] << 8);
6675 // shield and extra time elements do not have a score
6676 level->score[SC_SHIELD] = 0;
6677 level->extra_time_score = 0;
6679 // set time for normal and deadly shields to the same value
6680 level->shield_deadly_time = level->shield_normal_time;
6682 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6683 // can slip down from flat walls, like normal walls and steel walls
6684 level->em_slippery_gems = TRUE;
6686 // time score is counted for each 10 seconds left in Diamond Caves levels
6687 level->time_score_base = 10;
6690 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6691 struct LevelFileInfo *level_file_info,
6692 boolean level_info_only)
6694 char *filename = level_file_info->filename;
6696 int num_magic_bytes = 8;
6697 char magic_bytes[num_magic_bytes + 1];
6698 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6700 if (!(file = openFile(filename, MODE_READ)))
6702 level->no_valid_file = TRUE;
6704 if (!level_info_only)
6705 Warn("cannot read level '%s' -- using empty level", filename);
6710 // fseek(file, 0x0000, SEEK_SET);
6712 if (level_file_info->packed)
6714 // read "magic bytes" from start of file
6715 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6716 magic_bytes[0] = '\0';
6718 // check "magic bytes" for correct file format
6719 if (!strPrefix(magic_bytes, "DC2"))
6721 level->no_valid_file = TRUE;
6723 Warn("unknown DC level file '%s' -- using empty level", filename);
6728 if (strPrefix(magic_bytes, "DC2Win95") ||
6729 strPrefix(magic_bytes, "DC2Win98"))
6731 int position_first_level = 0x00fa;
6732 int extra_bytes = 4;
6735 // advance file stream to first level inside the level package
6736 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6738 // each block of level data is followed by block of non-level data
6739 num_levels_to_skip *= 2;
6741 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6742 while (num_levels_to_skip >= 0)
6744 // advance file stream to next level inside the level package
6745 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6747 level->no_valid_file = TRUE;
6749 Warn("cannot fseek in file '%s' -- using empty level", filename);
6754 // skip apparently unused extra bytes following each level
6755 ReadUnusedBytesFromFile(file, extra_bytes);
6757 // read size of next level in level package
6758 skip_bytes = getFile32BitLE(file);
6760 num_levels_to_skip--;
6765 level->no_valid_file = TRUE;
6767 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6773 LoadLevelFromFileStream_DC(file, level);
6779 // ----------------------------------------------------------------------------
6780 // functions for loading SB level
6781 // ----------------------------------------------------------------------------
6783 int getMappedElement_SB(int element_ascii, boolean use_ces)
6791 sb_element_mapping[] =
6793 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6794 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6795 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6796 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6797 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6798 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6799 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6800 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6807 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6808 if (element_ascii == sb_element_mapping[i].ascii)
6809 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6811 return EL_UNDEFINED;
6814 static void SetLevelSettings_SB(struct LevelInfo *level)
6818 level->use_step_counter = TRUE;
6821 level->score[SC_TIME_BONUS] = 0;
6822 level->time_score_base = 1;
6823 level->rate_time_over_score = TRUE;
6826 level->auto_exit_sokoban = TRUE;
6829 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6830 struct LevelFileInfo *level_file_info,
6831 boolean level_info_only)
6833 char *filename = level_file_info->filename;
6834 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6835 char last_comment[MAX_LINE_LEN];
6836 char level_name[MAX_LINE_LEN];
6839 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6840 boolean read_continued_line = FALSE;
6841 boolean reading_playfield = FALSE;
6842 boolean got_valid_playfield_line = FALSE;
6843 boolean invalid_playfield_char = FALSE;
6844 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6845 int file_level_nr = 0;
6846 int x = 0, y = 0; // initialized to make compilers happy
6848 last_comment[0] = '\0';
6849 level_name[0] = '\0';
6851 if (!(file = openFile(filename, MODE_READ)))
6853 level->no_valid_file = TRUE;
6855 if (!level_info_only)
6856 Warn("cannot read level '%s' -- using empty level", filename);
6861 while (!checkEndOfFile(file))
6863 // level successfully read, but next level may follow here
6864 if (!got_valid_playfield_line && reading_playfield)
6866 // read playfield from single level file -- skip remaining file
6867 if (!level_file_info->packed)
6870 if (file_level_nr >= num_levels_to_skip)
6875 last_comment[0] = '\0';
6876 level_name[0] = '\0';
6878 reading_playfield = FALSE;
6881 got_valid_playfield_line = FALSE;
6883 // read next line of input file
6884 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6887 // cut trailing line break (this can be newline and/or carriage return)
6888 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6889 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6892 // copy raw input line for later use (mainly debugging output)
6893 strcpy(line_raw, line);
6895 if (read_continued_line)
6897 // append new line to existing line, if there is enough space
6898 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6899 strcat(previous_line, line_ptr);
6901 strcpy(line, previous_line); // copy storage buffer to line
6903 read_continued_line = FALSE;
6906 // if the last character is '\', continue at next line
6907 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6909 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6910 strcpy(previous_line, line); // copy line to storage buffer
6912 read_continued_line = TRUE;
6918 if (line[0] == '\0')
6921 // extract comment text from comment line
6924 for (line_ptr = line; *line_ptr; line_ptr++)
6925 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6928 strcpy(last_comment, line_ptr);
6933 // extract level title text from line containing level title
6934 if (line[0] == '\'')
6936 strcpy(level_name, &line[1]);
6938 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6939 level_name[strlen(level_name) - 1] = '\0';
6944 // skip lines containing only spaces (or empty lines)
6945 for (line_ptr = line; *line_ptr; line_ptr++)
6946 if (*line_ptr != ' ')
6948 if (*line_ptr == '\0')
6951 // at this point, we have found a line containing part of a playfield
6953 got_valid_playfield_line = TRUE;
6955 if (!reading_playfield)
6957 reading_playfield = TRUE;
6958 invalid_playfield_char = FALSE;
6960 for (x = 0; x < MAX_LEV_FIELDX; x++)
6961 for (y = 0; y < MAX_LEV_FIELDY; y++)
6962 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6967 // start with topmost tile row
6971 // skip playfield line if larger row than allowed
6972 if (y >= MAX_LEV_FIELDY)
6975 // start with leftmost tile column
6978 // read playfield elements from line
6979 for (line_ptr = line; *line_ptr; line_ptr++)
6981 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6983 // stop parsing playfield line if larger column than allowed
6984 if (x >= MAX_LEV_FIELDX)
6987 if (mapped_sb_element == EL_UNDEFINED)
6989 invalid_playfield_char = TRUE;
6994 level->field[x][y] = mapped_sb_element;
6996 // continue with next tile column
6999 level->fieldx = MAX(x, level->fieldx);
7002 if (invalid_playfield_char)
7004 // if first playfield line, treat invalid lines as comment lines
7006 reading_playfield = FALSE;
7011 // continue with next tile row
7019 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7020 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7022 if (!reading_playfield)
7024 level->no_valid_file = TRUE;
7026 Warn("cannot read level '%s' -- using empty level", filename);
7031 if (*level_name != '\0')
7033 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7034 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7036 else if (*last_comment != '\0')
7038 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7039 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7043 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7046 // set all empty fields beyond the border walls to invisible steel wall
7047 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7049 if ((x == 0 || x == level->fieldx - 1 ||
7050 y == 0 || y == level->fieldy - 1) &&
7051 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7052 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7053 level->field, level->fieldx, level->fieldy);
7056 // set special level settings for Sokoban levels
7057 SetLevelSettings_SB(level);
7059 if (load_xsb_to_ces)
7061 // special global settings can now be set in level template
7062 level->use_custom_template = TRUE;
7067 // -------------------------------------------------------------------------
7068 // functions for handling native levels
7069 // -------------------------------------------------------------------------
7071 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7072 struct LevelFileInfo *level_file_info,
7073 boolean level_info_only)
7077 // determine position of requested level inside level package
7078 if (level_file_info->packed)
7079 pos = level_file_info->nr - leveldir_current->first_level;
7081 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7082 level->no_valid_file = TRUE;
7085 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7086 struct LevelFileInfo *level_file_info,
7087 boolean level_info_only)
7089 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7090 level->no_valid_file = TRUE;
7093 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7094 struct LevelFileInfo *level_file_info,
7095 boolean level_info_only)
7099 // determine position of requested level inside level package
7100 if (level_file_info->packed)
7101 pos = level_file_info->nr - leveldir_current->first_level;
7103 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7104 level->no_valid_file = TRUE;
7107 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7108 struct LevelFileInfo *level_file_info,
7109 boolean level_info_only)
7111 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7112 level->no_valid_file = TRUE;
7115 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7117 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7118 CopyNativeLevel_RND_to_BD(level);
7119 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7120 CopyNativeLevel_RND_to_EM(level);
7121 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7122 CopyNativeLevel_RND_to_SP(level);
7123 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7124 CopyNativeLevel_RND_to_MM(level);
7127 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7129 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7130 CopyNativeLevel_BD_to_RND(level);
7131 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7132 CopyNativeLevel_EM_to_RND(level);
7133 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7134 CopyNativeLevel_SP_to_RND(level);
7135 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7136 CopyNativeLevel_MM_to_RND(level);
7139 void SaveNativeLevel(struct LevelInfo *level)
7141 // saving native level files only supported for some game engines
7142 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7143 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7146 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7147 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7148 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7149 char *filename = getLevelFilenameFromBasename(basename);
7151 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7154 boolean success = FALSE;
7156 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7158 CopyNativeLevel_RND_to_BD(level);
7159 // CopyNativeTape_RND_to_BD(level);
7161 success = SaveNativeLevel_BD(filename);
7163 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7165 CopyNativeLevel_RND_to_SP(level);
7166 CopyNativeTape_RND_to_SP(level);
7168 success = SaveNativeLevel_SP(filename);
7172 Request("Native level file saved!", REQ_CONFIRM);
7174 Request("Failed to save native level file!", REQ_CONFIRM);
7178 // ----------------------------------------------------------------------------
7179 // functions for loading generic level
7180 // ----------------------------------------------------------------------------
7182 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7183 struct LevelFileInfo *level_file_info,
7184 boolean level_info_only)
7186 // always start with reliable default values
7187 setLevelInfoToDefaults(level, level_info_only, TRUE);
7189 switch (level_file_info->type)
7191 case LEVEL_FILE_TYPE_RND:
7192 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7195 case LEVEL_FILE_TYPE_BD:
7196 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7197 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7200 case LEVEL_FILE_TYPE_EM:
7201 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7202 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7205 case LEVEL_FILE_TYPE_SP:
7206 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7207 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7210 case LEVEL_FILE_TYPE_MM:
7211 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7212 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7215 case LEVEL_FILE_TYPE_DC:
7216 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7219 case LEVEL_FILE_TYPE_SB:
7220 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7224 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7228 // if level file is invalid, restore level structure to default values
7229 if (level->no_valid_file)
7230 setLevelInfoToDefaults(level, level_info_only, FALSE);
7232 if (check_special_flags("use_native_bd_game_engine"))
7233 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7235 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7236 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7238 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7239 CopyNativeLevel_Native_to_RND(level);
7242 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7244 static struct LevelFileInfo level_file_info;
7246 // always start with reliable default values
7247 setFileInfoToDefaults(&level_file_info);
7249 level_file_info.nr = 0; // unknown level number
7250 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7252 setString(&level_file_info.filename, filename);
7254 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7257 static void LoadLevel_InitVersion(struct LevelInfo *level)
7261 if (leveldir_current == NULL) // only when dumping level
7264 // all engine modifications also valid for levels which use latest engine
7265 if (level->game_version < VERSION_IDENT(3,2,0,5))
7267 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7268 level->time_score_base = 10;
7271 if (leveldir_current->latest_engine)
7273 // ---------- use latest game engine --------------------------------------
7275 /* For all levels which are forced to use the latest game engine version
7276 (normally all but user contributed, private and undefined levels), set
7277 the game engine version to the actual version; this allows for actual
7278 corrections in the game engine to take effect for existing, converted
7279 levels (from "classic" or other existing games) to make the emulation
7280 of the corresponding game more accurate, while (hopefully) not breaking
7281 existing levels created from other players. */
7283 level->game_version = GAME_VERSION_ACTUAL;
7285 /* Set special EM style gems behaviour: EM style gems slip down from
7286 normal, steel and growing wall. As this is a more fundamental change,
7287 it seems better to set the default behaviour to "off" (as it is more
7288 natural) and make it configurable in the level editor (as a property
7289 of gem style elements). Already existing converted levels (neither
7290 private nor contributed levels) are changed to the new behaviour. */
7292 if (level->file_version < FILE_VERSION_2_0)
7293 level->em_slippery_gems = TRUE;
7298 // ---------- use game engine the level was created with --------------------
7300 /* For all levels which are not forced to use the latest game engine
7301 version (normally user contributed, private and undefined levels),
7302 use the version of the game engine the levels were created for.
7304 Since 2.0.1, the game engine version is now directly stored
7305 in the level file (chunk "VERS"), so there is no need anymore
7306 to set the game version from the file version (except for old,
7307 pre-2.0 levels, where the game version is still taken from the
7308 file format version used to store the level -- see above). */
7310 // player was faster than enemies in 1.0.0 and before
7311 if (level->file_version == FILE_VERSION_1_0)
7312 for (i = 0; i < MAX_PLAYERS; i++)
7313 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7315 // default behaviour for EM style gems was "slippery" only in 2.0.1
7316 if (level->game_version == VERSION_IDENT(2,0,1,0))
7317 level->em_slippery_gems = TRUE;
7319 // springs could be pushed over pits before (pre-release version) 2.2.0
7320 if (level->game_version < VERSION_IDENT(2,2,0,0))
7321 level->use_spring_bug = TRUE;
7323 if (level->game_version < VERSION_IDENT(3,2,0,5))
7325 // time orb caused limited time in endless time levels before 3.2.0-5
7326 level->use_time_orb_bug = TRUE;
7328 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7329 level->block_snap_field = FALSE;
7331 // extra time score was same value as time left score before 3.2.0-5
7332 level->extra_time_score = level->score[SC_TIME_BONUS];
7335 if (level->game_version < VERSION_IDENT(3,2,0,7))
7337 // default behaviour for snapping was "not continuous" before 3.2.0-7
7338 level->continuous_snapping = FALSE;
7341 // only few elements were able to actively move into acid before 3.1.0
7342 // trigger settings did not exist before 3.1.0; set to default "any"
7343 if (level->game_version < VERSION_IDENT(3,1,0,0))
7345 // correct "can move into acid" settings (all zero in old levels)
7347 level->can_move_into_acid_bits = 0; // nothing can move into acid
7348 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7350 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7351 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7352 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7353 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7355 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7356 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7358 // correct trigger settings (stored as zero == "none" in old levels)
7360 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7362 int element = EL_CUSTOM_START + i;
7363 struct ElementInfo *ei = &element_info[element];
7365 for (j = 0; j < ei->num_change_pages; j++)
7367 struct ElementChangeInfo *change = &ei->change_page[j];
7369 change->trigger_player = CH_PLAYER_ANY;
7370 change->trigger_page = CH_PAGE_ANY;
7375 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7377 int element = EL_CUSTOM_256;
7378 struct ElementInfo *ei = &element_info[element];
7379 struct ElementChangeInfo *change = &ei->change_page[0];
7381 /* This is needed to fix a problem that was caused by a bugfix in function
7382 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7383 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7384 not replace walkable elements, but instead just placed the player on it,
7385 without placing the Sokoban field under the player). Unfortunately, this
7386 breaks "Snake Bite" style levels when the snake is halfway through a door
7387 that just closes (the snake head is still alive and can be moved in this
7388 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7389 player (without Sokoban element) which then gets killed as designed). */
7391 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7392 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7393 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7394 change->target_element = EL_PLAYER_1;
7397 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7398 if (level->game_version < VERSION_IDENT(3,2,5,0))
7400 /* This is needed to fix a problem that was caused by a bugfix in function
7401 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7402 corrects the behaviour when a custom element changes to another custom
7403 element with a higher element number that has change actions defined.
7404 Normally, only one change per frame is allowed for custom elements.
7405 Therefore, it is checked if a custom element already changed in the
7406 current frame; if it did, subsequent changes are suppressed.
7407 Unfortunately, this is only checked for element changes, but not for
7408 change actions, which are still executed. As the function above loops
7409 through all custom elements from lower to higher, an element change
7410 resulting in a lower CE number won't be checked again, while a target
7411 element with a higher number will also be checked, and potential change
7412 actions will get executed for this CE, too (which is wrong), while
7413 further changes are ignored (which is correct). As this bugfix breaks
7414 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7415 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7416 behaviour for existing levels and tapes that make use of this bug */
7418 level->use_action_after_change_bug = TRUE;
7421 // not centering level after relocating player was default only in 3.2.3
7422 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7423 level->shifted_relocation = TRUE;
7425 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7426 if (level->game_version < VERSION_IDENT(3,2,6,0))
7427 level->em_explodes_by_fire = TRUE;
7429 // levels were solved by the first player entering an exit up to 4.1.0.0
7430 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7431 level->solved_by_one_player = TRUE;
7433 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7434 if (level->game_version < VERSION_IDENT(4,1,1,1))
7435 level->use_life_bugs = TRUE;
7437 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7438 if (level->game_version < VERSION_IDENT(4,1,1,1))
7439 level->sb_objects_needed = FALSE;
7441 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7442 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7443 level->finish_dig_collect = FALSE;
7445 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7446 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7447 level->keep_walkable_ce = TRUE;
7450 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7452 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7455 // check if this level is (not) a Sokoban level
7456 for (y = 0; y < level->fieldy; y++)
7457 for (x = 0; x < level->fieldx; x++)
7458 if (!IS_SB_ELEMENT(Tile[x][y]))
7459 is_sokoban_level = FALSE;
7461 if (is_sokoban_level)
7463 // set special level settings for Sokoban levels
7464 SetLevelSettings_SB(level);
7468 static void LoadLevel_InitSettings(struct LevelInfo *level)
7470 // adjust level settings for (non-native) Sokoban-style levels
7471 LoadLevel_InitSettings_SB(level);
7473 // rename levels with title "nameless level" or if renaming is forced
7474 if (leveldir_current->empty_level_name != NULL &&
7475 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7476 leveldir_current->force_level_name))
7477 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7478 leveldir_current->empty_level_name, level_nr);
7481 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7485 // map elements that have changed in newer versions
7486 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7487 level->game_version);
7488 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7489 for (x = 0; x < 3; x++)
7490 for (y = 0; y < 3; y++)
7491 level->yamyam_content[i].e[x][y] =
7492 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7493 level->game_version);
7497 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7501 // map custom element change events that have changed in newer versions
7502 // (these following values were accidentally changed in version 3.0.1)
7503 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7504 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7506 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7508 int element = EL_CUSTOM_START + i;
7510 // order of checking and copying events to be mapped is important
7511 // (do not change the start and end value -- they are constant)
7512 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7514 if (HAS_CHANGE_EVENT(element, j - 2))
7516 SET_CHANGE_EVENT(element, j - 2, FALSE);
7517 SET_CHANGE_EVENT(element, j, TRUE);
7521 // order of checking and copying events to be mapped is important
7522 // (do not change the start and end value -- they are constant)
7523 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7525 if (HAS_CHANGE_EVENT(element, j - 1))
7527 SET_CHANGE_EVENT(element, j - 1, FALSE);
7528 SET_CHANGE_EVENT(element, j, TRUE);
7534 // initialize "can_change" field for old levels with only one change page
7535 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7537 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7539 int element = EL_CUSTOM_START + i;
7541 if (CAN_CHANGE(element))
7542 element_info[element].change->can_change = TRUE;
7546 // correct custom element values (for old levels without these options)
7547 if (level->game_version < VERSION_IDENT(3,1,1,0))
7549 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7551 int element = EL_CUSTOM_START + i;
7552 struct ElementInfo *ei = &element_info[element];
7554 if (ei->access_direction == MV_NO_DIRECTION)
7555 ei->access_direction = MV_ALL_DIRECTIONS;
7559 // correct custom element values (fix invalid values for all versions)
7562 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7564 int element = EL_CUSTOM_START + i;
7565 struct ElementInfo *ei = &element_info[element];
7567 for (j = 0; j < ei->num_change_pages; j++)
7569 struct ElementChangeInfo *change = &ei->change_page[j];
7571 if (change->trigger_player == CH_PLAYER_NONE)
7572 change->trigger_player = CH_PLAYER_ANY;
7574 if (change->trigger_side == CH_SIDE_NONE)
7575 change->trigger_side = CH_SIDE_ANY;
7580 // initialize "can_explode" field for old levels which did not store this
7581 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7582 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7584 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7586 int element = EL_CUSTOM_START + i;
7588 if (EXPLODES_1X1_OLD(element))
7589 element_info[element].explosion_type = EXPLODES_1X1;
7591 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7592 EXPLODES_SMASHED(element) ||
7593 EXPLODES_IMPACT(element)));
7597 // correct previously hard-coded move delay values for maze runner style
7598 if (level->game_version < VERSION_IDENT(3,1,1,0))
7600 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7602 int element = EL_CUSTOM_START + i;
7604 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7606 // previously hard-coded and therefore ignored
7607 element_info[element].move_delay_fixed = 9;
7608 element_info[element].move_delay_random = 0;
7613 // set some other uninitialized values of custom elements in older levels
7614 if (level->game_version < VERSION_IDENT(3,1,0,0))
7616 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7618 int element = EL_CUSTOM_START + i;
7620 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7622 element_info[element].explosion_delay = 17;
7623 element_info[element].ignition_delay = 8;
7627 // set mouse click change events to work for left/middle/right mouse button
7628 if (level->game_version < VERSION_IDENT(4,2,3,0))
7630 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7632 int element = EL_CUSTOM_START + i;
7633 struct ElementInfo *ei = &element_info[element];
7635 for (j = 0; j < ei->num_change_pages; j++)
7637 struct ElementChangeInfo *change = &ei->change_page[j];
7639 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7640 change->has_event[CE_PRESSED_BY_MOUSE] ||
7641 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7642 change->has_event[CE_MOUSE_PRESSED_ON_X])
7643 change->trigger_side = CH_SIDE_ANY;
7649 static void LoadLevel_InitElements(struct LevelInfo *level)
7651 LoadLevel_InitStandardElements(level);
7653 if (level->file_has_custom_elements)
7654 LoadLevel_InitCustomElements(level);
7656 // initialize element properties for level editor etc.
7657 InitElementPropertiesEngine(level->game_version);
7658 InitElementPropertiesGfxElement();
7661 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7665 // map elements that have changed in newer versions
7666 for (y = 0; y < level->fieldy; y++)
7667 for (x = 0; x < level->fieldx; x++)
7668 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7669 level->game_version);
7671 // clear unused playfield data (nicer if level gets resized in editor)
7672 for (x = 0; x < MAX_LEV_FIELDX; x++)
7673 for (y = 0; y < MAX_LEV_FIELDY; y++)
7674 if (x >= level->fieldx || y >= level->fieldy)
7675 level->field[x][y] = EL_EMPTY;
7677 // copy elements to runtime playfield array
7678 for (x = 0; x < MAX_LEV_FIELDX; x++)
7679 for (y = 0; y < MAX_LEV_FIELDY; y++)
7680 Tile[x][y] = level->field[x][y];
7682 // initialize level size variables for faster access
7683 lev_fieldx = level->fieldx;
7684 lev_fieldy = level->fieldy;
7686 // determine border element for this level
7687 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7688 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7693 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7695 struct LevelFileInfo *level_file_info = &level->file_info;
7697 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7698 CopyNativeLevel_RND_to_Native(level);
7701 static void LoadLevelTemplate_LoadAndInit(void)
7703 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7705 LoadLevel_InitVersion(&level_template);
7706 LoadLevel_InitElements(&level_template);
7707 LoadLevel_InitSettings(&level_template);
7709 ActivateLevelTemplate();
7712 void LoadLevelTemplate(int nr)
7714 if (!fileExists(getGlobalLevelTemplateFilename()))
7716 Warn("no level template found for this level");
7721 setLevelFileInfo(&level_template.file_info, nr);
7723 LoadLevelTemplate_LoadAndInit();
7726 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7728 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7730 LoadLevelTemplate_LoadAndInit();
7733 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7735 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7737 if (level.use_custom_template)
7739 if (network_level != NULL)
7740 LoadNetworkLevelTemplate(network_level);
7742 LoadLevelTemplate(-1);
7745 LoadLevel_InitVersion(&level);
7746 LoadLevel_InitElements(&level);
7747 LoadLevel_InitPlayfield(&level);
7748 LoadLevel_InitSettings(&level);
7750 LoadLevel_InitNativeEngines(&level);
7753 void LoadLevel(int nr)
7755 SetLevelSetInfo(leveldir_current->identifier, nr);
7757 setLevelFileInfo(&level.file_info, nr);
7759 LoadLevel_LoadAndInit(NULL);
7762 void LoadLevelInfoOnly(int nr)
7764 setLevelFileInfo(&level.file_info, nr);
7766 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7769 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7771 SetLevelSetInfo(network_level->leveldir_identifier,
7772 network_level->file_info.nr);
7774 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7776 LoadLevel_LoadAndInit(network_level);
7779 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7783 chunk_size += putFileVersion(file, level->file_version);
7784 chunk_size += putFileVersion(file, level->game_version);
7789 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7793 chunk_size += putFile16BitBE(file, level->creation_date.year);
7794 chunk_size += putFile8Bit(file, level->creation_date.month);
7795 chunk_size += putFile8Bit(file, level->creation_date.day);
7800 #if ENABLE_HISTORIC_CHUNKS
7801 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7805 putFile8Bit(file, level->fieldx);
7806 putFile8Bit(file, level->fieldy);
7808 putFile16BitBE(file, level->time);
7809 putFile16BitBE(file, level->gems_needed);
7811 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7812 putFile8Bit(file, level->name[i]);
7814 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7815 putFile8Bit(file, level->score[i]);
7817 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7818 for (y = 0; y < 3; y++)
7819 for (x = 0; x < 3; x++)
7820 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7821 level->yamyam_content[i].e[x][y]));
7822 putFile8Bit(file, level->amoeba_speed);
7823 putFile8Bit(file, level->time_magic_wall);
7824 putFile8Bit(file, level->time_wheel);
7825 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7826 level->amoeba_content));
7827 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7828 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7829 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7830 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7832 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7834 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7835 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7836 putFile32BitBE(file, level->can_move_into_acid_bits);
7837 putFile8Bit(file, level->dont_collide_with_bits);
7839 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7840 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7842 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7843 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7844 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7846 putFile8Bit(file, level->game_engine_type);
7848 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7852 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7857 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7858 chunk_size += putFile8Bit(file, level->name[i]);
7863 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7868 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7869 chunk_size += putFile8Bit(file, level->author[i]);
7874 #if ENABLE_HISTORIC_CHUNKS
7875 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7880 for (y = 0; y < level->fieldy; y++)
7881 for (x = 0; x < level->fieldx; x++)
7882 if (level->encoding_16bit_field)
7883 chunk_size += putFile16BitBE(file, level->field[x][y]);
7885 chunk_size += putFile8Bit(file, level->field[x][y]);
7891 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7896 for (y = 0; y < level->fieldy; y++)
7897 for (x = 0; x < level->fieldx; x++)
7898 chunk_size += putFile16BitBE(file, level->field[x][y]);
7903 #if ENABLE_HISTORIC_CHUNKS
7904 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7908 putFile8Bit(file, EL_YAMYAM);
7909 putFile8Bit(file, level->num_yamyam_contents);
7910 putFile8Bit(file, 0);
7911 putFile8Bit(file, 0);
7913 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7914 for (y = 0; y < 3; y++)
7915 for (x = 0; x < 3; x++)
7916 if (level->encoding_16bit_field)
7917 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7919 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7923 #if ENABLE_HISTORIC_CHUNKS
7924 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7927 int num_contents, content_xsize, content_ysize;
7928 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7930 if (element == EL_YAMYAM)
7932 num_contents = level->num_yamyam_contents;
7936 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7937 for (y = 0; y < 3; y++)
7938 for (x = 0; x < 3; x++)
7939 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7941 else if (element == EL_BD_AMOEBA)
7947 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7948 for (y = 0; y < 3; y++)
7949 for (x = 0; x < 3; x++)
7950 content_array[i][x][y] = EL_EMPTY;
7951 content_array[0][0][0] = level->amoeba_content;
7955 // chunk header already written -- write empty chunk data
7956 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7958 Warn("cannot save content for element '%d'", element);
7963 putFile16BitBE(file, element);
7964 putFile8Bit(file, num_contents);
7965 putFile8Bit(file, content_xsize);
7966 putFile8Bit(file, content_ysize);
7968 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7970 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7971 for (y = 0; y < 3; y++)
7972 for (x = 0; x < 3; x++)
7973 putFile16BitBE(file, content_array[i][x][y]);
7977 #if ENABLE_HISTORIC_CHUNKS
7978 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7980 int envelope_nr = element - EL_ENVELOPE_1;
7981 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7985 chunk_size += putFile16BitBE(file, element);
7986 chunk_size += putFile16BitBE(file, envelope_len);
7987 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7988 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7990 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7991 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7993 for (i = 0; i < envelope_len; i++)
7994 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8000 #if ENABLE_HISTORIC_CHUNKS
8001 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8002 int num_changed_custom_elements)
8006 putFile16BitBE(file, num_changed_custom_elements);
8008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8010 int element = EL_CUSTOM_START + i;
8012 struct ElementInfo *ei = &element_info[element];
8014 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8016 if (check < num_changed_custom_elements)
8018 putFile16BitBE(file, element);
8019 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8026 if (check != num_changed_custom_elements) // should not happen
8027 Warn("inconsistent number of custom element properties");
8031 #if ENABLE_HISTORIC_CHUNKS
8032 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8033 int num_changed_custom_elements)
8037 putFile16BitBE(file, num_changed_custom_elements);
8039 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8041 int element = EL_CUSTOM_START + i;
8043 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8045 if (check < num_changed_custom_elements)
8047 putFile16BitBE(file, element);
8048 putFile16BitBE(file, element_info[element].change->target_element);
8055 if (check != num_changed_custom_elements) // should not happen
8056 Warn("inconsistent number of custom target elements");
8060 #if ENABLE_HISTORIC_CHUNKS
8061 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8062 int num_changed_custom_elements)
8064 int i, j, x, y, check = 0;
8066 putFile16BitBE(file, num_changed_custom_elements);
8068 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8070 int element = EL_CUSTOM_START + i;
8071 struct ElementInfo *ei = &element_info[element];
8073 if (ei->modified_settings)
8075 if (check < num_changed_custom_elements)
8077 putFile16BitBE(file, element);
8079 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8080 putFile8Bit(file, ei->description[j]);
8082 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8084 // some free bytes for future properties and padding
8085 WriteUnusedBytesToFile(file, 7);
8087 putFile8Bit(file, ei->use_gfx_element);
8088 putFile16BitBE(file, ei->gfx_element_initial);
8090 putFile8Bit(file, ei->collect_score_initial);
8091 putFile8Bit(file, ei->collect_count_initial);
8093 putFile16BitBE(file, ei->push_delay_fixed);
8094 putFile16BitBE(file, ei->push_delay_random);
8095 putFile16BitBE(file, ei->move_delay_fixed);
8096 putFile16BitBE(file, ei->move_delay_random);
8098 putFile16BitBE(file, ei->move_pattern);
8099 putFile8Bit(file, ei->move_direction_initial);
8100 putFile8Bit(file, ei->move_stepsize);
8102 for (y = 0; y < 3; y++)
8103 for (x = 0; x < 3; x++)
8104 putFile16BitBE(file, ei->content.e[x][y]);
8106 putFile32BitBE(file, ei->change->events);
8108 putFile16BitBE(file, ei->change->target_element);
8110 putFile16BitBE(file, ei->change->delay_fixed);
8111 putFile16BitBE(file, ei->change->delay_random);
8112 putFile16BitBE(file, ei->change->delay_frames);
8114 putFile16BitBE(file, ei->change->initial_trigger_element);
8116 putFile8Bit(file, ei->change->explode);
8117 putFile8Bit(file, ei->change->use_target_content);
8118 putFile8Bit(file, ei->change->only_if_complete);
8119 putFile8Bit(file, ei->change->use_random_replace);
8121 putFile8Bit(file, ei->change->random_percentage);
8122 putFile8Bit(file, ei->change->replace_when);
8124 for (y = 0; y < 3; y++)
8125 for (x = 0; x < 3; x++)
8126 putFile16BitBE(file, ei->change->content.e[x][y]);
8128 putFile8Bit(file, ei->slippery_type);
8130 // some free bytes for future properties and padding
8131 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8138 if (check != num_changed_custom_elements) // should not happen
8139 Warn("inconsistent number of custom element properties");
8143 #if ENABLE_HISTORIC_CHUNKS
8144 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8146 struct ElementInfo *ei = &element_info[element];
8149 // ---------- custom element base property values (96 bytes) ----------------
8151 putFile16BitBE(file, element);
8153 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8154 putFile8Bit(file, ei->description[i]);
8156 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8158 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8160 putFile8Bit(file, ei->num_change_pages);
8162 putFile16BitBE(file, ei->ce_value_fixed_initial);
8163 putFile16BitBE(file, ei->ce_value_random_initial);
8164 putFile8Bit(file, ei->use_last_ce_value);
8166 putFile8Bit(file, ei->use_gfx_element);
8167 putFile16BitBE(file, ei->gfx_element_initial);
8169 putFile8Bit(file, ei->collect_score_initial);
8170 putFile8Bit(file, ei->collect_count_initial);
8172 putFile8Bit(file, ei->drop_delay_fixed);
8173 putFile8Bit(file, ei->push_delay_fixed);
8174 putFile8Bit(file, ei->drop_delay_random);
8175 putFile8Bit(file, ei->push_delay_random);
8176 putFile16BitBE(file, ei->move_delay_fixed);
8177 putFile16BitBE(file, ei->move_delay_random);
8179 // bits 0 - 15 of "move_pattern" ...
8180 putFile16BitBE(file, ei->move_pattern & 0xffff);
8181 putFile8Bit(file, ei->move_direction_initial);
8182 putFile8Bit(file, ei->move_stepsize);
8184 putFile8Bit(file, ei->slippery_type);
8186 for (y = 0; y < 3; y++)
8187 for (x = 0; x < 3; x++)
8188 putFile16BitBE(file, ei->content.e[x][y]);
8190 putFile16BitBE(file, ei->move_enter_element);
8191 putFile16BitBE(file, ei->move_leave_element);
8192 putFile8Bit(file, ei->move_leave_type);
8194 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8195 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8197 putFile8Bit(file, ei->access_direction);
8199 putFile8Bit(file, ei->explosion_delay);
8200 putFile8Bit(file, ei->ignition_delay);
8201 putFile8Bit(file, ei->explosion_type);
8203 // some free bytes for future custom property values and padding
8204 WriteUnusedBytesToFile(file, 1);
8206 // ---------- change page property values (48 bytes) ------------------------
8208 for (i = 0; i < ei->num_change_pages; i++)
8210 struct ElementChangeInfo *change = &ei->change_page[i];
8211 unsigned int event_bits;
8213 // bits 0 - 31 of "has_event[]" ...
8215 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8216 if (change->has_event[j])
8217 event_bits |= (1u << j);
8218 putFile32BitBE(file, event_bits);
8220 putFile16BitBE(file, change->target_element);
8222 putFile16BitBE(file, change->delay_fixed);
8223 putFile16BitBE(file, change->delay_random);
8224 putFile16BitBE(file, change->delay_frames);
8226 putFile16BitBE(file, change->initial_trigger_element);
8228 putFile8Bit(file, change->explode);
8229 putFile8Bit(file, change->use_target_content);
8230 putFile8Bit(file, change->only_if_complete);
8231 putFile8Bit(file, change->use_random_replace);
8233 putFile8Bit(file, change->random_percentage);
8234 putFile8Bit(file, change->replace_when);
8236 for (y = 0; y < 3; y++)
8237 for (x = 0; x < 3; x++)
8238 putFile16BitBE(file, change->target_content.e[x][y]);
8240 putFile8Bit(file, change->can_change);
8242 putFile8Bit(file, change->trigger_side);
8244 putFile8Bit(file, change->trigger_player);
8245 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8246 log_2(change->trigger_page)));
8248 putFile8Bit(file, change->has_action);
8249 putFile8Bit(file, change->action_type);
8250 putFile8Bit(file, change->action_mode);
8251 putFile16BitBE(file, change->action_arg);
8253 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8255 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8256 if (change->has_event[j])
8257 event_bits |= (1u << (j - 32));
8258 putFile8Bit(file, event_bits);
8263 #if ENABLE_HISTORIC_CHUNKS
8264 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8266 struct ElementInfo *ei = &element_info[element];
8267 struct ElementGroupInfo *group = ei->group;
8270 putFile16BitBE(file, element);
8272 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8273 putFile8Bit(file, ei->description[i]);
8275 putFile8Bit(file, group->num_elements);
8277 putFile8Bit(file, ei->use_gfx_element);
8278 putFile16BitBE(file, ei->gfx_element_initial);
8280 putFile8Bit(file, group->choice_mode);
8282 // some free bytes for future values and padding
8283 WriteUnusedBytesToFile(file, 3);
8285 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8286 putFile16BitBE(file, group->element[i]);
8290 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8291 boolean write_element)
8293 int save_type = entry->save_type;
8294 int data_type = entry->data_type;
8295 int conf_type = entry->conf_type;
8296 int byte_mask = conf_type & CONF_MASK_BYTES;
8297 int element = entry->element;
8298 int default_value = entry->default_value;
8300 boolean modified = FALSE;
8302 if (byte_mask != CONF_MASK_MULTI_BYTES)
8304 void *value_ptr = entry->value;
8305 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8308 // check if any settings have been modified before saving them
8309 if (value != default_value)
8312 // do not save if explicitly told or if unmodified default settings
8313 if ((save_type == SAVE_CONF_NEVER) ||
8314 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8318 num_bytes += putFile16BitBE(file, element);
8320 num_bytes += putFile8Bit(file, conf_type);
8321 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8322 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8323 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8326 else if (data_type == TYPE_STRING)
8328 char *default_string = entry->default_string;
8329 char *string = (char *)(entry->value);
8330 int string_length = strlen(string);
8333 // check if any settings have been modified before saving them
8334 if (!strEqual(string, default_string))
8337 // do not save if explicitly told or if unmodified default settings
8338 if ((save_type == SAVE_CONF_NEVER) ||
8339 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8343 num_bytes += putFile16BitBE(file, element);
8345 num_bytes += putFile8Bit(file, conf_type);
8346 num_bytes += putFile16BitBE(file, string_length);
8348 for (i = 0; i < string_length; i++)
8349 num_bytes += putFile8Bit(file, string[i]);
8351 else if (data_type == TYPE_ELEMENT_LIST)
8353 int *element_array = (int *)(entry->value);
8354 int num_elements = *(int *)(entry->num_entities);
8357 // check if any settings have been modified before saving them
8358 for (i = 0; i < num_elements; i++)
8359 if (element_array[i] != default_value)
8362 // do not save if explicitly told or if unmodified default settings
8363 if ((save_type == SAVE_CONF_NEVER) ||
8364 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8368 num_bytes += putFile16BitBE(file, element);
8370 num_bytes += putFile8Bit(file, conf_type);
8371 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8373 for (i = 0; i < num_elements; i++)
8374 num_bytes += putFile16BitBE(file, element_array[i]);
8376 else if (data_type == TYPE_CONTENT_LIST)
8378 struct Content *content = (struct Content *)(entry->value);
8379 int num_contents = *(int *)(entry->num_entities);
8382 // check if any settings have been modified before saving them
8383 for (i = 0; i < num_contents; i++)
8384 for (y = 0; y < 3; y++)
8385 for (x = 0; x < 3; x++)
8386 if (content[i].e[x][y] != 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_contents * CONF_CONTENT_NUM_BYTES);
8400 for (i = 0; i < num_contents; i++)
8401 for (y = 0; y < 3; y++)
8402 for (x = 0; x < 3; x++)
8403 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8409 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8414 li = *level; // copy level data into temporary buffer
8416 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8417 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8422 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8427 li = *level; // copy level data into temporary buffer
8429 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8430 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8435 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8437 int envelope_nr = element - EL_ENVELOPE_1;
8441 chunk_size += putFile16BitBE(file, element);
8443 // copy envelope data into temporary buffer
8444 xx_envelope = level->envelope[envelope_nr];
8446 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8447 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8452 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8454 struct ElementInfo *ei = &element_info[element];
8458 chunk_size += putFile16BitBE(file, element);
8460 xx_ei = *ei; // copy element data into temporary buffer
8462 // set default description string for this specific element
8463 strcpy(xx_default_description, getDefaultElementDescription(ei));
8465 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8466 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8468 for (i = 0; i < ei->num_change_pages; i++)
8470 struct ElementChangeInfo *change = &ei->change_page[i];
8472 xx_current_change_page = i;
8474 xx_change = *change; // copy change data into temporary buffer
8477 setEventBitsFromEventFlags(change);
8479 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8480 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8487 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8489 struct ElementInfo *ei = &element_info[element];
8490 struct ElementGroupInfo *group = ei->group;
8494 chunk_size += putFile16BitBE(file, element);
8496 xx_ei = *ei; // copy element data into temporary buffer
8497 xx_group = *group; // copy group data into temporary buffer
8499 // set default description string for this specific element
8500 strcpy(xx_default_description, getDefaultElementDescription(ei));
8502 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8503 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8508 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8510 struct ElementInfo *ei = &element_info[element];
8514 chunk_size += putFile16BitBE(file, element);
8516 xx_ei = *ei; // copy element data into temporary buffer
8518 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8519 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8524 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8525 boolean save_as_template)
8531 if (!(file = fopen(filename, MODE_WRITE)))
8533 Warn("cannot save level file '%s'", filename);
8538 level->file_version = FILE_VERSION_ACTUAL;
8539 level->game_version = GAME_VERSION_ACTUAL;
8541 level->creation_date = getCurrentDate();
8543 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8544 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8546 chunk_size = SaveLevel_VERS(NULL, level);
8547 putFileChunkBE(file, "VERS", chunk_size);
8548 SaveLevel_VERS(file, level);
8550 chunk_size = SaveLevel_DATE(NULL, level);
8551 putFileChunkBE(file, "DATE", chunk_size);
8552 SaveLevel_DATE(file, level);
8554 chunk_size = SaveLevel_NAME(NULL, level);
8555 putFileChunkBE(file, "NAME", chunk_size);
8556 SaveLevel_NAME(file, level);
8558 chunk_size = SaveLevel_AUTH(NULL, level);
8559 putFileChunkBE(file, "AUTH", chunk_size);
8560 SaveLevel_AUTH(file, level);
8562 chunk_size = SaveLevel_INFO(NULL, level);
8563 putFileChunkBE(file, "INFO", chunk_size);
8564 SaveLevel_INFO(file, level);
8566 chunk_size = SaveLevel_BODY(NULL, level);
8567 putFileChunkBE(file, "BODY", chunk_size);
8568 SaveLevel_BODY(file, level);
8570 chunk_size = SaveLevel_ELEM(NULL, level);
8571 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8573 putFileChunkBE(file, "ELEM", chunk_size);
8574 SaveLevel_ELEM(file, level);
8577 for (i = 0; i < NUM_ENVELOPES; i++)
8579 int element = EL_ENVELOPE_1 + i;
8581 chunk_size = SaveLevel_NOTE(NULL, level, element);
8582 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8584 putFileChunkBE(file, "NOTE", chunk_size);
8585 SaveLevel_NOTE(file, level, element);
8589 // if not using template level, check for non-default custom/group elements
8590 if (!level->use_custom_template || save_as_template)
8592 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8594 int element = EL_CUSTOM_START + i;
8596 chunk_size = SaveLevel_CUSX(NULL, level, element);
8597 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8599 putFileChunkBE(file, "CUSX", chunk_size);
8600 SaveLevel_CUSX(file, level, element);
8604 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8606 int element = EL_GROUP_START + i;
8608 chunk_size = SaveLevel_GRPX(NULL, level, element);
8609 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8611 putFileChunkBE(file, "GRPX", chunk_size);
8612 SaveLevel_GRPX(file, level, element);
8616 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8618 int element = GET_EMPTY_ELEMENT(i);
8620 chunk_size = SaveLevel_EMPX(NULL, level, element);
8621 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8623 putFileChunkBE(file, "EMPX", chunk_size);
8624 SaveLevel_EMPX(file, level, element);
8631 SetFilePermissions(filename, PERMS_PRIVATE);
8634 void SaveLevel(int nr)
8636 char *filename = getDefaultLevelFilename(nr);
8638 SaveLevelFromFilename(&level, filename, FALSE);
8641 void SaveLevelTemplate(void)
8643 char *filename = getLocalLevelTemplateFilename();
8645 SaveLevelFromFilename(&level, filename, TRUE);
8648 boolean SaveLevelChecked(int nr)
8650 char *filename = getDefaultLevelFilename(nr);
8651 boolean new_level = !fileExists(filename);
8652 boolean level_saved = FALSE;
8654 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8659 Request("Level saved!", REQ_CONFIRM);
8667 void DumpLevel(struct LevelInfo *level)
8669 if (level->no_level_file || level->no_valid_file)
8671 Warn("cannot dump -- no valid level file found");
8677 Print("Level xxx (file version %08d, game version %08d)\n",
8678 level->file_version, level->game_version);
8681 Print("Level author: '%s'\n", level->author);
8682 Print("Level title: '%s'\n", level->name);
8684 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8686 Print("Level time: %d seconds\n", level->time);
8687 Print("Gems needed: %d\n", level->gems_needed);
8689 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8690 Print("Time for wheel: %d seconds\n", level->time_wheel);
8691 Print("Time for light: %d seconds\n", level->time_light);
8692 Print("Time for timegate: %d seconds\n", level->time_timegate);
8694 Print("Amoeba speed: %d\n", level->amoeba_speed);
8697 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8698 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8699 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8700 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8701 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8702 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8708 for (i = 0; i < NUM_ENVELOPES; i++)
8710 char *text = level->envelope[i].text;
8711 int text_len = strlen(text);
8712 boolean has_text = FALSE;
8714 for (j = 0; j < text_len; j++)
8715 if (text[j] != ' ' && text[j] != '\n')
8721 Print("Envelope %d:\n'%s'\n", i + 1, text);
8729 void DumpLevels(void)
8731 static LevelDirTree *dumplevel_leveldir = NULL;
8733 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8734 global.dumplevel_leveldir);
8736 if (dumplevel_leveldir == NULL)
8737 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8739 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8740 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8741 Fail("no such level number: %d", global.dumplevel_level_nr);
8743 leveldir_current = dumplevel_leveldir;
8745 LoadLevel(global.dumplevel_level_nr);
8752 // ============================================================================
8753 // tape file functions
8754 // ============================================================================
8756 static void setTapeInfoToDefaults(void)
8760 // always start with reliable default values (empty tape)
8763 // default values (also for pre-1.2 tapes) with only the first player
8764 tape.player_participates[0] = TRUE;
8765 for (i = 1; i < MAX_PLAYERS; i++)
8766 tape.player_participates[i] = FALSE;
8768 // at least one (default: the first) player participates in every tape
8769 tape.num_participating_players = 1;
8771 tape.property_bits = TAPE_PROPERTY_NONE;
8773 tape.level_nr = level_nr;
8775 tape.changed = FALSE;
8776 tape.solved = FALSE;
8778 tape.recording = FALSE;
8779 tape.playing = FALSE;
8780 tape.pausing = FALSE;
8782 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8783 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8785 tape.no_info_chunk = TRUE;
8786 tape.no_valid_file = FALSE;
8789 static int getTapePosSize(struct TapeInfo *tape)
8791 int tape_pos_size = 0;
8793 if (tape->use_key_actions)
8794 tape_pos_size += tape->num_participating_players;
8796 if (tape->use_mouse_actions)
8797 tape_pos_size += 3; // x and y position and mouse button mask
8799 tape_pos_size += 1; // tape action delay value
8801 return tape_pos_size;
8804 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8806 tape->use_key_actions = FALSE;
8807 tape->use_mouse_actions = FALSE;
8809 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8810 tape->use_key_actions = TRUE;
8812 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8813 tape->use_mouse_actions = TRUE;
8816 static int getTapeActionValue(struct TapeInfo *tape)
8818 return (tape->use_key_actions &&
8819 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8820 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8821 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8822 TAPE_ACTIONS_DEFAULT);
8825 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8827 tape->file_version = getFileVersion(file);
8828 tape->game_version = getFileVersion(file);
8833 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8837 tape->random_seed = getFile32BitBE(file);
8838 tape->date = getFile32BitBE(file);
8839 tape->length = getFile32BitBE(file);
8841 // read header fields that are new since version 1.2
8842 if (tape->file_version >= FILE_VERSION_1_2)
8844 byte store_participating_players = getFile8Bit(file);
8847 // since version 1.2, tapes store which players participate in the tape
8848 tape->num_participating_players = 0;
8849 for (i = 0; i < MAX_PLAYERS; i++)
8851 tape->player_participates[i] = FALSE;
8853 if (store_participating_players & (1 << i))
8855 tape->player_participates[i] = TRUE;
8856 tape->num_participating_players++;
8860 setTapeActionFlags(tape, getFile8Bit(file));
8862 tape->property_bits = getFile8Bit(file);
8863 tape->solved = getFile8Bit(file);
8865 engine_version = getFileVersion(file);
8866 if (engine_version > 0)
8867 tape->engine_version = engine_version;
8869 tape->engine_version = tape->game_version;
8875 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8877 tape->scr_fieldx = getFile8Bit(file);
8878 tape->scr_fieldy = getFile8Bit(file);
8883 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8885 char *level_identifier = NULL;
8886 int level_identifier_size;
8889 tape->no_info_chunk = FALSE;
8891 level_identifier_size = getFile16BitBE(file);
8893 level_identifier = checked_malloc(level_identifier_size);
8895 for (i = 0; i < level_identifier_size; i++)
8896 level_identifier[i] = getFile8Bit(file);
8898 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8899 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8901 checked_free(level_identifier);
8903 tape->level_nr = getFile16BitBE(file);
8905 chunk_size = 2 + level_identifier_size + 2;
8910 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8913 int tape_pos_size = getTapePosSize(tape);
8914 int chunk_size_expected = tape_pos_size * tape->length;
8916 if (chunk_size_expected != chunk_size)
8918 ReadUnusedBytesFromFile(file, chunk_size);
8919 return chunk_size_expected;
8922 for (i = 0; i < tape->length; i++)
8924 if (i >= MAX_TAPE_LEN)
8926 Warn("tape truncated -- size exceeds maximum tape size %d",
8929 // tape too large; read and ignore remaining tape data from this chunk
8930 for (;i < tape->length; i++)
8931 ReadUnusedBytesFromFile(file, tape_pos_size);
8936 if (tape->use_key_actions)
8938 for (j = 0; j < MAX_PLAYERS; j++)
8940 tape->pos[i].action[j] = MV_NONE;
8942 if (tape->player_participates[j])
8943 tape->pos[i].action[j] = getFile8Bit(file);
8947 if (tape->use_mouse_actions)
8949 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8950 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8951 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8954 tape->pos[i].delay = getFile8Bit(file);
8956 if (tape->file_version == FILE_VERSION_1_0)
8958 // eliminate possible diagonal moves in old tapes
8959 // this is only for backward compatibility
8961 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8962 byte action = tape->pos[i].action[0];
8963 int k, num_moves = 0;
8965 for (k = 0; k < 4; k++)
8967 if (action & joy_dir[k])
8969 tape->pos[i + num_moves].action[0] = joy_dir[k];
8971 tape->pos[i + num_moves].delay = 0;
8980 tape->length += num_moves;
8983 else if (tape->file_version < FILE_VERSION_2_0)
8985 // convert pre-2.0 tapes to new tape format
8987 if (tape->pos[i].delay > 1)
8990 tape->pos[i + 1] = tape->pos[i];
8991 tape->pos[i + 1].delay = 1;
8994 for (j = 0; j < MAX_PLAYERS; j++)
8995 tape->pos[i].action[j] = MV_NONE;
8996 tape->pos[i].delay--;
9003 if (checkEndOfFile(file))
9007 if (i != tape->length)
9008 chunk_size = tape_pos_size * i;
9013 static void LoadTape_SokobanSolution(char *filename)
9016 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9018 if (!(file = openFile(filename, MODE_READ)))
9020 tape.no_valid_file = TRUE;
9025 while (!checkEndOfFile(file))
9027 unsigned char c = getByteFromFile(file);
9029 if (checkEndOfFile(file))
9036 tape.pos[tape.length].action[0] = MV_UP;
9037 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9043 tape.pos[tape.length].action[0] = MV_DOWN;
9044 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9050 tape.pos[tape.length].action[0] = MV_LEFT;
9051 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9057 tape.pos[tape.length].action[0] = MV_RIGHT;
9058 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9066 // ignore white-space characters
9070 tape.no_valid_file = TRUE;
9072 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9080 if (tape.no_valid_file)
9083 tape.length_frames = GetTapeLengthFrames();
9084 tape.length_seconds = GetTapeLengthSeconds();
9087 void LoadTapeFromFilename(char *filename)
9089 char cookie[MAX_LINE_LEN];
9090 char chunk_name[CHUNK_ID_LEN + 1];
9094 // always start with reliable default values
9095 setTapeInfoToDefaults();
9097 if (strSuffix(filename, ".sln"))
9099 LoadTape_SokobanSolution(filename);
9104 if (!(file = openFile(filename, MODE_READ)))
9106 tape.no_valid_file = TRUE;
9111 getFileChunkBE(file, chunk_name, NULL);
9112 if (strEqual(chunk_name, "RND1"))
9114 getFile32BitBE(file); // not used
9116 getFileChunkBE(file, chunk_name, NULL);
9117 if (!strEqual(chunk_name, "TAPE"))
9119 tape.no_valid_file = TRUE;
9121 Warn("unknown format of tape file '%s'", filename);
9128 else // check for pre-2.0 file format with cookie string
9130 strcpy(cookie, chunk_name);
9131 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9133 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9134 cookie[strlen(cookie) - 1] = '\0';
9136 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9138 tape.no_valid_file = TRUE;
9140 Warn("unknown format of tape file '%s'", filename);
9147 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9149 tape.no_valid_file = TRUE;
9151 Warn("unsupported version of tape file '%s'", filename);
9158 // pre-2.0 tape files have no game version, so use file version here
9159 tape.game_version = tape.file_version;
9162 if (tape.file_version < FILE_VERSION_1_2)
9164 // tape files from versions before 1.2.0 without chunk structure
9165 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9166 LoadTape_BODY(file, 2 * tape.length, &tape);
9174 int (*loader)(File *, int, struct TapeInfo *);
9178 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9179 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9180 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9181 { "INFO", -1, LoadTape_INFO },
9182 { "BODY", -1, LoadTape_BODY },
9186 while (getFileChunkBE(file, chunk_name, &chunk_size))
9190 while (chunk_info[i].name != NULL &&
9191 !strEqual(chunk_name, chunk_info[i].name))
9194 if (chunk_info[i].name == NULL)
9196 Warn("unknown chunk '%s' in tape file '%s'",
9197 chunk_name, filename);
9199 ReadUnusedBytesFromFile(file, chunk_size);
9201 else if (chunk_info[i].size != -1 &&
9202 chunk_info[i].size != chunk_size)
9204 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9205 chunk_size, chunk_name, filename);
9207 ReadUnusedBytesFromFile(file, chunk_size);
9211 // call function to load this tape chunk
9212 int chunk_size_expected =
9213 (chunk_info[i].loader)(file, chunk_size, &tape);
9215 // the size of some chunks cannot be checked before reading other
9216 // chunks first (like "HEAD" and "BODY") that contain some header
9217 // information, so check them here
9218 if (chunk_size_expected != chunk_size)
9220 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9221 chunk_size, chunk_name, filename);
9229 tape.length_frames = GetTapeLengthFrames();
9230 tape.length_seconds = GetTapeLengthSeconds();
9233 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9235 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9237 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9238 tape.engine_version);
9242 void LoadTape(int nr)
9244 char *filename = getTapeFilename(nr);
9246 LoadTapeFromFilename(filename);
9249 void LoadSolutionTape(int nr)
9251 char *filename = getSolutionTapeFilename(nr);
9253 LoadTapeFromFilename(filename);
9255 if (TAPE_IS_EMPTY(tape))
9257 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9258 level.native_bd_level->replay != NULL)
9259 CopyNativeTape_BD_to_RND(&level);
9260 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9261 level.native_sp_level->demo.is_available)
9262 CopyNativeTape_SP_to_RND(&level);
9266 void LoadScoreTape(char *score_tape_basename, int nr)
9268 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9270 LoadTapeFromFilename(filename);
9273 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9275 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9277 LoadTapeFromFilename(filename);
9280 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9282 // chunk required for team mode tapes with non-default screen size
9283 return (tape->num_participating_players > 1 &&
9284 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9285 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9288 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9290 putFileVersion(file, tape->file_version);
9291 putFileVersion(file, tape->game_version);
9294 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9297 byte store_participating_players = 0;
9299 // set bits for participating players for compact storage
9300 for (i = 0; i < MAX_PLAYERS; i++)
9301 if (tape->player_participates[i])
9302 store_participating_players |= (1 << i);
9304 putFile32BitBE(file, tape->random_seed);
9305 putFile32BitBE(file, tape->date);
9306 putFile32BitBE(file, tape->length);
9308 putFile8Bit(file, store_participating_players);
9310 putFile8Bit(file, getTapeActionValue(tape));
9312 putFile8Bit(file, tape->property_bits);
9313 putFile8Bit(file, tape->solved);
9315 putFileVersion(file, tape->engine_version);
9318 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9320 putFile8Bit(file, tape->scr_fieldx);
9321 putFile8Bit(file, tape->scr_fieldy);
9324 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9326 int level_identifier_size = strlen(tape->level_identifier) + 1;
9329 putFile16BitBE(file, level_identifier_size);
9331 for (i = 0; i < level_identifier_size; i++)
9332 putFile8Bit(file, tape->level_identifier[i]);
9334 putFile16BitBE(file, tape->level_nr);
9337 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9341 for (i = 0; i < tape->length; i++)
9343 if (tape->use_key_actions)
9345 for (j = 0; j < MAX_PLAYERS; j++)
9346 if (tape->player_participates[j])
9347 putFile8Bit(file, tape->pos[i].action[j]);
9350 if (tape->use_mouse_actions)
9352 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9353 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9354 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9357 putFile8Bit(file, tape->pos[i].delay);
9361 void SaveTapeToFilename(char *filename)
9365 int info_chunk_size;
9366 int body_chunk_size;
9368 if (!(file = fopen(filename, MODE_WRITE)))
9370 Warn("cannot save level recording file '%s'", filename);
9375 tape_pos_size = getTapePosSize(&tape);
9377 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9378 body_chunk_size = tape_pos_size * tape.length;
9380 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9381 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9383 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9384 SaveTape_VERS(file, &tape);
9386 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9387 SaveTape_HEAD(file, &tape);
9389 if (checkSaveTape_SCRN(&tape))
9391 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9392 SaveTape_SCRN(file, &tape);
9395 putFileChunkBE(file, "INFO", info_chunk_size);
9396 SaveTape_INFO(file, &tape);
9398 putFileChunkBE(file, "BODY", body_chunk_size);
9399 SaveTape_BODY(file, &tape);
9403 SetFilePermissions(filename, PERMS_PRIVATE);
9406 static void SaveTapeExt(char *filename)
9410 tape.file_version = FILE_VERSION_ACTUAL;
9411 tape.game_version = GAME_VERSION_ACTUAL;
9413 tape.num_participating_players = 0;
9415 // count number of participating players
9416 for (i = 0; i < MAX_PLAYERS; i++)
9417 if (tape.player_participates[i])
9418 tape.num_participating_players++;
9420 SaveTapeToFilename(filename);
9422 tape.changed = FALSE;
9425 void SaveTape(int nr)
9427 char *filename = getTapeFilename(nr);
9429 InitTapeDirectory(leveldir_current->subdir);
9431 SaveTapeExt(filename);
9434 void SaveScoreTape(int nr)
9436 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9438 // used instead of "leveldir_current->subdir" (for network games)
9439 InitScoreTapeDirectory(levelset.identifier, nr);
9441 SaveTapeExt(filename);
9444 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9445 unsigned int req_state_added)
9447 char *filename = getTapeFilename(nr);
9448 boolean new_tape = !fileExists(filename);
9449 boolean tape_saved = FALSE;
9451 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9456 Request(msg_saved, REQ_CONFIRM | req_state_added);
9464 boolean SaveTapeChecked(int nr)
9466 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9469 boolean SaveTapeChecked_LevelSolved(int nr)
9471 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9472 "Level solved! Tape saved!", REQ_STAY_OPEN);
9475 void DumpTape(struct TapeInfo *tape)
9477 int tape_frame_counter;
9480 if (tape->no_valid_file)
9482 Warn("cannot dump -- no valid tape file found");
9489 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9490 tape->level_nr, tape->file_version, tape->game_version);
9491 Print(" (effective engine version %08d)\n",
9492 tape->engine_version);
9493 Print("Level series identifier: '%s'\n", tape->level_identifier);
9495 Print("Solution tape: %s\n",
9496 tape->solved ? "yes" :
9497 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9499 Print("Special tape properties: ");
9500 if (tape->property_bits == TAPE_PROPERTY_NONE)
9502 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9503 Print("[em_random_bug]");
9504 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9505 Print("[game_speed]");
9506 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9508 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9509 Print("[single_step]");
9510 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9511 Print("[snapshot]");
9512 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9513 Print("[replayed]");
9514 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9515 Print("[tas_keys]");
9516 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9517 Print("[small_graphics]");
9520 int year2 = tape->date / 10000;
9521 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9522 int month_index_raw = (tape->date / 100) % 100;
9523 int month_index = month_index_raw % 12; // prevent invalid index
9524 int month = month_index + 1;
9525 int day = tape->date % 100;
9527 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9531 tape_frame_counter = 0;
9533 for (i = 0; i < tape->length; i++)
9535 if (i >= MAX_TAPE_LEN)
9540 for (j = 0; j < MAX_PLAYERS; j++)
9542 if (tape->player_participates[j])
9544 int action = tape->pos[i].action[j];
9546 Print("%d:%02x ", j, action);
9547 Print("[%c%c%c%c|%c%c] - ",
9548 (action & JOY_LEFT ? '<' : ' '),
9549 (action & JOY_RIGHT ? '>' : ' '),
9550 (action & JOY_UP ? '^' : ' '),
9551 (action & JOY_DOWN ? 'v' : ' '),
9552 (action & JOY_BUTTON_1 ? '1' : ' '),
9553 (action & JOY_BUTTON_2 ? '2' : ' '));
9557 Print("(%03d) ", tape->pos[i].delay);
9558 Print("[%05d]\n", tape_frame_counter);
9560 tape_frame_counter += tape->pos[i].delay;
9566 void DumpTapes(void)
9568 static LevelDirTree *dumptape_leveldir = NULL;
9570 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9571 global.dumptape_leveldir);
9573 if (dumptape_leveldir == NULL)
9574 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9576 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9577 global.dumptape_level_nr > dumptape_leveldir->last_level)
9578 Fail("no such level number: %d", global.dumptape_level_nr);
9580 leveldir_current = dumptape_leveldir;
9582 if (options.mytapes)
9583 LoadTape(global.dumptape_level_nr);
9585 LoadSolutionTape(global.dumptape_level_nr);
9593 // ============================================================================
9594 // score file functions
9595 // ============================================================================
9597 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9601 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9603 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9604 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9605 scores->entry[i].score = 0;
9606 scores->entry[i].time = 0;
9608 scores->entry[i].id = -1;
9609 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9610 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9611 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9612 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9613 strcpy(scores->entry[i].country_code, "??");
9616 scores->num_entries = 0;
9617 scores->last_added = -1;
9618 scores->last_added_local = -1;
9620 scores->updated = FALSE;
9621 scores->uploaded = FALSE;
9622 scores->tape_downloaded = FALSE;
9623 scores->force_last_added = FALSE;
9625 // The following values are intentionally not reset here:
9629 // - continue_playing
9630 // - continue_on_return
9633 static void setScoreInfoToDefaults(void)
9635 setScoreInfoToDefaultsExt(&scores);
9638 static void setServerScoreInfoToDefaults(void)
9640 setScoreInfoToDefaultsExt(&server_scores);
9643 static void LoadScore_OLD(int nr)
9646 char *filename = getScoreFilename(nr);
9647 char cookie[MAX_LINE_LEN];
9648 char line[MAX_LINE_LEN];
9652 if (!(file = fopen(filename, MODE_READ)))
9655 // check file identifier
9656 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9658 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9659 cookie[strlen(cookie) - 1] = '\0';
9661 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9663 Warn("unknown format of score file '%s'", filename);
9670 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9672 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9673 Warn("fscanf() failed; %s", strerror(errno));
9675 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9678 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9679 line[strlen(line) - 1] = '\0';
9681 for (line_ptr = line; *line_ptr; line_ptr++)
9683 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9685 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9686 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9695 static void ConvertScore_OLD(void)
9697 // only convert score to time for levels that rate playing time over score
9698 if (!level.rate_time_over_score)
9701 // convert old score to playing time for score-less levels (like Supaplex)
9702 int time_final_max = 999;
9705 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9707 int score = scores.entry[i].score;
9709 if (score > 0 && score < time_final_max)
9710 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9714 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9716 scores->file_version = getFileVersion(file);
9717 scores->game_version = getFileVersion(file);
9722 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9724 char *level_identifier = NULL;
9725 int level_identifier_size;
9728 level_identifier_size = getFile16BitBE(file);
9730 level_identifier = checked_malloc(level_identifier_size);
9732 for (i = 0; i < level_identifier_size; i++)
9733 level_identifier[i] = getFile8Bit(file);
9735 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9736 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9738 checked_free(level_identifier);
9740 scores->level_nr = getFile16BitBE(file);
9741 scores->num_entries = getFile16BitBE(file);
9743 chunk_size = 2 + level_identifier_size + 2 + 2;
9748 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9752 for (i = 0; i < scores->num_entries; i++)
9754 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9755 scores->entry[i].name[j] = getFile8Bit(file);
9757 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9760 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9765 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9769 for (i = 0; i < scores->num_entries; i++)
9770 scores->entry[i].score = getFile16BitBE(file);
9772 chunk_size = scores->num_entries * 2;
9777 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9781 for (i = 0; i < scores->num_entries; i++)
9782 scores->entry[i].score = getFile32BitBE(file);
9784 chunk_size = scores->num_entries * 4;
9789 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9793 for (i = 0; i < scores->num_entries; i++)
9794 scores->entry[i].time = getFile32BitBE(file);
9796 chunk_size = scores->num_entries * 4;
9801 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9805 for (i = 0; i < scores->num_entries; i++)
9807 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9808 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9810 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9813 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9818 void LoadScore(int nr)
9820 char *filename = getScoreFilename(nr);
9821 char cookie[MAX_LINE_LEN];
9822 char chunk_name[CHUNK_ID_LEN + 1];
9824 boolean old_score_file_format = FALSE;
9827 // always start with reliable default values
9828 setScoreInfoToDefaults();
9830 if (!(file = openFile(filename, MODE_READ)))
9833 getFileChunkBE(file, chunk_name, NULL);
9834 if (strEqual(chunk_name, "RND1"))
9836 getFile32BitBE(file); // not used
9838 getFileChunkBE(file, chunk_name, NULL);
9839 if (!strEqual(chunk_name, "SCOR"))
9841 Warn("unknown format of score file '%s'", filename);
9848 else // check for old file format with cookie string
9850 strcpy(cookie, chunk_name);
9851 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9853 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9854 cookie[strlen(cookie) - 1] = '\0';
9856 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9858 Warn("unknown format of score file '%s'", filename);
9865 old_score_file_format = TRUE;
9868 if (old_score_file_format)
9870 // score files from versions before 4.2.4.0 without chunk structure
9873 // convert score to time, if possible (mainly for Supaplex levels)
9882 int (*loader)(File *, int, struct ScoreInfo *);
9886 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9887 { "INFO", -1, LoadScore_INFO },
9888 { "NAME", -1, LoadScore_NAME },
9889 { "SCOR", -1, LoadScore_SCOR },
9890 { "SC4R", -1, LoadScore_SC4R },
9891 { "TIME", -1, LoadScore_TIME },
9892 { "TAPE", -1, LoadScore_TAPE },
9897 while (getFileChunkBE(file, chunk_name, &chunk_size))
9901 while (chunk_info[i].name != NULL &&
9902 !strEqual(chunk_name, chunk_info[i].name))
9905 if (chunk_info[i].name == NULL)
9907 Warn("unknown chunk '%s' in score file '%s'",
9908 chunk_name, filename);
9910 ReadUnusedBytesFromFile(file, chunk_size);
9912 else if (chunk_info[i].size != -1 &&
9913 chunk_info[i].size != chunk_size)
9915 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9916 chunk_size, chunk_name, filename);
9918 ReadUnusedBytesFromFile(file, chunk_size);
9922 // call function to load this score chunk
9923 int chunk_size_expected =
9924 (chunk_info[i].loader)(file, chunk_size, &scores);
9926 // the size of some chunks cannot be checked before reading other
9927 // chunks first (like "HEAD" and "BODY") that contain some header
9928 // information, so check them here
9929 if (chunk_size_expected != chunk_size)
9931 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9932 chunk_size, chunk_name, filename);
9941 #if ENABLE_HISTORIC_CHUNKS
9942 void SaveScore_OLD(int nr)
9945 char *filename = getScoreFilename(nr);
9948 // used instead of "leveldir_current->subdir" (for network games)
9949 InitScoreDirectory(levelset.identifier);
9951 if (!(file = fopen(filename, MODE_WRITE)))
9953 Warn("cannot save score for level %d", nr);
9958 fprintf(file, "%s\n\n", SCORE_COOKIE);
9960 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9961 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9965 SetFilePermissions(filename, PERMS_PRIVATE);
9969 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9971 putFileVersion(file, scores->file_version);
9972 putFileVersion(file, scores->game_version);
9975 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9977 int level_identifier_size = strlen(scores->level_identifier) + 1;
9980 putFile16BitBE(file, level_identifier_size);
9982 for (i = 0; i < level_identifier_size; i++)
9983 putFile8Bit(file, scores->level_identifier[i]);
9985 putFile16BitBE(file, scores->level_nr);
9986 putFile16BitBE(file, scores->num_entries);
9989 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9993 for (i = 0; i < scores->num_entries; i++)
9995 int name_size = strlen(scores->entry[i].name);
9997 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9998 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10002 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10006 for (i = 0; i < scores->num_entries; i++)
10007 putFile16BitBE(file, scores->entry[i].score);
10010 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10014 for (i = 0; i < scores->num_entries; i++)
10015 putFile32BitBE(file, scores->entry[i].score);
10018 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10022 for (i = 0; i < scores->num_entries; i++)
10023 putFile32BitBE(file, scores->entry[i].time);
10026 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10030 for (i = 0; i < scores->num_entries; i++)
10032 int size = strlen(scores->entry[i].tape_basename);
10034 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10035 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10039 static void SaveScoreToFilename(char *filename)
10042 int info_chunk_size;
10043 int name_chunk_size;
10044 int scor_chunk_size;
10045 int sc4r_chunk_size;
10046 int time_chunk_size;
10047 int tape_chunk_size;
10048 boolean has_large_score_values;
10051 if (!(file = fopen(filename, MODE_WRITE)))
10053 Warn("cannot save score file '%s'", filename);
10058 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10059 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10060 scor_chunk_size = scores.num_entries * 2;
10061 sc4r_chunk_size = scores.num_entries * 4;
10062 time_chunk_size = scores.num_entries * 4;
10063 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10065 has_large_score_values = FALSE;
10066 for (i = 0; i < scores.num_entries; i++)
10067 if (scores.entry[i].score > 0xffff)
10068 has_large_score_values = TRUE;
10070 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10071 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10073 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10074 SaveScore_VERS(file, &scores);
10076 putFileChunkBE(file, "INFO", info_chunk_size);
10077 SaveScore_INFO(file, &scores);
10079 putFileChunkBE(file, "NAME", name_chunk_size);
10080 SaveScore_NAME(file, &scores);
10082 if (has_large_score_values)
10084 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10085 SaveScore_SC4R(file, &scores);
10089 putFileChunkBE(file, "SCOR", scor_chunk_size);
10090 SaveScore_SCOR(file, &scores);
10093 putFileChunkBE(file, "TIME", time_chunk_size);
10094 SaveScore_TIME(file, &scores);
10096 putFileChunkBE(file, "TAPE", tape_chunk_size);
10097 SaveScore_TAPE(file, &scores);
10101 SetFilePermissions(filename, PERMS_PRIVATE);
10104 void SaveScore(int nr)
10106 char *filename = getScoreFilename(nr);
10109 // used instead of "leveldir_current->subdir" (for network games)
10110 InitScoreDirectory(levelset.identifier);
10112 scores.file_version = FILE_VERSION_ACTUAL;
10113 scores.game_version = GAME_VERSION_ACTUAL;
10115 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10116 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10117 scores.level_nr = level_nr;
10119 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10120 if (scores.entry[i].score == 0 &&
10121 scores.entry[i].time == 0 &&
10122 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10125 scores.num_entries = i;
10127 if (scores.num_entries == 0)
10130 SaveScoreToFilename(filename);
10133 static void LoadServerScoreFromCache(int nr)
10135 struct ScoreEntry score_entry;
10144 { &score_entry.score, FALSE, 0 },
10145 { &score_entry.time, FALSE, 0 },
10146 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10147 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10148 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10149 { &score_entry.id, FALSE, 0 },
10150 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10151 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10152 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10153 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10157 char *filename = getScoreCacheFilename(nr);
10158 SetupFileHash *score_hash = loadSetupFileHash(filename);
10161 server_scores.num_entries = 0;
10163 if (score_hash == NULL)
10166 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10168 score_entry = server_scores.entry[i];
10170 for (j = 0; score_mapping[j].value != NULL; j++)
10174 sprintf(token, "%02d.%d", i, j);
10176 char *value = getHashEntry(score_hash, token);
10181 if (score_mapping[j].is_string)
10183 char *score_value = (char *)score_mapping[j].value;
10184 int value_size = score_mapping[j].string_size;
10186 strncpy(score_value, value, value_size);
10187 score_value[value_size] = '\0';
10191 int *score_value = (int *)score_mapping[j].value;
10193 *score_value = atoi(value);
10196 server_scores.num_entries = i + 1;
10199 server_scores.entry[i] = score_entry;
10202 freeSetupFileHash(score_hash);
10205 void LoadServerScore(int nr, boolean download_score)
10207 if (!setup.use_api_server)
10210 // always start with reliable default values
10211 setServerScoreInfoToDefaults();
10213 // 1st step: load server scores from cache file (which may not exist)
10214 // (this should prevent reading it while the thread is writing to it)
10215 LoadServerScoreFromCache(nr);
10217 if (download_score && runtime.use_api_server)
10219 // 2nd step: download server scores from score server to cache file
10220 // (as thread, as it might time out if the server is not reachable)
10221 ApiGetScoreAsThread(nr);
10225 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10227 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10229 // if score tape not uploaded, ask for uploading missing tapes later
10230 if (!setup.has_remaining_tapes)
10231 setup.ask_for_remaining_tapes = TRUE;
10233 setup.provide_uploading_tapes = TRUE;
10234 setup.has_remaining_tapes = TRUE;
10236 SaveSetup_ServerSetup();
10239 void SaveServerScore(int nr, boolean tape_saved)
10241 if (!runtime.use_api_server)
10243 PrepareScoreTapesForUpload(leveldir_current->subdir);
10248 ApiAddScoreAsThread(nr, tape_saved, NULL);
10251 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10252 char *score_tape_filename)
10254 if (!runtime.use_api_server)
10257 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10260 void LoadLocalAndServerScore(int nr, boolean download_score)
10262 int last_added_local = scores.last_added_local;
10263 boolean force_last_added = scores.force_last_added;
10265 // needed if only showing server scores
10266 setScoreInfoToDefaults();
10268 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10271 // restore last added local score entry (before merging server scores)
10272 scores.last_added = scores.last_added_local = last_added_local;
10274 if (setup.use_api_server &&
10275 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10277 // load server scores from cache file and trigger update from server
10278 LoadServerScore(nr, download_score);
10280 // merge local scores with scores from server
10281 MergeServerScore();
10284 if (force_last_added)
10285 scores.force_last_added = force_last_added;
10289 // ============================================================================
10290 // setup file functions
10291 // ============================================================================
10293 #define TOKEN_STR_PLAYER_PREFIX "player_"
10296 static struct TokenInfo global_setup_tokens[] =
10300 &setup.player_name, "player_name"
10304 &setup.multiple_users, "multiple_users"
10308 &setup.sound, "sound"
10312 &setup.sound_loops, "repeating_sound_loops"
10316 &setup.sound_music, "background_music"
10320 &setup.sound_simple, "simple_sound_effects"
10324 &setup.toons, "toons"
10328 &setup.global_animations, "global_animations"
10332 &setup.scroll_delay, "scroll_delay"
10336 &setup.forced_scroll_delay, "forced_scroll_delay"
10340 &setup.scroll_delay_value, "scroll_delay_value"
10344 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10348 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10352 &setup.fade_screens, "fade_screens"
10356 &setup.autorecord, "automatic_tape_recording"
10360 &setup.autorecord_after_replay, "autorecord_after_replay"
10364 &setup.auto_pause_on_start, "auto_pause_on_start"
10368 &setup.show_titlescreen, "show_titlescreen"
10372 &setup.quick_doors, "quick_doors"
10376 &setup.team_mode, "team_mode"
10380 &setup.handicap, "handicap"
10384 &setup.skip_levels, "skip_levels"
10388 &setup.increment_levels, "increment_levels"
10392 &setup.auto_play_next_level, "auto_play_next_level"
10396 &setup.count_score_after_game, "count_score_after_game"
10400 &setup.show_scores_after_game, "show_scores_after_game"
10404 &setup.time_limit, "time_limit"
10408 &setup.fullscreen, "fullscreen"
10412 &setup.window_scaling_percent, "window_scaling_percent"
10416 &setup.window_scaling_quality, "window_scaling_quality"
10420 &setup.screen_rendering_mode, "screen_rendering_mode"
10424 &setup.vsync_mode, "vsync_mode"
10428 &setup.ask_on_escape, "ask_on_escape"
10432 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10436 &setup.ask_on_game_over, "ask_on_game_over"
10440 &setup.ask_on_quit_game, "ask_on_quit_game"
10444 &setup.ask_on_quit_program, "ask_on_quit_program"
10448 &setup.quick_switch, "quick_player_switch"
10452 &setup.input_on_focus, "input_on_focus"
10456 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10460 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10464 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10468 &setup.game_speed_extended, "game_speed_extended"
10472 &setup.game_frame_delay, "game_frame_delay"
10476 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10480 &setup.bd_skip_hatching, "bd_skip_hatching"
10484 &setup.bd_scroll_delay, "bd_scroll_delay"
10488 &setup.bd_smooth_movements, "bd_smooth_movements"
10492 &setup.sp_show_border_elements, "sp_show_border_elements"
10496 &setup.small_game_graphics, "small_game_graphics"
10500 &setup.show_load_save_buttons, "show_load_save_buttons"
10504 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10508 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10512 &setup.graphics_set, "graphics_set"
10516 &setup.sounds_set, "sounds_set"
10520 &setup.music_set, "music_set"
10524 &setup.override_level_graphics, "override_level_graphics"
10528 &setup.override_level_sounds, "override_level_sounds"
10532 &setup.override_level_music, "override_level_music"
10536 &setup.volume_simple, "volume_simple"
10540 &setup.volume_loops, "volume_loops"
10544 &setup.volume_music, "volume_music"
10548 &setup.network_mode, "network_mode"
10552 &setup.network_player_nr, "network_player"
10556 &setup.network_server_hostname, "network_server_hostname"
10560 &setup.touch.control_type, "touch.control_type"
10564 &setup.touch.move_distance, "touch.move_distance"
10568 &setup.touch.drop_distance, "touch.drop_distance"
10572 &setup.touch.transparency, "touch.transparency"
10576 &setup.touch.draw_outlined, "touch.draw_outlined"
10580 &setup.touch.draw_pressed, "touch.draw_pressed"
10584 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10588 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10592 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10596 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10600 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10604 static struct TokenInfo auto_setup_tokens[] =
10608 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10612 static struct TokenInfo server_setup_tokens[] =
10616 &setup.player_uuid, "player_uuid"
10620 &setup.player_version, "player_version"
10624 &setup.use_api_server, TEST_PREFIX "use_api_server"
10628 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10632 &setup.api_server_password, TEST_PREFIX "api_server_password"
10636 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10640 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10644 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10648 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10652 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10656 static struct TokenInfo editor_setup_tokens[] =
10660 &setup.editor.el_classic, "editor.el_classic"
10664 &setup.editor.el_custom, "editor.el_custom"
10668 &setup.editor.el_user_defined, "editor.el_user_defined"
10672 &setup.editor.el_dynamic, "editor.el_dynamic"
10676 &setup.editor.el_headlines, "editor.el_headlines"
10680 &setup.editor.show_element_token, "editor.show_element_token"
10684 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10688 static struct TokenInfo editor_cascade_setup_tokens[] =
10692 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10696 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10700 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10704 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10708 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10712 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10716 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10720 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10724 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10728 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10732 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10736 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10740 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10744 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10748 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10752 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10756 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10760 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10764 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10768 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10772 static struct TokenInfo shortcut_setup_tokens[] =
10776 &setup.shortcut.save_game, "shortcut.save_game"
10780 &setup.shortcut.load_game, "shortcut.load_game"
10784 &setup.shortcut.restart_game, "shortcut.restart_game"
10788 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10792 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10796 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10800 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10804 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10808 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10812 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10816 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10820 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10824 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10828 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10832 &setup.shortcut.tape_record, "shortcut.tape_record"
10836 &setup.shortcut.tape_play, "shortcut.tape_play"
10840 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10844 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10848 &setup.shortcut.sound_music, "shortcut.sound_music"
10852 &setup.shortcut.snap_left, "shortcut.snap_left"
10856 &setup.shortcut.snap_right, "shortcut.snap_right"
10860 &setup.shortcut.snap_up, "shortcut.snap_up"
10864 &setup.shortcut.snap_down, "shortcut.snap_down"
10868 static struct SetupInputInfo setup_input;
10869 static struct TokenInfo player_setup_tokens[] =
10873 &setup_input.use_joystick, ".use_joystick"
10877 &setup_input.joy.device_name, ".joy.device_name"
10881 &setup_input.joy.xleft, ".joy.xleft"
10885 &setup_input.joy.xmiddle, ".joy.xmiddle"
10889 &setup_input.joy.xright, ".joy.xright"
10893 &setup_input.joy.yupper, ".joy.yupper"
10897 &setup_input.joy.ymiddle, ".joy.ymiddle"
10901 &setup_input.joy.ylower, ".joy.ylower"
10905 &setup_input.joy.snap, ".joy.snap_field"
10909 &setup_input.joy.drop, ".joy.place_bomb"
10913 &setup_input.key.left, ".key.move_left"
10917 &setup_input.key.right, ".key.move_right"
10921 &setup_input.key.up, ".key.move_up"
10925 &setup_input.key.down, ".key.move_down"
10929 &setup_input.key.snap, ".key.snap_field"
10933 &setup_input.key.drop, ".key.place_bomb"
10937 static struct TokenInfo system_setup_tokens[] =
10941 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10945 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10949 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10953 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10957 static struct TokenInfo internal_setup_tokens[] =
10961 &setup.internal.program_title, "program_title"
10965 &setup.internal.program_version, "program_version"
10969 &setup.internal.program_author, "program_author"
10973 &setup.internal.program_email, "program_email"
10977 &setup.internal.program_website, "program_website"
10981 &setup.internal.program_copyright, "program_copyright"
10985 &setup.internal.program_company, "program_company"
10989 &setup.internal.program_icon_file, "program_icon_file"
10993 &setup.internal.default_graphics_set, "default_graphics_set"
10997 &setup.internal.default_sounds_set, "default_sounds_set"
11001 &setup.internal.default_music_set, "default_music_set"
11005 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11009 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11013 &setup.internal.fallback_music_file, "fallback_music_file"
11017 &setup.internal.default_level_series, "default_level_series"
11021 &setup.internal.default_window_width, "default_window_width"
11025 &setup.internal.default_window_height, "default_window_height"
11029 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11033 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11037 &setup.internal.create_user_levelset, "create_user_levelset"
11041 &setup.internal.info_screens_from_main, "info_screens_from_main"
11045 &setup.internal.menu_game, "menu_game"
11049 &setup.internal.menu_engines, "menu_engines"
11053 &setup.internal.menu_editor, "menu_editor"
11057 &setup.internal.menu_graphics, "menu_graphics"
11061 &setup.internal.menu_sound, "menu_sound"
11065 &setup.internal.menu_artwork, "menu_artwork"
11069 &setup.internal.menu_input, "menu_input"
11073 &setup.internal.menu_touch, "menu_touch"
11077 &setup.internal.menu_shortcuts, "menu_shortcuts"
11081 &setup.internal.menu_exit, "menu_exit"
11085 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11089 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11093 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11097 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11101 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11105 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11109 &setup.internal.info_title, "info_title"
11113 &setup.internal.info_elements, "info_elements"
11117 &setup.internal.info_music, "info_music"
11121 &setup.internal.info_credits, "info_credits"
11125 &setup.internal.info_program, "info_program"
11129 &setup.internal.info_version, "info_version"
11133 &setup.internal.info_levelset, "info_levelset"
11137 &setup.internal.info_exit, "info_exit"
11141 static struct TokenInfo debug_setup_tokens[] =
11145 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11149 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11153 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11157 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11161 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11165 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11169 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11173 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11177 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11181 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11185 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11189 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11193 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11197 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11201 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11205 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11209 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11213 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11217 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11221 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11225 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11228 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11232 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11236 &setup.debug.xsn_mode, "debug.xsn_mode"
11240 &setup.debug.xsn_percent, "debug.xsn_percent"
11244 static struct TokenInfo options_setup_tokens[] =
11248 &setup.options.verbose, "options.verbose"
11252 &setup.options.debug, "options.debug"
11256 &setup.options.debug_mode, "options.debug_mode"
11260 static void setSetupInfoToDefaults(struct SetupInfo *si)
11264 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11266 si->multiple_users = TRUE;
11269 si->sound_loops = TRUE;
11270 si->sound_music = TRUE;
11271 si->sound_simple = TRUE;
11273 si->global_animations = TRUE;
11274 si->scroll_delay = TRUE;
11275 si->forced_scroll_delay = FALSE;
11276 si->scroll_delay_value = STD_SCROLL_DELAY;
11277 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11278 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11279 si->fade_screens = TRUE;
11280 si->autorecord = TRUE;
11281 si->autorecord_after_replay = TRUE;
11282 si->auto_pause_on_start = FALSE;
11283 si->show_titlescreen = TRUE;
11284 si->quick_doors = FALSE;
11285 si->team_mode = FALSE;
11286 si->handicap = TRUE;
11287 si->skip_levels = TRUE;
11288 si->increment_levels = TRUE;
11289 si->auto_play_next_level = TRUE;
11290 si->count_score_after_game = TRUE;
11291 si->show_scores_after_game = TRUE;
11292 si->time_limit = TRUE;
11293 si->fullscreen = FALSE;
11294 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11295 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11296 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11297 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11298 si->ask_on_escape = TRUE;
11299 si->ask_on_escape_editor = TRUE;
11300 si->ask_on_game_over = TRUE;
11301 si->ask_on_quit_game = TRUE;
11302 si->ask_on_quit_program = TRUE;
11303 si->quick_switch = FALSE;
11304 si->input_on_focus = FALSE;
11305 si->prefer_aga_graphics = TRUE;
11306 si->prefer_lowpass_sounds = FALSE;
11307 si->prefer_extra_panel_items = TRUE;
11308 si->game_speed_extended = FALSE;
11309 si->game_frame_delay = GAME_FRAME_DELAY;
11310 si->bd_skip_uncovering = FALSE;
11311 si->bd_skip_hatching = FALSE;
11312 si->bd_scroll_delay = TRUE;
11313 si->bd_smooth_movements = AUTO;
11314 si->sp_show_border_elements = FALSE;
11315 si->small_game_graphics = FALSE;
11316 si->show_load_save_buttons = FALSE;
11317 si->show_undo_redo_buttons = FALSE;
11318 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11320 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11321 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11322 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11324 si->override_level_graphics = FALSE;
11325 si->override_level_sounds = FALSE;
11326 si->override_level_music = FALSE;
11328 si->volume_simple = 100; // percent
11329 si->volume_loops = 100; // percent
11330 si->volume_music = 100; // percent
11332 si->network_mode = FALSE;
11333 si->network_player_nr = 0; // first player
11334 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11336 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11337 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11338 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11339 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11340 si->touch.draw_outlined = TRUE;
11341 si->touch.draw_pressed = TRUE;
11343 for (i = 0; i < 2; i++)
11345 char *default_grid_button[6][2] =
11351 { "111222", " vv " },
11352 { "111222", " vv " }
11354 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11355 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11356 int min_xsize = MIN(6, grid_xsize);
11357 int min_ysize = MIN(6, grid_ysize);
11358 int startx = grid_xsize - min_xsize;
11359 int starty = grid_ysize - min_ysize;
11362 // virtual buttons grid can only be set to defaults if video is initialized
11363 // (this will be repeated if virtual buttons are not loaded from setup file)
11364 if (video.initialized)
11366 si->touch.grid_xsize[i] = grid_xsize;
11367 si->touch.grid_ysize[i] = grid_ysize;
11371 si->touch.grid_xsize[i] = -1;
11372 si->touch.grid_ysize[i] = -1;
11375 for (x = 0; x < MAX_GRID_XSIZE; x++)
11376 for (y = 0; y < MAX_GRID_YSIZE; y++)
11377 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11379 for (x = 0; x < min_xsize; x++)
11380 for (y = 0; y < min_ysize; y++)
11381 si->touch.grid_button[i][x][starty + y] =
11382 default_grid_button[y][0][x];
11384 for (x = 0; x < min_xsize; x++)
11385 for (y = 0; y < min_ysize; y++)
11386 si->touch.grid_button[i][startx + x][starty + y] =
11387 default_grid_button[y][1][x];
11390 si->touch.grid_initialized = video.initialized;
11392 si->touch.overlay_buttons = FALSE;
11394 si->editor.el_boulderdash = TRUE;
11395 si->editor.el_boulderdash_native = TRUE;
11396 si->editor.el_boulderdash_effects = TRUE;
11397 si->editor.el_emerald_mine = TRUE;
11398 si->editor.el_emerald_mine_club = TRUE;
11399 si->editor.el_more = TRUE;
11400 si->editor.el_sokoban = TRUE;
11401 si->editor.el_supaplex = TRUE;
11402 si->editor.el_diamond_caves = TRUE;
11403 si->editor.el_dx_boulderdash = TRUE;
11405 si->editor.el_mirror_magic = TRUE;
11406 si->editor.el_deflektor = TRUE;
11408 si->editor.el_chars = TRUE;
11409 si->editor.el_steel_chars = TRUE;
11411 si->editor.el_classic = TRUE;
11412 si->editor.el_custom = TRUE;
11414 si->editor.el_user_defined = FALSE;
11415 si->editor.el_dynamic = TRUE;
11417 si->editor.el_headlines = TRUE;
11419 si->editor.show_element_token = FALSE;
11421 si->editor.show_read_only_warning = TRUE;
11423 si->editor.use_template_for_new_levels = TRUE;
11425 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11426 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11427 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11428 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11429 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11431 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11432 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11433 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11434 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11435 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11437 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11438 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11439 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11440 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11441 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11442 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11444 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11445 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11446 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11448 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11449 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11450 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11451 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11453 for (i = 0; i < MAX_PLAYERS; i++)
11455 si->input[i].use_joystick = FALSE;
11456 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11457 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11458 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11459 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11460 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11461 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11462 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11463 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11464 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11465 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11466 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11467 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11468 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11469 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11470 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11473 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11474 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11475 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11476 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11478 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11479 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11480 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11481 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11482 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11483 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11484 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11486 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11488 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11489 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11490 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11492 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11493 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11494 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11496 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11497 si->internal.choose_from_top_leveldir = FALSE;
11498 si->internal.show_scaling_in_title = TRUE;
11499 si->internal.create_user_levelset = TRUE;
11500 si->internal.info_screens_from_main = FALSE;
11502 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11503 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11505 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11506 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11507 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11508 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11509 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11510 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11511 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11512 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11513 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11514 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11516 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11517 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11518 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11519 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11520 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11521 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11522 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11523 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11524 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11525 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11527 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11528 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11530 si->debug.show_frames_per_second = FALSE;
11532 si->debug.xsn_mode = AUTO;
11533 si->debug.xsn_percent = 0;
11535 si->options.verbose = FALSE;
11536 si->options.debug = FALSE;
11537 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11539 #if defined(PLATFORM_ANDROID)
11540 si->fullscreen = TRUE;
11541 si->touch.overlay_buttons = TRUE;
11544 setHideSetupEntry(&setup.debug.xsn_mode);
11547 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11549 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11552 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11554 si->player_uuid = NULL; // (will be set later)
11555 si->player_version = 1; // (will be set later)
11557 si->use_api_server = TRUE;
11558 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11559 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11560 si->ask_for_uploading_tapes = TRUE;
11561 si->ask_for_remaining_tapes = FALSE;
11562 si->provide_uploading_tapes = TRUE;
11563 si->ask_for_using_api_server = TRUE;
11564 si->has_remaining_tapes = FALSE;
11567 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11569 si->editor_cascade.el_bd = TRUE;
11570 si->editor_cascade.el_bd_native = TRUE;
11571 si->editor_cascade.el_bd_effects = FALSE;
11572 si->editor_cascade.el_em = TRUE;
11573 si->editor_cascade.el_emc = TRUE;
11574 si->editor_cascade.el_rnd = TRUE;
11575 si->editor_cascade.el_sb = TRUE;
11576 si->editor_cascade.el_sp = TRUE;
11577 si->editor_cascade.el_dc = TRUE;
11578 si->editor_cascade.el_dx = TRUE;
11580 si->editor_cascade.el_mm = TRUE;
11581 si->editor_cascade.el_df = TRUE;
11583 si->editor_cascade.el_chars = FALSE;
11584 si->editor_cascade.el_steel_chars = FALSE;
11585 si->editor_cascade.el_ce = FALSE;
11586 si->editor_cascade.el_ge = FALSE;
11587 si->editor_cascade.el_es = FALSE;
11588 si->editor_cascade.el_ref = FALSE;
11589 si->editor_cascade.el_user = FALSE;
11590 si->editor_cascade.el_dynamic = FALSE;
11593 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11595 static char *getHideSetupToken(void *setup_value)
11597 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11599 if (setup_value != NULL)
11600 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11602 return hide_setup_token;
11605 void setHideSetupEntry(void *setup_value)
11607 char *hide_setup_token = getHideSetupToken(setup_value);
11609 if (hide_setup_hash == NULL)
11610 hide_setup_hash = newSetupFileHash();
11612 if (setup_value != NULL)
11613 setHashEntry(hide_setup_hash, hide_setup_token, "");
11616 void removeHideSetupEntry(void *setup_value)
11618 char *hide_setup_token = getHideSetupToken(setup_value);
11620 if (setup_value != NULL)
11621 removeHashEntry(hide_setup_hash, hide_setup_token);
11624 boolean hideSetupEntry(void *setup_value)
11626 char *hide_setup_token = getHideSetupToken(setup_value);
11628 return (setup_value != NULL &&
11629 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11632 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11633 struct TokenInfo *token_info,
11634 int token_nr, char *token_text)
11636 char *token_hide_text = getStringCat2(token_text, ".hide");
11637 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11639 // set the value of this setup option in the setup option structure
11640 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11642 // check if this setup option should be hidden in the setup menu
11643 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11644 setHideSetupEntry(token_info[token_nr].value);
11646 free(token_hide_text);
11649 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11650 struct TokenInfo *token_info,
11653 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11654 token_info[token_nr].text);
11657 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11661 if (!setup_file_hash)
11664 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11665 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11667 setup.touch.grid_initialized = TRUE;
11668 for (i = 0; i < 2; i++)
11670 int grid_xsize = setup.touch.grid_xsize[i];
11671 int grid_ysize = setup.touch.grid_ysize[i];
11674 // if virtual buttons are not loaded from setup file, repeat initializing
11675 // virtual buttons grid with default values later when video is initialized
11676 if (grid_xsize == -1 ||
11679 setup.touch.grid_initialized = FALSE;
11684 for (y = 0; y < grid_ysize; y++)
11686 char token_string[MAX_LINE_LEN];
11688 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11690 char *value_string = getHashEntry(setup_file_hash, token_string);
11692 if (value_string == NULL)
11695 for (x = 0; x < grid_xsize; x++)
11697 char c = value_string[x];
11699 setup.touch.grid_button[i][x][y] =
11700 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11705 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11706 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11708 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11709 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11711 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11715 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11717 setup_input = setup.input[pnr];
11718 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11720 char full_token[100];
11722 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11723 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11726 setup.input[pnr] = setup_input;
11729 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11730 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11732 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11733 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11735 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11736 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11738 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11739 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11741 setHideRelatedSetupEntries();
11744 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11748 if (!setup_file_hash)
11751 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11752 setSetupInfo(auto_setup_tokens, i,
11753 getHashEntry(setup_file_hash,
11754 auto_setup_tokens[i].text));
11757 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11761 if (!setup_file_hash)
11764 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11765 setSetupInfo(server_setup_tokens, i,
11766 getHashEntry(setup_file_hash,
11767 server_setup_tokens[i].text));
11770 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11774 if (!setup_file_hash)
11777 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11778 setSetupInfo(editor_cascade_setup_tokens, i,
11779 getHashEntry(setup_file_hash,
11780 editor_cascade_setup_tokens[i].text));
11783 void LoadUserNames(void)
11785 int last_user_nr = user.nr;
11788 if (global.user_names != NULL)
11790 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11791 checked_free(global.user_names[i]);
11793 checked_free(global.user_names);
11796 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11798 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11802 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11804 if (setup_file_hash)
11806 char *player_name = getHashEntry(setup_file_hash, "player_name");
11808 global.user_names[i] = getFixedUserName(player_name);
11810 freeSetupFileHash(setup_file_hash);
11813 if (global.user_names[i] == NULL)
11814 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11817 user.nr = last_user_nr;
11820 void LoadSetupFromFilename(char *filename)
11822 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11824 if (setup_file_hash)
11826 decodeSetupFileHash_Default(setup_file_hash);
11828 freeSetupFileHash(setup_file_hash);
11832 Debug("setup", "using default setup values");
11836 static void LoadSetup_SpecialPostProcessing(void)
11838 char *player_name_new;
11840 // needed to work around problems with fixed length strings
11841 player_name_new = getFixedUserName(setup.player_name);
11842 free(setup.player_name);
11843 setup.player_name = player_name_new;
11845 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11846 if (setup.scroll_delay == FALSE)
11848 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11849 setup.scroll_delay = TRUE; // now always "on"
11852 // make sure that scroll delay value stays inside valid range
11853 setup.scroll_delay_value =
11854 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11857 void LoadSetup_Default(void)
11861 // always start with reliable default values
11862 setSetupInfoToDefaults(&setup);
11864 // try to load setup values from default setup file
11865 filename = getDefaultSetupFilename();
11867 if (fileExists(filename))
11868 LoadSetupFromFilename(filename);
11870 // try to load setup values from platform setup file
11871 filename = getPlatformSetupFilename();
11873 if (fileExists(filename))
11874 LoadSetupFromFilename(filename);
11876 // try to load setup values from user setup file
11877 filename = getSetupFilename();
11879 LoadSetupFromFilename(filename);
11881 LoadSetup_SpecialPostProcessing();
11884 void LoadSetup_AutoSetup(void)
11886 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11887 SetupFileHash *setup_file_hash = NULL;
11889 // always start with reliable default values
11890 setSetupInfoToDefaults_AutoSetup(&setup);
11892 setup_file_hash = loadSetupFileHash(filename);
11894 if (setup_file_hash)
11896 decodeSetupFileHash_AutoSetup(setup_file_hash);
11898 freeSetupFileHash(setup_file_hash);
11904 void LoadSetup_ServerSetup(void)
11906 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11907 SetupFileHash *setup_file_hash = NULL;
11909 // always start with reliable default values
11910 setSetupInfoToDefaults_ServerSetup(&setup);
11912 setup_file_hash = loadSetupFileHash(filename);
11914 if (setup_file_hash)
11916 decodeSetupFileHash_ServerSetup(setup_file_hash);
11918 freeSetupFileHash(setup_file_hash);
11923 if (setup.player_uuid == NULL)
11925 // player UUID does not yet exist in setup file
11926 setup.player_uuid = getStringCopy(getUUID());
11927 setup.player_version = 2;
11929 SaveSetup_ServerSetup();
11933 void LoadSetup_EditorCascade(void)
11935 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11936 SetupFileHash *setup_file_hash = NULL;
11938 // always start with reliable default values
11939 setSetupInfoToDefaults_EditorCascade(&setup);
11941 setup_file_hash = loadSetupFileHash(filename);
11943 if (setup_file_hash)
11945 decodeSetupFileHash_EditorCascade(setup_file_hash);
11947 freeSetupFileHash(setup_file_hash);
11953 void LoadSetup(void)
11955 LoadSetup_Default();
11956 LoadSetup_AutoSetup();
11957 LoadSetup_ServerSetup();
11958 LoadSetup_EditorCascade();
11961 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11962 char *mapping_line)
11964 char mapping_guid[MAX_LINE_LEN];
11965 char *mapping_start, *mapping_end;
11967 // get GUID from game controller mapping line: copy complete line
11968 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11969 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11971 // get GUID from game controller mapping line: cut after GUID part
11972 mapping_start = strchr(mapping_guid, ',');
11973 if (mapping_start != NULL)
11974 *mapping_start = '\0';
11976 // cut newline from game controller mapping line
11977 mapping_end = strchr(mapping_line, '\n');
11978 if (mapping_end != NULL)
11979 *mapping_end = '\0';
11981 // add mapping entry to game controller mappings hash
11982 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11985 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11990 if (!(file = fopen(filename, MODE_READ)))
11992 Warn("cannot read game controller mappings file '%s'", filename);
11997 while (!feof(file))
11999 char line[MAX_LINE_LEN];
12001 if (!fgets(line, MAX_LINE_LEN, file))
12004 addGameControllerMappingToHash(mappings_hash, line);
12010 void SaveSetup_Default(void)
12012 char *filename = getSetupFilename();
12016 InitUserDataDirectory();
12018 if (!(file = fopen(filename, MODE_WRITE)))
12020 Warn("cannot write setup file '%s'", filename);
12025 fprintFileHeader(file, SETUP_FILENAME);
12027 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12029 // just to make things nicer :)
12030 if (global_setup_tokens[i].value == &setup.multiple_users ||
12031 global_setup_tokens[i].value == &setup.sound ||
12032 global_setup_tokens[i].value == &setup.graphics_set ||
12033 global_setup_tokens[i].value == &setup.volume_simple ||
12034 global_setup_tokens[i].value == &setup.network_mode ||
12035 global_setup_tokens[i].value == &setup.touch.control_type ||
12036 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12037 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12038 fprintf(file, "\n");
12040 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12043 for (i = 0; i < 2; i++)
12045 int grid_xsize = setup.touch.grid_xsize[i];
12046 int grid_ysize = setup.touch.grid_ysize[i];
12049 fprintf(file, "\n");
12051 for (y = 0; y < grid_ysize; y++)
12053 char token_string[MAX_LINE_LEN];
12054 char value_string[MAX_LINE_LEN];
12056 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12058 for (x = 0; x < grid_xsize; x++)
12060 char c = setup.touch.grid_button[i][x][y];
12062 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12065 value_string[grid_xsize] = '\0';
12067 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12071 fprintf(file, "\n");
12072 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12073 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12075 fprintf(file, "\n");
12076 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12077 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12079 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12083 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12084 fprintf(file, "\n");
12086 setup_input = setup.input[pnr];
12087 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12088 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12091 fprintf(file, "\n");
12092 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12093 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12095 // (internal setup values not saved to user setup file)
12097 fprintf(file, "\n");
12098 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12099 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12100 setup.debug.xsn_mode != AUTO)
12101 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12103 fprintf(file, "\n");
12104 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12105 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12109 SetFilePermissions(filename, PERMS_PRIVATE);
12112 void SaveSetup_AutoSetup(void)
12114 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12118 InitUserDataDirectory();
12120 if (!(file = fopen(filename, MODE_WRITE)))
12122 Warn("cannot write auto setup file '%s'", filename);
12129 fprintFileHeader(file, AUTOSETUP_FILENAME);
12131 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12132 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12136 SetFilePermissions(filename, PERMS_PRIVATE);
12141 void SaveSetup_ServerSetup(void)
12143 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12147 InitUserDataDirectory();
12149 if (!(file = fopen(filename, MODE_WRITE)))
12151 Warn("cannot write server setup file '%s'", filename);
12158 fprintFileHeader(file, SERVERSETUP_FILENAME);
12160 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12162 // just to make things nicer :)
12163 if (server_setup_tokens[i].value == &setup.use_api_server)
12164 fprintf(file, "\n");
12166 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12171 SetFilePermissions(filename, PERMS_PRIVATE);
12176 void SaveSetup_EditorCascade(void)
12178 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12182 InitUserDataDirectory();
12184 if (!(file = fopen(filename, MODE_WRITE)))
12186 Warn("cannot write editor cascade state file '%s'", filename);
12193 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12195 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12196 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12200 SetFilePermissions(filename, PERMS_PRIVATE);
12205 void SaveSetup(void)
12207 SaveSetup_Default();
12208 SaveSetup_AutoSetup();
12209 SaveSetup_ServerSetup();
12210 SaveSetup_EditorCascade();
12213 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12218 if (!(file = fopen(filename, MODE_WRITE)))
12220 Warn("cannot write game controller mappings file '%s'", filename);
12225 BEGIN_HASH_ITERATION(mappings_hash, itr)
12227 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12229 END_HASH_ITERATION(mappings_hash, itr)
12234 void SaveSetup_AddGameControllerMapping(char *mapping)
12236 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12237 SetupFileHash *mappings_hash = newSetupFileHash();
12239 InitUserDataDirectory();
12241 // load existing personal game controller mappings
12242 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12244 // add new mapping to personal game controller mappings
12245 addGameControllerMappingToHash(mappings_hash, mapping);
12247 // save updated personal game controller mappings
12248 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12250 freeSetupFileHash(mappings_hash);
12254 void LoadCustomElementDescriptions(void)
12256 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12257 SetupFileHash *setup_file_hash;
12260 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12262 if (element_info[i].custom_description != NULL)
12264 free(element_info[i].custom_description);
12265 element_info[i].custom_description = NULL;
12269 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12272 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12274 char *token = getStringCat2(element_info[i].token_name, ".name");
12275 char *value = getHashEntry(setup_file_hash, token);
12278 element_info[i].custom_description = getStringCopy(value);
12283 freeSetupFileHash(setup_file_hash);
12286 static int getElementFromToken(char *token)
12288 char *value = getHashEntry(element_token_hash, token);
12291 return atoi(value);
12293 Warn("unknown element token '%s'", token);
12295 return EL_UNDEFINED;
12298 void FreeGlobalAnimEventInfo(void)
12300 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12302 if (gaei->event_list == NULL)
12307 for (i = 0; i < gaei->num_event_lists; i++)
12309 checked_free(gaei->event_list[i]->event_value);
12310 checked_free(gaei->event_list[i]);
12313 checked_free(gaei->event_list);
12315 gaei->event_list = NULL;
12316 gaei->num_event_lists = 0;
12319 static int AddGlobalAnimEventList(void)
12321 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12322 int list_pos = gaei->num_event_lists++;
12324 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12325 sizeof(struct GlobalAnimEventListInfo *));
12327 gaei->event_list[list_pos] =
12328 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12330 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12332 gaeli->event_value = NULL;
12333 gaeli->num_event_values = 0;
12338 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12340 // do not add empty global animation events
12341 if (event_value == ANIM_EVENT_NONE)
12344 // if list position is undefined, create new list
12345 if (list_pos == ANIM_EVENT_UNDEFINED)
12346 list_pos = AddGlobalAnimEventList();
12348 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12349 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12350 int value_pos = gaeli->num_event_values++;
12352 gaeli->event_value = checked_realloc(gaeli->event_value,
12353 gaeli->num_event_values * sizeof(int *));
12355 gaeli->event_value[value_pos] = event_value;
12360 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12362 if (list_pos == ANIM_EVENT_UNDEFINED)
12363 return ANIM_EVENT_NONE;
12365 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12366 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12368 return gaeli->event_value[value_pos];
12371 int GetGlobalAnimEventValueCount(int list_pos)
12373 if (list_pos == ANIM_EVENT_UNDEFINED)
12376 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12377 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12379 return gaeli->num_event_values;
12382 // This function checks if a string <s> of the format "string1, string2, ..."
12383 // exactly contains a string <s_contained>.
12385 static boolean string_has_parameter(char *s, char *s_contained)
12389 if (s == NULL || s_contained == NULL)
12392 if (strlen(s_contained) > strlen(s))
12395 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12397 char next_char = s[strlen(s_contained)];
12399 // check if next character is delimiter or whitespace
12400 if (next_char == ',' || next_char == '\0' ||
12401 next_char == ' ' || next_char == '\t')
12405 // check if string contains another parameter string after a comma
12406 substring = strchr(s, ',');
12407 if (substring == NULL) // string does not contain a comma
12410 // advance string pointer to next character after the comma
12413 // skip potential whitespaces after the comma
12414 while (*substring == ' ' || *substring == '\t')
12417 return string_has_parameter(substring, s_contained);
12420 static int get_anim_parameter_value_ce(char *s)
12423 char *pattern_1 = "ce_change:custom_";
12424 char *pattern_2 = ".page_";
12425 int pattern_1_len = strlen(pattern_1);
12426 char *matching_char = strstr(s_ptr, pattern_1);
12427 int result = ANIM_EVENT_NONE;
12429 if (matching_char == NULL)
12430 return ANIM_EVENT_NONE;
12432 result = ANIM_EVENT_CE_CHANGE;
12434 s_ptr = matching_char + pattern_1_len;
12436 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12437 if (*s_ptr >= '0' && *s_ptr <= '9')
12439 int gic_ce_nr = (*s_ptr++ - '0');
12441 if (*s_ptr >= '0' && *s_ptr <= '9')
12443 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12445 if (*s_ptr >= '0' && *s_ptr <= '9')
12446 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12449 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12450 return ANIM_EVENT_NONE;
12452 // custom element stored as 0 to 255
12455 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12459 // invalid custom element number specified
12461 return ANIM_EVENT_NONE;
12464 // check for change page number ("page_X" or "page_XX") (optional)
12465 if (strPrefix(s_ptr, pattern_2))
12467 s_ptr += strlen(pattern_2);
12469 if (*s_ptr >= '0' && *s_ptr <= '9')
12471 int gic_page_nr = (*s_ptr++ - '0');
12473 if (*s_ptr >= '0' && *s_ptr <= '9')
12474 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12476 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12477 return ANIM_EVENT_NONE;
12479 // change page stored as 1 to 32 (0 means "all change pages")
12481 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12485 // invalid animation part number specified
12487 return ANIM_EVENT_NONE;
12491 // discard result if next character is neither delimiter nor whitespace
12492 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12493 *s_ptr == ' ' || *s_ptr == '\t'))
12494 return ANIM_EVENT_NONE;
12499 static int get_anim_parameter_value(char *s)
12501 int event_value[] =
12509 char *pattern_1[] =
12517 char *pattern_2 = ".part_";
12518 char *matching_char = NULL;
12520 int pattern_1_len = 0;
12521 int result = ANIM_EVENT_NONE;
12524 result = get_anim_parameter_value_ce(s);
12526 if (result != ANIM_EVENT_NONE)
12529 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12531 matching_char = strstr(s_ptr, pattern_1[i]);
12532 pattern_1_len = strlen(pattern_1[i]);
12533 result = event_value[i];
12535 if (matching_char != NULL)
12539 if (matching_char == NULL)
12540 return ANIM_EVENT_NONE;
12542 s_ptr = matching_char + pattern_1_len;
12544 // check for main animation number ("anim_X" or "anim_XX")
12545 if (*s_ptr >= '0' && *s_ptr <= '9')
12547 int gic_anim_nr = (*s_ptr++ - '0');
12549 if (*s_ptr >= '0' && *s_ptr <= '9')
12550 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12552 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12553 return ANIM_EVENT_NONE;
12555 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12559 // invalid main animation number specified
12561 return ANIM_EVENT_NONE;
12564 // check for animation part number ("part_X" or "part_XX") (optional)
12565 if (strPrefix(s_ptr, pattern_2))
12567 s_ptr += strlen(pattern_2);
12569 if (*s_ptr >= '0' && *s_ptr <= '9')
12571 int gic_part_nr = (*s_ptr++ - '0');
12573 if (*s_ptr >= '0' && *s_ptr <= '9')
12574 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12576 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12577 return ANIM_EVENT_NONE;
12579 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12583 // invalid animation part number specified
12585 return ANIM_EVENT_NONE;
12589 // discard result if next character is neither delimiter nor whitespace
12590 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12591 *s_ptr == ' ' || *s_ptr == '\t'))
12592 return ANIM_EVENT_NONE;
12597 static int get_anim_parameter_values(char *s)
12599 int list_pos = ANIM_EVENT_UNDEFINED;
12600 int event_value = ANIM_EVENT_DEFAULT;
12602 if (string_has_parameter(s, "any"))
12603 event_value |= ANIM_EVENT_ANY;
12605 if (string_has_parameter(s, "click:self") ||
12606 string_has_parameter(s, "click") ||
12607 string_has_parameter(s, "self"))
12608 event_value |= ANIM_EVENT_SELF;
12610 if (string_has_parameter(s, "unclick:any"))
12611 event_value |= ANIM_EVENT_UNCLICK_ANY;
12613 // if animation event found, add it to global animation event list
12614 if (event_value != ANIM_EVENT_NONE)
12615 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12619 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12620 event_value = get_anim_parameter_value(s);
12622 // if animation event found, add it to global animation event list
12623 if (event_value != ANIM_EVENT_NONE)
12624 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12626 // continue with next part of the string, starting with next comma
12627 s = strchr(s + 1, ',');
12633 static int get_anim_action_parameter_value(char *token)
12635 // check most common default case first to massively speed things up
12636 if (strEqual(token, ARG_UNDEFINED))
12637 return ANIM_EVENT_ACTION_NONE;
12639 int result = getImageIDFromToken(token);
12643 char *gfx_token = getStringCat2("gfx.", token);
12645 result = getImageIDFromToken(gfx_token);
12647 checked_free(gfx_token);
12652 Key key = getKeyFromX11KeyName(token);
12654 if (key != KSYM_UNDEFINED)
12655 result = -(int)key;
12662 result = get_hash_from_string(token); // unsigned int => int
12663 result = ABS(result); // may be negative now
12664 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12666 setHashEntry(anim_url_hash, int2str(result, 0), token);
12671 result = ANIM_EVENT_ACTION_NONE;
12676 int get_parameter_value(char *value_raw, char *suffix, int type)
12678 char *value = getStringToLower(value_raw);
12679 int result = 0; // probably a save default value
12681 if (strEqual(suffix, ".direction"))
12683 result = (strEqual(value, "left") ? MV_LEFT :
12684 strEqual(value, "right") ? MV_RIGHT :
12685 strEqual(value, "up") ? MV_UP :
12686 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12688 else if (strEqual(suffix, ".position"))
12690 result = (strEqual(value, "left") ? POS_LEFT :
12691 strEqual(value, "right") ? POS_RIGHT :
12692 strEqual(value, "top") ? POS_TOP :
12693 strEqual(value, "upper") ? POS_UPPER :
12694 strEqual(value, "middle") ? POS_MIDDLE :
12695 strEqual(value, "lower") ? POS_LOWER :
12696 strEqual(value, "bottom") ? POS_BOTTOM :
12697 strEqual(value, "any") ? POS_ANY :
12698 strEqual(value, "ce") ? POS_CE :
12699 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12700 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12702 else if (strEqual(suffix, ".align"))
12704 result = (strEqual(value, "left") ? ALIGN_LEFT :
12705 strEqual(value, "right") ? ALIGN_RIGHT :
12706 strEqual(value, "center") ? ALIGN_CENTER :
12707 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12709 else if (strEqual(suffix, ".valign"))
12711 result = (strEqual(value, "top") ? VALIGN_TOP :
12712 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12713 strEqual(value, "middle") ? VALIGN_MIDDLE :
12714 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12716 else if (strEqual(suffix, ".anim_mode"))
12718 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12719 string_has_parameter(value, "loop") ? ANIM_LOOP :
12720 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12721 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12722 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12723 string_has_parameter(value, "random") ? ANIM_RANDOM :
12724 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12725 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12726 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12727 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12728 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12729 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12730 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12731 string_has_parameter(value, "all") ? ANIM_ALL :
12732 string_has_parameter(value, "tiled") ? ANIM_TILED :
12733 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12736 if (string_has_parameter(value, "once"))
12737 result |= ANIM_ONCE;
12739 if (string_has_parameter(value, "reverse"))
12740 result |= ANIM_REVERSE;
12742 if (string_has_parameter(value, "opaque_player"))
12743 result |= ANIM_OPAQUE_PLAYER;
12745 if (string_has_parameter(value, "static_panel"))
12746 result |= ANIM_STATIC_PANEL;
12748 else if (strEqual(suffix, ".init_event") ||
12749 strEqual(suffix, ".anim_event"))
12751 result = get_anim_parameter_values(value);
12753 else if (strEqual(suffix, ".init_delay_action") ||
12754 strEqual(suffix, ".anim_delay_action") ||
12755 strEqual(suffix, ".post_delay_action") ||
12756 strEqual(suffix, ".init_event_action") ||
12757 strEqual(suffix, ".anim_event_action"))
12759 result = get_anim_action_parameter_value(value_raw);
12761 else if (strEqual(suffix, ".class"))
12763 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12764 get_hash_from_string(value));
12766 else if (strEqual(suffix, ".style"))
12768 result = STYLE_DEFAULT;
12770 if (string_has_parameter(value, "accurate_borders"))
12771 result |= STYLE_ACCURATE_BORDERS;
12773 if (string_has_parameter(value, "inner_corners"))
12774 result |= STYLE_INNER_CORNERS;
12776 if (string_has_parameter(value, "reverse"))
12777 result |= STYLE_REVERSE;
12779 if (string_has_parameter(value, "leftmost_position"))
12780 result |= STYLE_LEFTMOST_POSITION;
12782 if (string_has_parameter(value, "block_clicks"))
12783 result |= STYLE_BLOCK;
12785 if (string_has_parameter(value, "passthrough_clicks"))
12786 result |= STYLE_PASSTHROUGH;
12788 if (string_has_parameter(value, "multiple_actions"))
12789 result |= STYLE_MULTIPLE_ACTIONS;
12791 if (string_has_parameter(value, "consume_ce_event"))
12792 result |= STYLE_CONSUME_CE_EVENT;
12794 else if (strEqual(suffix, ".fade_mode"))
12796 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12797 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12798 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12799 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12800 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12801 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12802 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12803 FADE_MODE_DEFAULT);
12805 else if (strEqual(suffix, ".auto_delay_unit"))
12807 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12808 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12809 AUTO_DELAY_UNIT_DEFAULT);
12811 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12813 result = gfx.get_font_from_token_function(value);
12815 else // generic parameter of type integer or boolean
12817 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12818 type == TYPE_INTEGER ? get_integer_from_string(value) :
12819 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12820 ARG_UNDEFINED_VALUE);
12828 static int get_token_parameter_value(char *token, char *value_raw)
12832 if (token == NULL || value_raw == NULL)
12833 return ARG_UNDEFINED_VALUE;
12835 suffix = strrchr(token, '.');
12836 if (suffix == NULL)
12839 if (strEqual(suffix, ".element"))
12840 return getElementFromToken(value_raw);
12842 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12843 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12846 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12847 boolean ignore_defaults)
12851 for (i = 0; image_config_vars[i].token != NULL; i++)
12853 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12855 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12856 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12860 *image_config_vars[i].value =
12861 get_token_parameter_value(image_config_vars[i].token, value);
12865 void InitMenuDesignSettings_Static(void)
12867 // always start with reliable default values from static default config
12868 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12871 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12875 // the following initializes hierarchical values from static configuration
12877 // special case: initialize "ARG_DEFAULT" values in static default config
12878 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12879 titlescreen_initial_first_default.fade_mode =
12880 title_initial_first_default.fade_mode;
12881 titlescreen_initial_first_default.fade_delay =
12882 title_initial_first_default.fade_delay;
12883 titlescreen_initial_first_default.post_delay =
12884 title_initial_first_default.post_delay;
12885 titlescreen_initial_first_default.auto_delay =
12886 title_initial_first_default.auto_delay;
12887 titlescreen_initial_first_default.auto_delay_unit =
12888 title_initial_first_default.auto_delay_unit;
12889 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12890 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12891 titlescreen_first_default.post_delay = title_first_default.post_delay;
12892 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12893 titlescreen_first_default.auto_delay_unit =
12894 title_first_default.auto_delay_unit;
12895 titlemessage_initial_first_default.fade_mode =
12896 title_initial_first_default.fade_mode;
12897 titlemessage_initial_first_default.fade_delay =
12898 title_initial_first_default.fade_delay;
12899 titlemessage_initial_first_default.post_delay =
12900 title_initial_first_default.post_delay;
12901 titlemessage_initial_first_default.auto_delay =
12902 title_initial_first_default.auto_delay;
12903 titlemessage_initial_first_default.auto_delay_unit =
12904 title_initial_first_default.auto_delay_unit;
12905 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12906 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12907 titlemessage_first_default.post_delay = title_first_default.post_delay;
12908 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12909 titlemessage_first_default.auto_delay_unit =
12910 title_first_default.auto_delay_unit;
12912 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12913 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12914 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12915 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12916 titlescreen_initial_default.auto_delay_unit =
12917 title_initial_default.auto_delay_unit;
12918 titlescreen_default.fade_mode = title_default.fade_mode;
12919 titlescreen_default.fade_delay = title_default.fade_delay;
12920 titlescreen_default.post_delay = title_default.post_delay;
12921 titlescreen_default.auto_delay = title_default.auto_delay;
12922 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12923 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12924 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12925 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12926 titlemessage_initial_default.auto_delay_unit =
12927 title_initial_default.auto_delay_unit;
12928 titlemessage_default.fade_mode = title_default.fade_mode;
12929 titlemessage_default.fade_delay = title_default.fade_delay;
12930 titlemessage_default.post_delay = title_default.post_delay;
12931 titlemessage_default.auto_delay = title_default.auto_delay;
12932 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12934 // special case: initialize "ARG_DEFAULT" values in static default config
12935 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12936 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12938 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12939 titlescreen_first[i] = titlescreen_first_default;
12940 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12941 titlemessage_first[i] = titlemessage_first_default;
12943 titlescreen_initial[i] = titlescreen_initial_default;
12944 titlescreen[i] = titlescreen_default;
12945 titlemessage_initial[i] = titlemessage_initial_default;
12946 titlemessage[i] = titlemessage_default;
12949 // special case: initialize "ARG_DEFAULT" values in static default config
12950 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12951 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12953 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12956 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12957 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12958 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12961 // special case: initialize "ARG_DEFAULT" values in static default config
12962 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12963 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12965 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12966 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12967 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12969 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12972 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12976 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12980 struct XY *dst, *src;
12982 game_buttons_xy[] =
12984 { &game.button.save, &game.button.stop },
12985 { &game.button.pause2, &game.button.pause },
12986 { &game.button.load, &game.button.play },
12987 { &game.button.undo, &game.button.stop },
12988 { &game.button.redo, &game.button.play },
12994 // special case: initialize later added SETUP list size from LEVELS value
12995 if (menu.list_size[GAME_MODE_SETUP] == -1)
12996 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12998 // set default position for snapshot buttons to stop/pause/play buttons
12999 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13000 if ((*game_buttons_xy[i].dst).x == -1 &&
13001 (*game_buttons_xy[i].dst).y == -1)
13002 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13004 // --------------------------------------------------------------------------
13005 // dynamic viewports (including playfield margins, borders and alignments)
13006 // --------------------------------------------------------------------------
13008 // dynamic viewports currently only supported for landscape mode
13009 int display_width = MAX(video.display_width, video.display_height);
13010 int display_height = MIN(video.display_width, video.display_height);
13012 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13014 struct RectWithBorder *vp_window = &viewport.window[i];
13015 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13016 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13017 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13018 boolean dynamic_window_width = (vp_window->min_width != -1);
13019 boolean dynamic_window_height = (vp_window->min_height != -1);
13020 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13021 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13023 // adjust window size if min/max width/height is specified
13025 if (vp_window->min_width != -1)
13027 int window_width = display_width;
13029 // when using static window height, use aspect ratio of display
13030 if (vp_window->min_height == -1)
13031 window_width = vp_window->height * display_width / display_height;
13033 vp_window->width = MAX(vp_window->min_width, window_width);
13036 if (vp_window->min_height != -1)
13038 int window_height = display_height;
13040 // when using static window width, use aspect ratio of display
13041 if (vp_window->min_width == -1)
13042 window_height = vp_window->width * display_height / display_width;
13044 vp_window->height = MAX(vp_window->min_height, window_height);
13047 if (vp_window->max_width != -1)
13048 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13050 if (vp_window->max_height != -1)
13051 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13053 int playfield_width = vp_window->width;
13054 int playfield_height = vp_window->height;
13056 // adjust playfield size and position according to specified margins
13058 playfield_width -= vp_playfield->margin_left;
13059 playfield_width -= vp_playfield->margin_right;
13061 playfield_height -= vp_playfield->margin_top;
13062 playfield_height -= vp_playfield->margin_bottom;
13064 // adjust playfield size if min/max width/height is specified
13066 if (vp_playfield->min_width != -1)
13067 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13069 if (vp_playfield->min_height != -1)
13070 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13072 if (vp_playfield->max_width != -1)
13073 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13075 if (vp_playfield->max_height != -1)
13076 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13078 // adjust playfield position according to specified alignment
13080 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13081 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13082 else if (vp_playfield->align == ALIGN_CENTER)
13083 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13084 else if (vp_playfield->align == ALIGN_RIGHT)
13085 vp_playfield->x += playfield_width - vp_playfield->width;
13087 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13088 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13089 else if (vp_playfield->valign == VALIGN_MIDDLE)
13090 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13091 else if (vp_playfield->valign == VALIGN_BOTTOM)
13092 vp_playfield->y += playfield_height - vp_playfield->height;
13094 vp_playfield->x += vp_playfield->margin_left;
13095 vp_playfield->y += vp_playfield->margin_top;
13097 // adjust individual playfield borders if only default border is specified
13099 if (vp_playfield->border_left == -1)
13100 vp_playfield->border_left = vp_playfield->border_size;
13101 if (vp_playfield->border_right == -1)
13102 vp_playfield->border_right = vp_playfield->border_size;
13103 if (vp_playfield->border_top == -1)
13104 vp_playfield->border_top = vp_playfield->border_size;
13105 if (vp_playfield->border_bottom == -1)
13106 vp_playfield->border_bottom = vp_playfield->border_size;
13108 // set dynamic playfield borders if borders are specified as undefined
13109 // (but only if window size was dynamic and playfield size was static)
13111 if (dynamic_window_width && !dynamic_playfield_width)
13113 if (vp_playfield->border_left == -1)
13115 vp_playfield->border_left = (vp_playfield->x -
13116 vp_playfield->margin_left);
13117 vp_playfield->x -= vp_playfield->border_left;
13118 vp_playfield->width += vp_playfield->border_left;
13121 if (vp_playfield->border_right == -1)
13123 vp_playfield->border_right = (vp_window->width -
13125 vp_playfield->width -
13126 vp_playfield->margin_right);
13127 vp_playfield->width += vp_playfield->border_right;
13131 if (dynamic_window_height && !dynamic_playfield_height)
13133 if (vp_playfield->border_top == -1)
13135 vp_playfield->border_top = (vp_playfield->y -
13136 vp_playfield->margin_top);
13137 vp_playfield->y -= vp_playfield->border_top;
13138 vp_playfield->height += vp_playfield->border_top;
13141 if (vp_playfield->border_bottom == -1)
13143 vp_playfield->border_bottom = (vp_window->height -
13145 vp_playfield->height -
13146 vp_playfield->margin_bottom);
13147 vp_playfield->height += vp_playfield->border_bottom;
13151 // adjust playfield size to be a multiple of a defined alignment tile size
13153 int align_size = vp_playfield->align_size;
13154 int playfield_xtiles = vp_playfield->width / align_size;
13155 int playfield_ytiles = vp_playfield->height / align_size;
13156 int playfield_width_corrected = playfield_xtiles * align_size;
13157 int playfield_height_corrected = playfield_ytiles * align_size;
13158 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13159 i == GFX_SPECIAL_ARG_EDITOR);
13161 if (is_playfield_mode &&
13162 dynamic_playfield_width &&
13163 vp_playfield->width != playfield_width_corrected)
13165 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13167 vp_playfield->width = playfield_width_corrected;
13169 if (vp_playfield->align == ALIGN_LEFT)
13171 vp_playfield->border_left += playfield_xdiff;
13173 else if (vp_playfield->align == ALIGN_RIGHT)
13175 vp_playfield->border_right += playfield_xdiff;
13177 else if (vp_playfield->align == ALIGN_CENTER)
13179 int border_left_diff = playfield_xdiff / 2;
13180 int border_right_diff = playfield_xdiff - border_left_diff;
13182 vp_playfield->border_left += border_left_diff;
13183 vp_playfield->border_right += border_right_diff;
13187 if (is_playfield_mode &&
13188 dynamic_playfield_height &&
13189 vp_playfield->height != playfield_height_corrected)
13191 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13193 vp_playfield->height = playfield_height_corrected;
13195 if (vp_playfield->valign == VALIGN_TOP)
13197 vp_playfield->border_top += playfield_ydiff;
13199 else if (vp_playfield->align == VALIGN_BOTTOM)
13201 vp_playfield->border_right += playfield_ydiff;
13203 else if (vp_playfield->align == VALIGN_MIDDLE)
13205 int border_top_diff = playfield_ydiff / 2;
13206 int border_bottom_diff = playfield_ydiff - border_top_diff;
13208 vp_playfield->border_top += border_top_diff;
13209 vp_playfield->border_bottom += border_bottom_diff;
13213 // adjust door positions according to specified alignment
13215 for (j = 0; j < 2; j++)
13217 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13219 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13220 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13221 else if (vp_door->align == ALIGN_CENTER)
13222 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13223 else if (vp_door->align == ALIGN_RIGHT)
13224 vp_door->x += vp_window->width - vp_door->width;
13226 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13227 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13228 else if (vp_door->valign == VALIGN_MIDDLE)
13229 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13230 else if (vp_door->valign == VALIGN_BOTTOM)
13231 vp_door->y += vp_window->height - vp_door->height;
13236 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13240 struct XYTileSize *dst, *src;
13243 editor_buttons_xy[] =
13246 &editor.button.element_left, &editor.palette.element_left,
13247 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13250 &editor.button.element_middle, &editor.palette.element_middle,
13251 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13254 &editor.button.element_right, &editor.palette.element_right,
13255 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13262 // set default position for element buttons to element graphics
13263 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13265 if ((*editor_buttons_xy[i].dst).x == -1 &&
13266 (*editor_buttons_xy[i].dst).y == -1)
13268 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13270 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13272 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13276 // adjust editor palette rows and columns if specified to be dynamic
13278 if (editor.palette.cols == -1)
13280 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13281 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13282 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13284 editor.palette.cols = (vp_width - sc_width) / bt_width;
13286 if (editor.palette.x == -1)
13288 int palette_width = editor.palette.cols * bt_width + sc_width;
13290 editor.palette.x = (vp_width - palette_width) / 2;
13294 if (editor.palette.rows == -1)
13296 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13297 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13298 int tx_height = getFontHeight(FONT_TEXT_2);
13300 editor.palette.rows = (vp_height - tx_height) / bt_height;
13302 if (editor.palette.y == -1)
13304 int palette_height = editor.palette.rows * bt_height + tx_height;
13306 editor.palette.y = (vp_height - palette_height) / 2;
13311 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13312 boolean initialize)
13314 // special case: check if network and preview player positions are redefined,
13315 // to compare this later against the main menu level preview being redefined
13316 struct TokenIntPtrInfo menu_config_players[] =
13318 { "main.network_players.x", &menu.main.network_players.redefined },
13319 { "main.network_players.y", &menu.main.network_players.redefined },
13320 { "main.preview_players.x", &menu.main.preview_players.redefined },
13321 { "main.preview_players.y", &menu.main.preview_players.redefined },
13322 { "preview.x", &preview.redefined },
13323 { "preview.y", &preview.redefined }
13329 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13330 *menu_config_players[i].value = FALSE;
13334 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13335 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13336 *menu_config_players[i].value = TRUE;
13340 static void InitMenuDesignSettings_PreviewPlayers(void)
13342 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13345 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13347 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13350 static void LoadMenuDesignSettingsFromFilename(char *filename)
13352 static struct TitleFadingInfo tfi;
13353 static struct TitleMessageInfo tmi;
13354 static struct TokenInfo title_tokens[] =
13356 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13357 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13358 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13359 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13360 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13364 static struct TokenInfo titlemessage_tokens[] =
13366 { TYPE_INTEGER, &tmi.x, ".x" },
13367 { TYPE_INTEGER, &tmi.y, ".y" },
13368 { TYPE_INTEGER, &tmi.width, ".width" },
13369 { TYPE_INTEGER, &tmi.height, ".height" },
13370 { TYPE_INTEGER, &tmi.chars, ".chars" },
13371 { TYPE_INTEGER, &tmi.lines, ".lines" },
13372 { TYPE_INTEGER, &tmi.align, ".align" },
13373 { TYPE_INTEGER, &tmi.valign, ".valign" },
13374 { TYPE_INTEGER, &tmi.font, ".font" },
13375 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13376 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13377 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13378 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13379 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13380 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13381 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13382 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13383 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13389 struct TitleFadingInfo *info;
13394 // initialize first titles from "enter screen" definitions, if defined
13395 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13396 { &title_first_default, "menu.enter_screen.TITLE" },
13398 // initialize title screens from "next screen" definitions, if defined
13399 { &title_initial_default, "menu.next_screen.TITLE" },
13400 { &title_default, "menu.next_screen.TITLE" },
13406 struct TitleMessageInfo *array;
13409 titlemessage_arrays[] =
13411 // initialize first titles from "enter screen" definitions, if defined
13412 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13413 { titlescreen_first, "menu.enter_screen.TITLE" },
13414 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13415 { titlemessage_first, "menu.enter_screen.TITLE" },
13417 // initialize titles from "next screen" definitions, if defined
13418 { titlescreen_initial, "menu.next_screen.TITLE" },
13419 { titlescreen, "menu.next_screen.TITLE" },
13420 { titlemessage_initial, "menu.next_screen.TITLE" },
13421 { titlemessage, "menu.next_screen.TITLE" },
13423 // overwrite titles with title definitions, if defined
13424 { titlescreen_initial_first, "[title_initial]" },
13425 { titlescreen_first, "[title]" },
13426 { titlemessage_initial_first, "[title_initial]" },
13427 { titlemessage_first, "[title]" },
13429 { titlescreen_initial, "[title_initial]" },
13430 { titlescreen, "[title]" },
13431 { titlemessage_initial, "[title_initial]" },
13432 { titlemessage, "[title]" },
13434 // overwrite titles with title screen/message definitions, if defined
13435 { titlescreen_initial_first, "[titlescreen_initial]" },
13436 { titlescreen_first, "[titlescreen]" },
13437 { titlemessage_initial_first, "[titlemessage_initial]" },
13438 { titlemessage_first, "[titlemessage]" },
13440 { titlescreen_initial, "[titlescreen_initial]" },
13441 { titlescreen, "[titlescreen]" },
13442 { titlemessage_initial, "[titlemessage_initial]" },
13443 { titlemessage, "[titlemessage]" },
13447 SetupFileHash *setup_file_hash;
13450 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13453 // the following initializes hierarchical values from dynamic configuration
13455 // special case: initialize with default values that may be overwritten
13456 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13457 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13459 struct TokenIntPtrInfo menu_config[] =
13461 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13462 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13463 { "menu.list_size", &menu.list_size[i] }
13466 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13468 char *token = menu_config[j].token;
13469 char *value = getHashEntry(setup_file_hash, token);
13472 *menu_config[j].value = get_integer_from_string(value);
13476 // special case: initialize with default values that may be overwritten
13477 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13478 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13480 struct TokenIntPtrInfo menu_config[] =
13482 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13483 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13484 { "menu.list_size.INFO", &menu.list_size_info[i] },
13485 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13486 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13489 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13491 char *token = menu_config[j].token;
13492 char *value = getHashEntry(setup_file_hash, token);
13495 *menu_config[j].value = get_integer_from_string(value);
13499 // special case: initialize with default values that may be overwritten
13500 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13501 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13503 struct TokenIntPtrInfo menu_config[] =
13505 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13506 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13509 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13511 char *token = menu_config[j].token;
13512 char *value = getHashEntry(setup_file_hash, token);
13515 *menu_config[j].value = get_integer_from_string(value);
13519 // special case: initialize with default values that may be overwritten
13520 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13521 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13523 struct TokenIntPtrInfo menu_config[] =
13525 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13526 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13527 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13528 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13529 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13530 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13531 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13532 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13533 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13534 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13537 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13539 char *token = menu_config[j].token;
13540 char *value = getHashEntry(setup_file_hash, token);
13543 *menu_config[j].value = get_integer_from_string(value);
13547 // special case: initialize with default values that may be overwritten
13548 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13549 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13551 struct TokenIntPtrInfo menu_config[] =
13553 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13554 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13555 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13556 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13557 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13558 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13559 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13560 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13561 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
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_token_parameter_value(token, value);
13574 // special case: initialize with default values that may be overwritten
13575 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13576 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13580 char *token_prefix;
13581 struct RectWithBorder *struct_ptr;
13585 { "viewport.window", &viewport.window[i] },
13586 { "viewport.playfield", &viewport.playfield[i] },
13587 { "viewport.door_1", &viewport.door_1[i] },
13588 { "viewport.door_2", &viewport.door_2[i] }
13591 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13593 struct TokenIntPtrInfo vp_config[] =
13595 { ".x", &vp_struct[j].struct_ptr->x },
13596 { ".y", &vp_struct[j].struct_ptr->y },
13597 { ".width", &vp_struct[j].struct_ptr->width },
13598 { ".height", &vp_struct[j].struct_ptr->height },
13599 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13600 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13601 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13602 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13603 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13604 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13605 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13606 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13607 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13608 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13609 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13610 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13611 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13612 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13613 { ".align", &vp_struct[j].struct_ptr->align },
13614 { ".valign", &vp_struct[j].struct_ptr->valign }
13617 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13619 char *token = getStringCat2(vp_struct[j].token_prefix,
13620 vp_config[k].token);
13621 char *value = getHashEntry(setup_file_hash, token);
13624 *vp_config[k].value = get_token_parameter_value(token, value);
13631 // special case: initialize with default values that may be overwritten
13632 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13633 for (i = 0; title_info[i].info != NULL; i++)
13635 struct TitleFadingInfo *info = title_info[i].info;
13636 char *base_token = title_info[i].text;
13638 for (j = 0; title_tokens[j].type != -1; j++)
13640 char *token = getStringCat2(base_token, title_tokens[j].text);
13641 char *value = getHashEntry(setup_file_hash, token);
13645 int parameter_value = get_token_parameter_value(token, value);
13649 *(int *)title_tokens[j].value = (int)parameter_value;
13658 // special case: initialize with default values that may be overwritten
13659 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13660 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13662 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13663 char *base_token = titlemessage_arrays[i].text;
13665 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13667 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13668 char *value = getHashEntry(setup_file_hash, token);
13672 int parameter_value = get_token_parameter_value(token, value);
13674 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13678 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13679 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13681 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13691 // read (and overwrite with) values that may be specified in config file
13692 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13694 // special case: check if network and preview player positions are redefined
13695 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13697 freeSetupFileHash(setup_file_hash);
13700 void LoadMenuDesignSettings(void)
13702 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13704 InitMenuDesignSettings_Static();
13705 InitMenuDesignSettings_SpecialPreProcessing();
13706 InitMenuDesignSettings_PreviewPlayers();
13708 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13710 // first look for special settings configured in level series config
13711 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13713 if (fileExists(filename_base))
13714 LoadMenuDesignSettingsFromFilename(filename_base);
13717 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13719 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13720 LoadMenuDesignSettingsFromFilename(filename_local);
13722 InitMenuDesignSettings_SpecialPostProcessing();
13725 void LoadMenuDesignSettings_AfterGraphics(void)
13727 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13730 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13731 boolean ignore_defaults)
13735 for (i = 0; sound_config_vars[i].token != NULL; i++)
13737 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13739 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13740 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13744 *sound_config_vars[i].value =
13745 get_token_parameter_value(sound_config_vars[i].token, value);
13749 void InitSoundSettings_Static(void)
13751 // always start with reliable default values from static default config
13752 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13755 static void LoadSoundSettingsFromFilename(char *filename)
13757 SetupFileHash *setup_file_hash;
13759 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13762 // read (and overwrite with) values that may be specified in config file
13763 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13765 freeSetupFileHash(setup_file_hash);
13768 void LoadSoundSettings(void)
13770 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13772 InitSoundSettings_Static();
13774 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13776 // first look for special settings configured in level series config
13777 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13779 if (fileExists(filename_base))
13780 LoadSoundSettingsFromFilename(filename_base);
13783 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13785 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13786 LoadSoundSettingsFromFilename(filename_local);
13789 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13791 char *filename = getEditorSetupFilename();
13792 SetupFileList *setup_file_list, *list;
13793 SetupFileHash *element_hash;
13794 int num_unknown_tokens = 0;
13797 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13800 element_hash = newSetupFileHash();
13802 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13803 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13805 // determined size may be larger than needed (due to unknown elements)
13807 for (list = setup_file_list; list != NULL; list = list->next)
13810 // add space for up to 3 more elements for padding that may be needed
13811 *num_elements += 3;
13813 // free memory for old list of elements, if needed
13814 checked_free(*elements);
13816 // allocate memory for new list of elements
13817 *elements = checked_malloc(*num_elements * sizeof(int));
13820 for (list = setup_file_list; list != NULL; list = list->next)
13822 char *value = getHashEntry(element_hash, list->token);
13824 if (value == NULL) // try to find obsolete token mapping
13826 char *mapped_token = get_mapped_token(list->token);
13828 if (mapped_token != NULL)
13830 value = getHashEntry(element_hash, mapped_token);
13832 free(mapped_token);
13838 (*elements)[(*num_elements)++] = atoi(value);
13842 if (num_unknown_tokens == 0)
13845 Warn("unknown token(s) found in config file:");
13846 Warn("- config file: '%s'", filename);
13848 num_unknown_tokens++;
13851 Warn("- token: '%s'", list->token);
13855 if (num_unknown_tokens > 0)
13858 while (*num_elements % 4) // pad with empty elements, if needed
13859 (*elements)[(*num_elements)++] = EL_EMPTY;
13861 freeSetupFileList(setup_file_list);
13862 freeSetupFileHash(element_hash);
13865 for (i = 0; i < *num_elements; i++)
13866 Debug("editor", "element '%s' [%d]\n",
13867 element_info[(*elements)[i]].token_name, (*elements)[i]);
13871 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13874 SetupFileHash *setup_file_hash = NULL;
13875 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13876 char *filename_music, *filename_prefix, *filename_info;
13882 token_to_value_ptr[] =
13884 { "title_header", &tmp_music_file_info.title_header },
13885 { "artist_header", &tmp_music_file_info.artist_header },
13886 { "album_header", &tmp_music_file_info.album_header },
13887 { "year_header", &tmp_music_file_info.year_header },
13888 { "played_header", &tmp_music_file_info.played_header },
13890 { "title", &tmp_music_file_info.title },
13891 { "artist", &tmp_music_file_info.artist },
13892 { "album", &tmp_music_file_info.album },
13893 { "year", &tmp_music_file_info.year },
13894 { "played", &tmp_music_file_info.played },
13900 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13901 getCustomMusicFilename(basename));
13903 if (filename_music == NULL)
13906 // ---------- try to replace file extension ----------
13908 filename_prefix = getStringCopy(filename_music);
13909 if (strrchr(filename_prefix, '.') != NULL)
13910 *strrchr(filename_prefix, '.') = '\0';
13911 filename_info = getStringCat2(filename_prefix, ".txt");
13913 if (fileExists(filename_info))
13914 setup_file_hash = loadSetupFileHash(filename_info);
13916 free(filename_prefix);
13917 free(filename_info);
13919 if (setup_file_hash == NULL)
13921 // ---------- try to add file extension ----------
13923 filename_prefix = getStringCopy(filename_music);
13924 filename_info = getStringCat2(filename_prefix, ".txt");
13926 if (fileExists(filename_info))
13927 setup_file_hash = loadSetupFileHash(filename_info);
13929 free(filename_prefix);
13930 free(filename_info);
13933 if (setup_file_hash == NULL)
13936 // ---------- music file info found ----------
13938 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13940 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13942 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13944 *token_to_value_ptr[i].value_ptr =
13945 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13948 tmp_music_file_info.basename = getStringCopy(basename);
13949 tmp_music_file_info.music = music;
13950 tmp_music_file_info.is_sound = is_sound;
13952 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13953 *new_music_file_info = tmp_music_file_info;
13955 return new_music_file_info;
13958 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13960 return get_music_file_info_ext(basename, music, FALSE);
13963 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13965 return get_music_file_info_ext(basename, sound, TRUE);
13968 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13969 char *basename, boolean is_sound)
13971 for (; list != NULL; list = list->next)
13972 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13978 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13980 return music_info_listed_ext(list, basename, FALSE);
13983 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13985 return music_info_listed_ext(list, basename, TRUE);
13988 void LoadMusicInfo(void)
13990 int num_music_noconf = getMusicListSize_NoConf();
13991 int num_music = getMusicListSize();
13992 int num_sounds = getSoundListSize();
13993 struct FileInfo *music, *sound;
13994 struct MusicFileInfo *next, **new;
13998 while (music_file_info != NULL)
14000 next = music_file_info->next;
14002 checked_free(music_file_info->basename);
14004 checked_free(music_file_info->title_header);
14005 checked_free(music_file_info->artist_header);
14006 checked_free(music_file_info->album_header);
14007 checked_free(music_file_info->year_header);
14008 checked_free(music_file_info->played_header);
14010 checked_free(music_file_info->title);
14011 checked_free(music_file_info->artist);
14012 checked_free(music_file_info->album);
14013 checked_free(music_file_info->year);
14014 checked_free(music_file_info->played);
14016 free(music_file_info);
14018 music_file_info = next;
14021 new = &music_file_info;
14023 // get (configured or unconfigured) music file info for all levels
14024 for (i = leveldir_current->first_level;
14025 i <= leveldir_current->last_level; i++)
14029 if (levelset.music[i] != MUS_UNDEFINED)
14031 // get music file info for configured level music
14032 music_nr = levelset.music[i];
14034 else if (num_music_noconf > 0)
14036 // get music file info for unconfigured level music
14037 int level_pos = i - leveldir_current->first_level;
14039 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14046 char *basename = getMusicInfoEntryFilename(music_nr);
14048 if (basename == NULL)
14051 if (!music_info_listed(music_file_info, basename))
14053 *new = get_music_file_info(basename, music_nr);
14056 new = &(*new)->next;
14060 // get music file info for all remaining configured music files
14061 for (i = 0; i < num_music; i++)
14063 music = getMusicListEntry(i);
14065 if (music->filename == NULL)
14068 if (strEqual(music->filename, UNDEFINED_FILENAME))
14071 // a configured file may be not recognized as music
14072 if (!FileIsMusic(music->filename))
14075 if (!music_info_listed(music_file_info, music->filename))
14077 *new = get_music_file_info(music->filename, i);
14080 new = &(*new)->next;
14084 // get sound file info for all configured sound files
14085 for (i = 0; i < num_sounds; i++)
14087 sound = getSoundListEntry(i);
14089 if (sound->filename == NULL)
14092 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14095 // a configured file may be not recognized as sound
14096 if (!FileIsSound(sound->filename))
14099 if (!sound_info_listed(music_file_info, sound->filename))
14101 *new = get_sound_file_info(sound->filename, i);
14103 new = &(*new)->next;
14107 // add pointers to previous list nodes
14109 struct MusicFileInfo *node = music_file_info;
14111 while (node != NULL)
14114 node->next->prev = node;
14120 static void add_helpanim_entry(int element, int action, int direction,
14121 int delay, int *num_list_entries)
14123 struct HelpAnimInfo *new_list_entry;
14124 (*num_list_entries)++;
14127 checked_realloc(helpanim_info,
14128 *num_list_entries * sizeof(struct HelpAnimInfo));
14129 new_list_entry = &helpanim_info[*num_list_entries - 1];
14131 new_list_entry->element = element;
14132 new_list_entry->action = action;
14133 new_list_entry->direction = direction;
14134 new_list_entry->delay = delay;
14137 static void print_unknown_token(char *filename, char *token, int token_nr)
14142 Warn("unknown token(s) found in config file:");
14143 Warn("- config file: '%s'", filename);
14146 Warn("- token: '%s'", token);
14149 static void print_unknown_token_end(int token_nr)
14155 void LoadHelpAnimInfo(void)
14157 char *filename = getHelpAnimFilename();
14158 SetupFileList *setup_file_list = NULL, *list;
14159 SetupFileHash *element_hash, *action_hash, *direction_hash;
14160 int num_list_entries = 0;
14161 int num_unknown_tokens = 0;
14164 if (fileExists(filename))
14165 setup_file_list = loadSetupFileList(filename);
14167 if (setup_file_list == NULL)
14169 // use reliable default values from static configuration
14170 SetupFileList *insert_ptr;
14172 insert_ptr = setup_file_list =
14173 newSetupFileList(helpanim_config[0].token,
14174 helpanim_config[0].value);
14176 for (i = 1; helpanim_config[i].token; i++)
14177 insert_ptr = addListEntry(insert_ptr,
14178 helpanim_config[i].token,
14179 helpanim_config[i].value);
14182 element_hash = newSetupFileHash();
14183 action_hash = newSetupFileHash();
14184 direction_hash = newSetupFileHash();
14186 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14187 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14189 for (i = 0; i < NUM_ACTIONS; i++)
14190 setHashEntry(action_hash, element_action_info[i].suffix,
14191 i_to_a(element_action_info[i].value));
14193 // do not store direction index (bit) here, but direction value!
14194 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14195 setHashEntry(direction_hash, element_direction_info[i].suffix,
14196 i_to_a(1 << element_direction_info[i].value));
14198 for (list = setup_file_list; list != NULL; list = list->next)
14200 char *element_token, *action_token, *direction_token;
14201 char *element_value, *action_value, *direction_value;
14202 int delay = atoi(list->value);
14204 if (strEqual(list->token, "end"))
14206 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14211 /* first try to break element into element/action/direction parts;
14212 if this does not work, also accept combined "element[.act][.dir]"
14213 elements (like "dynamite.active"), which are unique elements */
14215 if (strchr(list->token, '.') == NULL) // token contains no '.'
14217 element_value = getHashEntry(element_hash, list->token);
14218 if (element_value != NULL) // element found
14219 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14220 &num_list_entries);
14223 // no further suffixes found -- this is not an element
14224 print_unknown_token(filename, list->token, num_unknown_tokens++);
14230 // token has format "<prefix>.<something>"
14232 action_token = strchr(list->token, '.'); // suffix may be action ...
14233 direction_token = action_token; // ... or direction
14235 element_token = getStringCopy(list->token);
14236 *strchr(element_token, '.') = '\0';
14238 element_value = getHashEntry(element_hash, element_token);
14240 if (element_value == NULL) // this is no element
14242 element_value = getHashEntry(element_hash, list->token);
14243 if (element_value != NULL) // combined element found
14244 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14245 &num_list_entries);
14247 print_unknown_token(filename, list->token, num_unknown_tokens++);
14249 free(element_token);
14254 action_value = getHashEntry(action_hash, action_token);
14256 if (action_value != NULL) // action found
14258 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14259 &num_list_entries);
14261 free(element_token);
14266 direction_value = getHashEntry(direction_hash, direction_token);
14268 if (direction_value != NULL) // direction found
14270 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14271 &num_list_entries);
14273 free(element_token);
14278 if (strchr(action_token + 1, '.') == NULL)
14280 // no further suffixes found -- this is not an action nor direction
14282 element_value = getHashEntry(element_hash, list->token);
14283 if (element_value != NULL) // combined element found
14284 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14285 &num_list_entries);
14287 print_unknown_token(filename, list->token, num_unknown_tokens++);
14289 free(element_token);
14294 // token has format "<prefix>.<suffix>.<something>"
14296 direction_token = strchr(action_token + 1, '.');
14298 action_token = getStringCopy(action_token);
14299 *strchr(action_token + 1, '.') = '\0';
14301 action_value = getHashEntry(action_hash, action_token);
14303 if (action_value == NULL) // this is no action
14305 element_value = getHashEntry(element_hash, list->token);
14306 if (element_value != NULL) // combined element found
14307 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14308 &num_list_entries);
14310 print_unknown_token(filename, list->token, num_unknown_tokens++);
14312 free(element_token);
14313 free(action_token);
14318 direction_value = getHashEntry(direction_hash, direction_token);
14320 if (direction_value != NULL) // direction found
14322 add_helpanim_entry(atoi(element_value), atoi(action_value),
14323 atoi(direction_value), delay, &num_list_entries);
14325 free(element_token);
14326 free(action_token);
14331 // this is no direction
14333 element_value = getHashEntry(element_hash, list->token);
14334 if (element_value != NULL) // combined element found
14335 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14336 &num_list_entries);
14338 print_unknown_token(filename, list->token, num_unknown_tokens++);
14340 free(element_token);
14341 free(action_token);
14344 print_unknown_token_end(num_unknown_tokens);
14346 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14347 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14349 freeSetupFileList(setup_file_list);
14350 freeSetupFileHash(element_hash);
14351 freeSetupFileHash(action_hash);
14352 freeSetupFileHash(direction_hash);
14355 for (i = 0; i < num_list_entries; i++)
14356 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14357 EL_NAME(helpanim_info[i].element),
14358 helpanim_info[i].element,
14359 helpanim_info[i].action,
14360 helpanim_info[i].direction,
14361 helpanim_info[i].delay);
14365 void LoadHelpTextInfo(void)
14367 char *filename = getHelpTextFilename();
14370 if (helptext_info != NULL)
14372 freeSetupFileHash(helptext_info);
14373 helptext_info = NULL;
14376 if (fileExists(filename))
14377 helptext_info = loadSetupFileHash(filename);
14379 if (helptext_info == NULL)
14381 // use reliable default values from static configuration
14382 helptext_info = newSetupFileHash();
14384 for (i = 0; helptext_config[i].token; i++)
14385 setHashEntry(helptext_info,
14386 helptext_config[i].token,
14387 helptext_config[i].value);
14391 BEGIN_HASH_ITERATION(helptext_info, itr)
14393 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14394 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14396 END_HASH_ITERATION(hash, itr)
14401 // ----------------------------------------------------------------------------
14403 // ----------------------------------------------------------------------------
14405 #define MAX_NUM_CONVERT_LEVELS 1000
14407 void ConvertLevels(void)
14409 static LevelDirTree *convert_leveldir = NULL;
14410 static int convert_level_nr = -1;
14411 static int num_levels_handled = 0;
14412 static int num_levels_converted = 0;
14413 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14416 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14417 global.convert_leveldir);
14419 if (convert_leveldir == NULL)
14420 Fail("no such level identifier: '%s'", global.convert_leveldir);
14422 leveldir_current = convert_leveldir;
14424 if (global.convert_level_nr != -1)
14426 convert_leveldir->first_level = global.convert_level_nr;
14427 convert_leveldir->last_level = global.convert_level_nr;
14430 convert_level_nr = convert_leveldir->first_level;
14432 PrintLine("=", 79);
14433 Print("Converting levels\n");
14434 PrintLine("-", 79);
14435 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14436 Print("Level series name: '%s'\n", convert_leveldir->name);
14437 Print("Level series author: '%s'\n", convert_leveldir->author);
14438 Print("Number of levels: %d\n", convert_leveldir->levels);
14439 PrintLine("=", 79);
14442 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14443 levels_failed[i] = FALSE;
14445 while (convert_level_nr <= convert_leveldir->last_level)
14447 char *level_filename;
14450 level_nr = convert_level_nr++;
14452 Print("Level %03d: ", level_nr);
14454 LoadLevel(level_nr);
14455 if (level.no_level_file || level.no_valid_file)
14457 Print("(no level)\n");
14461 Print("converting level ... ");
14464 // special case: conversion of some EMC levels as requested by ACME
14465 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14468 level_filename = getDefaultLevelFilename(level_nr);
14469 new_level = !fileExists(level_filename);
14473 SaveLevel(level_nr);
14475 num_levels_converted++;
14477 Print("converted.\n");
14481 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14482 levels_failed[level_nr] = TRUE;
14484 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14487 num_levels_handled++;
14491 PrintLine("=", 79);
14492 Print("Number of levels handled: %d\n", num_levels_handled);
14493 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14494 (num_levels_handled ?
14495 num_levels_converted * 100 / num_levels_handled : 0));
14496 PrintLine("-", 79);
14497 Print("Summary (for automatic parsing by scripts):\n");
14498 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14499 convert_leveldir->identifier, num_levels_converted,
14500 num_levels_handled,
14501 (num_levels_handled ?
14502 num_levels_converted * 100 / num_levels_handled : 0));
14504 if (num_levels_handled != num_levels_converted)
14506 Print(", FAILED:");
14507 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14508 if (levels_failed[i])
14513 PrintLine("=", 79);
14515 CloseAllAndExit(0);
14519 // ----------------------------------------------------------------------------
14520 // create and save images for use in level sketches (raw BMP format)
14521 // ----------------------------------------------------------------------------
14523 void CreateLevelSketchImages(void)
14529 InitElementPropertiesGfxElement();
14531 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14532 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14534 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14536 int element = getMappedElement(i);
14537 char basename1[16];
14538 char basename2[16];
14542 sprintf(basename1, "%04d.bmp", i);
14543 sprintf(basename2, "%04ds.bmp", i);
14545 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14546 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14548 DrawSizedElement(0, 0, element, TILESIZE);
14549 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14551 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14552 Fail("cannot save level sketch image file '%s'", filename1);
14554 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14555 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14557 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14558 Fail("cannot save level sketch image file '%s'", filename2);
14563 // create corresponding SQL statements (for normal and small images)
14566 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14567 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14570 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14571 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14573 // optional: create content for forum level sketch demonstration post
14575 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14578 FreeBitmap(bitmap1);
14579 FreeBitmap(bitmap2);
14582 fprintf(stderr, "\n");
14584 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14586 CloseAllAndExit(0);
14590 // ----------------------------------------------------------------------------
14591 // create and save images for element collecting animations (raw BMP format)
14592 // ----------------------------------------------------------------------------
14594 static boolean createCollectImage(int element)
14596 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14599 void CreateCollectElementImages(void)
14603 int anim_frames = num_steps - 1;
14604 int tile_size = TILESIZE;
14605 int anim_width = tile_size * anim_frames;
14606 int anim_height = tile_size;
14607 int num_collect_images = 0;
14608 int pos_collect_images = 0;
14610 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14611 if (createCollectImage(i))
14612 num_collect_images++;
14614 Info("Creating %d element collecting animation images ...",
14615 num_collect_images);
14617 int dst_width = anim_width * 2;
14618 int dst_height = anim_height * num_collect_images / 2;
14619 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14620 char *basename_bmp = "RocksCollect.bmp";
14621 char *basename_png = "RocksCollect.png";
14622 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14623 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14624 int len_filename_bmp = strlen(filename_bmp);
14625 int len_filename_png = strlen(filename_png);
14626 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14627 char cmd_convert[max_command_len];
14629 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14633 // force using RGBA surface for destination bitmap
14634 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14635 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14637 dst_bitmap->surface =
14638 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14640 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14642 if (!createCollectImage(i))
14645 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14646 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14647 int graphic = el2img(i);
14648 char *token_name = element_info[i].token_name;
14649 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14650 Bitmap *src_bitmap;
14653 Info("- creating collecting image for '%s' ...", token_name);
14655 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14657 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14658 tile_size, tile_size, 0, 0);
14660 // force using RGBA surface for temporary bitmap (using transparent black)
14661 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14662 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14664 tmp_bitmap->surface =
14665 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14667 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14669 for (j = 0; j < anim_frames; j++)
14671 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14672 int frame_size = frame_size_final * num_steps;
14673 int offset = (tile_size - frame_size_final) / 2;
14674 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14676 while (frame_size > frame_size_final)
14680 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14682 FreeBitmap(frame_bitmap);
14684 frame_bitmap = half_bitmap;
14687 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14688 frame_size_final, frame_size_final,
14689 dst_x + j * tile_size + offset, dst_y + offset);
14691 FreeBitmap(frame_bitmap);
14694 tmp_bitmap->surface_masked = NULL;
14696 FreeBitmap(tmp_bitmap);
14698 pos_collect_images++;
14701 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14702 Fail("cannot save element collecting image file '%s'", filename_bmp);
14704 FreeBitmap(dst_bitmap);
14706 Info("Converting image file from BMP to PNG ...");
14708 if (system(cmd_convert) != 0)
14709 Fail("converting image file failed");
14711 unlink(filename_bmp);
14715 CloseAllAndExit(0);
14719 // ----------------------------------------------------------------------------
14720 // create and save images for custom and group elements (raw BMP format)
14721 // ----------------------------------------------------------------------------
14723 void CreateCustomElementImages(char *directory)
14725 char *src_basename = "RocksCE-template.ilbm";
14726 char *dst_basename = "RocksCE.bmp";
14727 char *src_filename = getPath2(directory, src_basename);
14728 char *dst_filename = getPath2(directory, dst_basename);
14729 Bitmap *src_bitmap;
14731 int yoffset_ce = 0;
14732 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14735 InitVideoDefaults();
14737 ReCreateBitmap(&backbuffer, video.width, video.height);
14739 src_bitmap = LoadImage(src_filename);
14741 bitmap = CreateBitmap(TILEX * 16 * 2,
14742 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14745 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14752 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14753 TILEX * x, TILEY * y + yoffset_ce);
14755 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14757 TILEX * x + TILEX * 16,
14758 TILEY * y + yoffset_ce);
14760 for (j = 2; j >= 0; j--)
14764 BlitBitmap(src_bitmap, bitmap,
14765 TILEX + c * 7, 0, 6, 10,
14766 TILEX * x + 6 + j * 7,
14767 TILEY * y + 11 + yoffset_ce);
14769 BlitBitmap(src_bitmap, bitmap,
14770 TILEX + c * 8, TILEY, 6, 10,
14771 TILEX * 16 + TILEX * x + 6 + j * 8,
14772 TILEY * y + 10 + yoffset_ce);
14778 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14785 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14786 TILEX * x, TILEY * y + yoffset_ge);
14788 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14790 TILEX * x + TILEX * 16,
14791 TILEY * y + yoffset_ge);
14793 for (j = 1; j >= 0; j--)
14797 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14798 TILEX * x + 6 + j * 10,
14799 TILEY * y + 11 + yoffset_ge);
14801 BlitBitmap(src_bitmap, bitmap,
14802 TILEX + c * 8, TILEY + 12, 6, 10,
14803 TILEX * 16 + TILEX * x + 10 + j * 8,
14804 TILEY * y + 10 + yoffset_ge);
14810 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14811 Fail("cannot save CE graphics file '%s'", dst_filename);
14813 FreeBitmap(bitmap);
14815 CloseAllAndExit(0);