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
817 EL_BD_REPLICATOR, -1,
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
819 &li.bd_replicators_active, TRUE
822 EL_BD_REPLICATOR, -1,
823 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
824 &li.bd_replicator_create_delay, 4
828 EL_BD_CONVEYOR_LEFT, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
830 &li.bd_conveyor_belts_active, TRUE
833 EL_BD_CONVEYOR_LEFT, -1,
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
835 &li.bd_conveyor_belts_changed, FALSE
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
841 &li.bd_water_cannot_flow_down, FALSE
846 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
847 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
851 EL_BD_PNEUMATIC_HAMMER, -1,
852 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
853 &li.bd_hammer_walls_break_delay, 5
856 EL_BD_PNEUMATIC_HAMMER, -1,
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
858 &li.bd_hammer_walls_reappear, FALSE
861 EL_BD_PNEUMATIC_HAMMER, -1,
862 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
863 &li.bd_hammer_walls_reappear_delay, 100
868 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
869 &li.bd_num_skeletons_needed_for_pot, 5
873 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
874 &li.bd_skeleton_worth_num_diamonds, 0
877 // (the following values are related to various game elements)
881 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
882 &li.score[SC_EMERALD], 10
887 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
888 &li.score[SC_DIAMOND], 10
893 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
894 &li.score[SC_BUG], 10
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
900 &li.score[SC_SPACESHIP], 10
905 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
906 &li.score[SC_PACMAN], 10
911 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
912 &li.score[SC_NUT], 10
917 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
918 &li.score[SC_DYNAMITE], 10
923 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
924 &li.score[SC_KEY], 10
929 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
930 &li.score[SC_PEARL], 10
935 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
936 &li.score[SC_CRYSTAL], 10
939 // (amoeba values used by R'n'D game engine only)
942 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
943 &li.amoeba_content, EL_DIAMOND
947 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
953 &li.grow_into_diggable, TRUE
955 // (amoeba values used by BD game engine only)
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
959 &li.bd_amoeba_wait_for_hatching, FALSE
963 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
964 &li.bd_amoeba_start_immediately, TRUE
968 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
969 &li.bd_amoeba_2_explode_by_amoeba, TRUE
973 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
974 &li.bd_amoeba_threshold_too_big, 200
978 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
979 &li.bd_amoeba_slow_growth_time, 200
983 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
984 &li.bd_amoeba_slow_growth_rate, 3
988 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
989 &li.bd_amoeba_fast_growth_rate, 25
993 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
994 &li.bd_amoeba_content_too_big, EL_BD_ROCK
998 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
999 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1004 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1005 &li.bd_amoeba_2_threshold_too_big, 200
1009 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1010 &li.bd_amoeba_2_slow_growth_time, 200
1014 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1015 &li.bd_amoeba_2_slow_growth_rate, 3
1019 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1020 &li.bd_amoeba_2_fast_growth_rate, 25
1024 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1025 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1029 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1030 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1034 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1035 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1039 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1040 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1045 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1046 &li.yamyam_content, EL_ROCK, NULL,
1047 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1052 &li.score[SC_YAMYAM], 10
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.score[SC_ROBOT], 10
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.time_magic_wall, 10
1079 EL_GAME_OF_LIFE, -1,
1080 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1081 &li.game_of_life[0], 2
1084 EL_GAME_OF_LIFE, -1,
1085 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1086 &li.game_of_life[1], 3
1089 EL_GAME_OF_LIFE, -1,
1090 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1091 &li.game_of_life[2], 3
1094 EL_GAME_OF_LIFE, -1,
1095 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1096 &li.game_of_life[3], 3
1099 EL_GAME_OF_LIFE, -1,
1100 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1101 &li.use_life_bugs, FALSE
1106 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1111 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1116 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1121 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1126 EL_TIMEGATE_SWITCH, -1,
1127 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1128 &li.time_timegate, 10
1132 EL_LIGHT_SWITCH_ACTIVE, -1,
1133 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1138 EL_SHIELD_NORMAL, -1,
1139 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1140 &li.shield_normal_time, 10
1143 EL_SHIELD_NORMAL, -1,
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1145 &li.score[SC_SHIELD], 10
1149 EL_SHIELD_DEADLY, -1,
1150 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1151 &li.shield_deadly_time, 10
1154 EL_SHIELD_DEADLY, -1,
1155 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1156 &li.score[SC_SHIELD], 10
1161 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1167 &li.extra_time_score, 10
1171 EL_TIME_ORB_FULL, -1,
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1173 &li.time_orb_time, 10
1176 EL_TIME_ORB_FULL, -1,
1177 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1178 &li.use_time_orb_bug, FALSE
1183 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1184 &li.use_spring_bug, FALSE
1189 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1190 &li.android_move_time, 10
1194 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1195 &li.android_clone_time, 10
1198 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1199 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1200 &li.android_clone_element[0], EL_EMPTY, NULL,
1201 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1205 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1206 &li.android_clone_element[0], EL_EMPTY, NULL,
1207 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1212 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1213 &li.lenses_score, 10
1217 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1222 EL_EMC_MAGNIFIER, -1,
1223 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1224 &li.magnify_score, 10
1227 EL_EMC_MAGNIFIER, -1,
1228 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1229 &li.magnify_time, 10
1233 EL_EMC_MAGIC_BALL, -1,
1234 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1238 EL_EMC_MAGIC_BALL, -1,
1239 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1240 &li.ball_random, FALSE
1243 EL_EMC_MAGIC_BALL, -1,
1244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1245 &li.ball_active_initial, FALSE
1248 EL_EMC_MAGIC_BALL, -1,
1249 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1250 &li.ball_content, EL_EMPTY, NULL,
1251 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1255 EL_SOKOBAN_FIELD_EMPTY, -1,
1256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1257 &li.sb_fields_needed, TRUE
1261 EL_SOKOBAN_OBJECT, -1,
1262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1263 &li.sb_objects_needed, TRUE
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1269 &li.mm_laser_red, FALSE
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1274 &li.mm_laser_green, FALSE
1278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1279 &li.mm_laser_blue, TRUE
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1285 &li.df_laser_red, TRUE
1289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1290 &li.df_laser_green, TRUE
1294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1295 &li.df_laser_blue, FALSE
1299 EL_MM_FUSE_ACTIVE, -1,
1300 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1301 &li.mm_time_fuse, 25
1305 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1306 &li.mm_time_bomb, 75
1310 EL_MM_GRAY_BALL, -1,
1311 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1312 &li.mm_time_ball, 75
1315 EL_MM_GRAY_BALL, -1,
1316 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1317 &li.mm_ball_choice_mode, ANIM_RANDOM
1320 EL_MM_GRAY_BALL, -1,
1321 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1322 &li.mm_ball_content, EL_EMPTY, NULL,
1323 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1326 EL_MM_GRAY_BALL, -1,
1327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1328 &li.rotate_mm_ball_content, TRUE
1331 EL_MM_GRAY_BALL, -1,
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1333 &li.explode_mm_ball, FALSE
1337 EL_MM_STEEL_BLOCK, -1,
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1339 &li.mm_time_block, 75
1342 EL_MM_LIGHTBALL, -1,
1343 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1344 &li.score[SC_ELEM_BONUS], 10
1354 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1358 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1359 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1363 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1364 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1370 &xx_envelope.autowrap, FALSE
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1375 &xx_envelope.centered, FALSE
1380 TYPE_STRING, CONF_VALUE_BYTES(1),
1381 &xx_envelope.text, -1, NULL,
1382 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1383 &xx_default_string_empty[0]
1393 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1397 TYPE_STRING, CONF_VALUE_BYTES(1),
1398 &xx_ei.description[0], -1,
1399 &yy_ei.description[0],
1400 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1401 &xx_default_description[0]
1406 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1407 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1408 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1410 #if ENABLE_RESERVED_CODE
1411 // (reserved for later use)
1414 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1415 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1416 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1423 &xx_ei.use_gfx_element, FALSE,
1424 &yy_ei.use_gfx_element
1428 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1429 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1430 &yy_ei.gfx_element_initial
1435 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1436 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1437 &yy_ei.access_direction
1442 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1443 &xx_ei.collect_score_initial, 10,
1444 &yy_ei.collect_score_initial
1448 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1449 &xx_ei.collect_count_initial, 1,
1450 &yy_ei.collect_count_initial
1455 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1456 &xx_ei.ce_value_fixed_initial, 0,
1457 &yy_ei.ce_value_fixed_initial
1461 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1462 &xx_ei.ce_value_random_initial, 0,
1463 &yy_ei.ce_value_random_initial
1467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1468 &xx_ei.use_last_ce_value, FALSE,
1469 &yy_ei.use_last_ce_value
1474 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1475 &xx_ei.push_delay_fixed, 8,
1476 &yy_ei.push_delay_fixed
1480 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1481 &xx_ei.push_delay_random, 8,
1482 &yy_ei.push_delay_random
1486 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1487 &xx_ei.drop_delay_fixed, 0,
1488 &yy_ei.drop_delay_fixed
1492 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1493 &xx_ei.drop_delay_random, 0,
1494 &yy_ei.drop_delay_random
1498 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1499 &xx_ei.move_delay_fixed, 0,
1500 &yy_ei.move_delay_fixed
1504 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1505 &xx_ei.move_delay_random, 0,
1506 &yy_ei.move_delay_random
1510 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1511 &xx_ei.step_delay_fixed, 0,
1512 &yy_ei.step_delay_fixed
1516 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1517 &xx_ei.step_delay_random, 0,
1518 &yy_ei.step_delay_random
1523 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1524 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1529 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1530 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1531 &yy_ei.move_direction_initial
1535 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1536 &xx_ei.move_stepsize, TILEX / 8,
1537 &yy_ei.move_stepsize
1542 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1543 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1544 &yy_ei.move_enter_element
1548 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1549 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1550 &yy_ei.move_leave_element
1554 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1555 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1556 &yy_ei.move_leave_type
1561 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1562 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1563 &yy_ei.slippery_type
1568 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1569 &xx_ei.explosion_type, EXPLODES_3X3,
1570 &yy_ei.explosion_type
1574 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1575 &xx_ei.explosion_delay, 16,
1576 &yy_ei.explosion_delay
1580 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1581 &xx_ei.ignition_delay, 8,
1582 &yy_ei.ignition_delay
1587 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1588 &xx_ei.content, EL_EMPTY_SPACE,
1590 &xx_num_contents, 1, 1
1593 // ---------- "num_change_pages" must be the last entry ---------------------
1596 -1, SAVE_CONF_ALWAYS,
1597 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1598 &xx_ei.num_change_pages, 1,
1599 &yy_ei.num_change_pages
1610 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1612 // ---------- "current_change_page" must be the first entry -----------------
1615 -1, SAVE_CONF_ALWAYS,
1616 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1617 &xx_current_change_page, -1
1620 // ---------- (the remaining entries can be in any order) -------------------
1624 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1625 &xx_change.can_change, FALSE
1630 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1631 &xx_event_bits[0], 0
1635 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1636 &xx_event_bits[1], 0
1641 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1642 &xx_change.trigger_player, CH_PLAYER_ANY
1646 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1647 &xx_change.trigger_side, CH_SIDE_ANY
1651 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1652 &xx_change.trigger_page, CH_PAGE_ANY
1657 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1658 &xx_change.target_element, EL_EMPTY_SPACE
1663 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1664 &xx_change.delay_fixed, 0
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1669 &xx_change.delay_random, 0
1673 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1674 &xx_change.delay_frames, FRAMES_PER_SECOND
1679 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1680 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1685 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1686 &xx_change.explode, FALSE
1690 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1691 &xx_change.use_target_content, FALSE
1695 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1696 &xx_change.only_if_complete, FALSE
1700 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1701 &xx_change.use_random_replace, FALSE
1705 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1706 &xx_change.random_percentage, 100
1710 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1711 &xx_change.replace_when, CP_WHEN_EMPTY
1716 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1717 &xx_change.has_action, FALSE
1721 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1722 &xx_change.action_type, CA_NO_ACTION
1726 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1727 &xx_change.action_mode, CA_MODE_UNDEFINED
1731 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1732 &xx_change.action_arg, CA_ARG_UNDEFINED
1737 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1738 &xx_change.action_element, EL_EMPTY_SPACE
1743 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1744 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1745 &xx_num_contents, 1, 1
1755 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1759 TYPE_STRING, CONF_VALUE_BYTES(1),
1760 &xx_ei.description[0], -1, NULL,
1761 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1762 &xx_default_description[0]
1767 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1768 &xx_ei.use_gfx_element, FALSE
1772 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1773 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1778 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1779 &xx_group.choice_mode, ANIM_RANDOM
1784 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1785 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1786 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1796 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1801 &xx_ei.use_gfx_element, FALSE
1805 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1806 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1816 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1820 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1821 &li.block_snap_field, TRUE
1825 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1826 &li.continuous_snapping, TRUE
1830 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1831 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1836 &li.use_start_element[0], FALSE
1840 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1841 &li.start_element[0], EL_PLAYER_1
1845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1846 &li.use_artwork_element[0], FALSE
1850 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1851 &li.artwork_element[0], EL_PLAYER_1
1855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1856 &li.use_explosion_element[0], FALSE
1860 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1861 &li.explosion_element[0], EL_PLAYER_1
1876 filetype_id_list[] =
1878 { LEVEL_FILE_TYPE_RND, "RND" },
1879 { LEVEL_FILE_TYPE_BD, "BD" },
1880 { LEVEL_FILE_TYPE_EM, "EM" },
1881 { LEVEL_FILE_TYPE_SP, "SP" },
1882 { LEVEL_FILE_TYPE_DX, "DX" },
1883 { LEVEL_FILE_TYPE_SB, "SB" },
1884 { LEVEL_FILE_TYPE_DC, "DC" },
1885 { LEVEL_FILE_TYPE_MM, "MM" },
1886 { LEVEL_FILE_TYPE_MM, "DF" },
1891 // ============================================================================
1892 // level file functions
1893 // ============================================================================
1895 static boolean check_special_flags(char *flag)
1897 if (strEqual(options.special_flags, flag) ||
1898 strEqual(leveldir_current->special_flags, flag))
1904 static struct DateInfo getCurrentDate(void)
1906 time_t epoch_seconds = time(NULL);
1907 struct tm *now = localtime(&epoch_seconds);
1908 struct DateInfo date;
1910 date.year = now->tm_year + 1900;
1911 date.month = now->tm_mon + 1;
1912 date.day = now->tm_mday;
1914 date.src = DATE_SRC_CLOCK;
1919 static void resetEventFlags(struct ElementChangeInfo *change)
1923 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1924 change->has_event[i] = FALSE;
1927 static void resetEventBits(void)
1931 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1932 xx_event_bits[i] = 0;
1935 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1939 /* important: only change event flag if corresponding event bit is set
1940 (this is because all xx_event_bits[] values are loaded separately,
1941 and all xx_event_bits[] values are set back to zero before loading
1942 another value xx_event_bits[x] (each value representing 32 flags)) */
1944 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1945 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1946 change->has_event[i] = TRUE;
1949 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1953 /* in contrast to the above function setEventFlagsFromEventBits(), it
1954 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1955 depending on the corresponding change->has_event[i] values here, as
1956 all xx_event_bits[] values are reset in resetEventBits() before */
1958 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1959 if (change->has_event[i])
1960 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1963 static char *getDefaultElementDescription(struct ElementInfo *ei)
1965 static char description[MAX_ELEMENT_NAME_LEN + 1];
1966 char *default_description = (ei->custom_description != NULL ?
1967 ei->custom_description :
1968 ei->editor_description);
1971 // always start with reliable default values
1972 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1973 description[i] = '\0';
1975 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1976 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1978 return &description[0];
1981 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1983 char *default_description = getDefaultElementDescription(ei);
1986 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1987 ei->description[i] = default_description[i];
1990 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1994 for (i = 0; conf[i].data_type != -1; i++)
1996 int default_value = conf[i].default_value;
1997 int data_type = conf[i].data_type;
1998 int conf_type = conf[i].conf_type;
1999 int byte_mask = conf_type & CONF_MASK_BYTES;
2001 if (byte_mask == CONF_MASK_MULTI_BYTES)
2003 int default_num_entities = conf[i].default_num_entities;
2004 int max_num_entities = conf[i].max_num_entities;
2006 *(int *)(conf[i].num_entities) = default_num_entities;
2008 if (data_type == TYPE_STRING)
2010 char *default_string = conf[i].default_string;
2011 char *string = (char *)(conf[i].value);
2013 strncpy(string, default_string, max_num_entities);
2015 else if (data_type == TYPE_ELEMENT_LIST)
2017 int *element_array = (int *)(conf[i].value);
2020 for (j = 0; j < max_num_entities; j++)
2021 element_array[j] = default_value;
2023 else if (data_type == TYPE_CONTENT_LIST)
2025 struct Content *content = (struct Content *)(conf[i].value);
2028 for (c = 0; c < max_num_entities; c++)
2029 for (y = 0; y < 3; y++)
2030 for (x = 0; x < 3; x++)
2031 content[c].e[x][y] = default_value;
2034 else // constant size configuration data (1, 2 or 4 bytes)
2036 if (data_type == TYPE_BOOLEAN)
2037 *(boolean *)(conf[i].value) = default_value;
2039 *(int *) (conf[i].value) = default_value;
2044 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2048 for (i = 0; conf[i].data_type != -1; i++)
2050 int data_type = conf[i].data_type;
2051 int conf_type = conf[i].conf_type;
2052 int byte_mask = conf_type & CONF_MASK_BYTES;
2054 if (byte_mask == CONF_MASK_MULTI_BYTES)
2056 int max_num_entities = conf[i].max_num_entities;
2058 if (data_type == TYPE_STRING)
2060 char *string = (char *)(conf[i].value);
2061 char *string_copy = (char *)(conf[i].value_copy);
2063 strncpy(string_copy, string, max_num_entities);
2065 else if (data_type == TYPE_ELEMENT_LIST)
2067 int *element_array = (int *)(conf[i].value);
2068 int *element_array_copy = (int *)(conf[i].value_copy);
2071 for (j = 0; j < max_num_entities; j++)
2072 element_array_copy[j] = element_array[j];
2074 else if (data_type == TYPE_CONTENT_LIST)
2076 struct Content *content = (struct Content *)(conf[i].value);
2077 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2080 for (c = 0; c < max_num_entities; c++)
2081 for (y = 0; y < 3; y++)
2082 for (x = 0; x < 3; x++)
2083 content_copy[c].e[x][y] = content[c].e[x][y];
2086 else // constant size configuration data (1, 2 or 4 bytes)
2088 if (data_type == TYPE_BOOLEAN)
2089 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2091 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2096 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2100 xx_ei = *ei_from; // copy element data into temporary buffer
2101 yy_ei = *ei_to; // copy element data into temporary buffer
2103 copyConfigFromConfigList(chunk_config_CUSX_base);
2108 // ---------- reinitialize and copy change pages ----------
2110 ei_to->num_change_pages = ei_from->num_change_pages;
2111 ei_to->current_change_page = ei_from->current_change_page;
2113 setElementChangePages(ei_to, ei_to->num_change_pages);
2115 for (i = 0; i < ei_to->num_change_pages; i++)
2116 ei_to->change_page[i] = ei_from->change_page[i];
2118 // ---------- copy group element info ----------
2119 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2120 *ei_to->group = *ei_from->group;
2122 // mark this custom element as modified
2123 ei_to->modified_settings = TRUE;
2126 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2128 int change_page_size = sizeof(struct ElementChangeInfo);
2130 ei->num_change_pages = MAX(1, change_pages);
2133 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2135 if (ei->current_change_page >= ei->num_change_pages)
2136 ei->current_change_page = ei->num_change_pages - 1;
2138 ei->change = &ei->change_page[ei->current_change_page];
2141 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2143 xx_change = *change; // copy change data into temporary buffer
2145 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2147 *change = xx_change;
2149 resetEventFlags(change);
2151 change->direct_action = 0;
2152 change->other_action = 0;
2154 change->pre_change_function = NULL;
2155 change->change_function = NULL;
2156 change->post_change_function = NULL;
2159 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2163 li = *level; // copy level data into temporary buffer
2164 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2165 *level = li; // copy temporary buffer back to level data
2167 setLevelInfoToDefaults_BD();
2168 setLevelInfoToDefaults_EM();
2169 setLevelInfoToDefaults_SP();
2170 setLevelInfoToDefaults_MM();
2172 level->native_bd_level = &native_bd_level;
2173 level->native_em_level = &native_em_level;
2174 level->native_sp_level = &native_sp_level;
2175 level->native_mm_level = &native_mm_level;
2177 level->file_version = FILE_VERSION_ACTUAL;
2178 level->game_version = GAME_VERSION_ACTUAL;
2180 level->creation_date = getCurrentDate();
2182 level->encoding_16bit_field = TRUE;
2183 level->encoding_16bit_yamyam = TRUE;
2184 level->encoding_16bit_amoeba = TRUE;
2186 // clear level name and level author string buffers
2187 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2188 level->name[i] = '\0';
2189 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2190 level->author[i] = '\0';
2192 // set level name and level author to default values
2193 strcpy(level->name, NAMELESS_LEVEL_NAME);
2194 strcpy(level->author, ANONYMOUS_NAME);
2196 // set level playfield to playable default level with player and exit
2197 for (x = 0; x < MAX_LEV_FIELDX; x++)
2198 for (y = 0; y < MAX_LEV_FIELDY; y++)
2199 level->field[x][y] = EL_SAND;
2201 level->field[0][0] = EL_PLAYER_1;
2202 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2204 BorderElement = EL_STEELWALL;
2206 // detect custom elements when loading them
2207 level->file_has_custom_elements = FALSE;
2209 // set all bug compatibility flags to "false" => do not emulate this bug
2210 level->use_action_after_change_bug = FALSE;
2212 if (leveldir_current)
2214 // try to determine better author name than 'anonymous'
2215 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2217 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2218 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2222 switch (LEVELCLASS(leveldir_current))
2224 case LEVELCLASS_TUTORIAL:
2225 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2228 case LEVELCLASS_CONTRIB:
2229 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2230 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2233 case LEVELCLASS_PRIVATE:
2234 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2235 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2239 // keep default value
2246 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2248 static boolean clipboard_elements_initialized = FALSE;
2251 InitElementPropertiesStatic();
2253 li = *level; // copy level data into temporary buffer
2254 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2255 *level = li; // copy temporary buffer back to level data
2257 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2260 struct ElementInfo *ei = &element_info[element];
2262 if (element == EL_MM_GRAY_BALL)
2264 struct LevelInfo_MM *level_mm = level->native_mm_level;
2267 for (j = 0; j < level->num_mm_ball_contents; j++)
2268 level->mm_ball_content[j] =
2269 map_element_MM_to_RND(level_mm->ball_content[j]);
2272 // never initialize clipboard elements after the very first time
2273 // (to be able to use clipboard elements between several levels)
2274 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2277 if (IS_ENVELOPE(element))
2279 int envelope_nr = element - EL_ENVELOPE_1;
2281 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2283 level->envelope[envelope_nr] = xx_envelope;
2286 if (IS_CUSTOM_ELEMENT(element) ||
2287 IS_GROUP_ELEMENT(element) ||
2288 IS_INTERNAL_ELEMENT(element))
2290 xx_ei = *ei; // copy element data into temporary buffer
2292 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2297 setElementChangePages(ei, 1);
2298 setElementChangeInfoToDefaults(ei->change);
2300 if (IS_CUSTOM_ELEMENT(element) ||
2301 IS_GROUP_ELEMENT(element))
2303 setElementDescriptionToDefault(ei);
2305 ei->modified_settings = FALSE;
2308 if (IS_CUSTOM_ELEMENT(element) ||
2309 IS_INTERNAL_ELEMENT(element))
2311 // internal values used in level editor
2313 ei->access_type = 0;
2314 ei->access_layer = 0;
2315 ei->access_protected = 0;
2316 ei->walk_to_action = 0;
2317 ei->smash_targets = 0;
2320 ei->can_explode_by_fire = FALSE;
2321 ei->can_explode_smashed = FALSE;
2322 ei->can_explode_impact = FALSE;
2324 ei->current_change_page = 0;
2327 if (IS_GROUP_ELEMENT(element) ||
2328 IS_INTERNAL_ELEMENT(element))
2330 struct ElementGroupInfo *group;
2332 // initialize memory for list of elements in group
2333 if (ei->group == NULL)
2334 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2338 xx_group = *group; // copy group data into temporary buffer
2340 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2345 if (IS_EMPTY_ELEMENT(element) ||
2346 IS_INTERNAL_ELEMENT(element))
2348 xx_ei = *ei; // copy element data into temporary buffer
2350 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2356 clipboard_elements_initialized = TRUE;
2359 static void setLevelInfoToDefaults(struct LevelInfo *level,
2360 boolean level_info_only,
2361 boolean reset_file_status)
2363 setLevelInfoToDefaults_Level(level);
2365 if (!level_info_only)
2366 setLevelInfoToDefaults_Elements(level);
2368 if (reset_file_status)
2370 level->no_valid_file = FALSE;
2371 level->no_level_file = FALSE;
2374 level->changed = FALSE;
2377 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2379 level_file_info->nr = 0;
2380 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2381 level_file_info->packed = FALSE;
2383 setString(&level_file_info->basename, NULL);
2384 setString(&level_file_info->filename, NULL);
2387 int getMappedElement_SB(int, boolean);
2389 static void ActivateLevelTemplate(void)
2393 if (check_special_flags("load_xsb_to_ces"))
2395 // fill smaller playfields with padding "beyond border wall" elements
2396 if (level.fieldx < level_template.fieldx ||
2397 level.fieldy < level_template.fieldy)
2399 short field[level.fieldx][level.fieldy];
2400 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2401 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2402 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2403 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2405 // copy old playfield (which is smaller than the visible area)
2406 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2407 field[x][y] = level.field[x][y];
2409 // fill new, larger playfield with "beyond border wall" elements
2410 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2411 level.field[x][y] = getMappedElement_SB('_', TRUE);
2413 // copy the old playfield to the middle of the new playfield
2414 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2415 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2417 level.fieldx = new_fieldx;
2418 level.fieldy = new_fieldy;
2422 // Currently there is no special action needed to activate the template
2423 // data, because 'element_info' property settings overwrite the original
2424 // level data, while all other variables do not change.
2426 // Exception: 'from_level_template' elements in the original level playfield
2427 // are overwritten with the corresponding elements at the same position in
2428 // playfield from the level template.
2430 for (x = 0; x < level.fieldx; x++)
2431 for (y = 0; y < level.fieldy; y++)
2432 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2433 level.field[x][y] = level_template.field[x][y];
2435 if (check_special_flags("load_xsb_to_ces"))
2437 struct LevelInfo level_backup = level;
2439 // overwrite all individual level settings from template level settings
2440 level = level_template;
2442 // restore level file info
2443 level.file_info = level_backup.file_info;
2445 // restore playfield size
2446 level.fieldx = level_backup.fieldx;
2447 level.fieldy = level_backup.fieldy;
2449 // restore playfield content
2450 for (x = 0; x < level.fieldx; x++)
2451 for (y = 0; y < level.fieldy; y++)
2452 level.field[x][y] = level_backup.field[x][y];
2454 // restore name and author from individual level
2455 strcpy(level.name, level_backup.name);
2456 strcpy(level.author, level_backup.author);
2458 // restore flag "use_custom_template"
2459 level.use_custom_template = level_backup.use_custom_template;
2463 static boolean checkForPackageFromBasename_BD(char *basename)
2465 // check for native BD level file extensions
2466 if (!strSuffixLower(basename, ".bd") &&
2467 !strSuffixLower(basename, ".bdr") &&
2468 !strSuffixLower(basename, ".brc") &&
2469 !strSuffixLower(basename, ".gds"))
2472 // check for standard single-level BD files (like "001.bd")
2473 if (strSuffixLower(basename, ".bd") &&
2474 strlen(basename) == 6 &&
2475 basename[0] >= '0' && basename[0] <= '9' &&
2476 basename[1] >= '0' && basename[1] <= '9' &&
2477 basename[2] >= '0' && basename[2] <= '9')
2480 // this is a level package in native BD file format
2484 static char *getLevelFilenameFromBasename(char *basename)
2486 static char *filename = NULL;
2488 checked_free(filename);
2490 filename = getPath2(getCurrentLevelDir(), basename);
2495 static int getFileTypeFromBasename(char *basename)
2497 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2499 static char *filename = NULL;
2500 struct stat file_status;
2502 // ---------- try to determine file type from filename ----------
2504 // check for typical filename of a Supaplex level package file
2505 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2506 return LEVEL_FILE_TYPE_SP;
2508 // check for typical filename of a Diamond Caves II level package file
2509 if (strSuffixLower(basename, ".dc") ||
2510 strSuffixLower(basename, ".dc2"))
2511 return LEVEL_FILE_TYPE_DC;
2513 // check for typical filename of a Sokoban level package file
2514 if (strSuffixLower(basename, ".xsb") &&
2515 strchr(basename, '%') == NULL)
2516 return LEVEL_FILE_TYPE_SB;
2518 // check for typical filename of a Boulder Dash (GDash) level package file
2519 if (checkForPackageFromBasename_BD(basename))
2520 return LEVEL_FILE_TYPE_BD;
2522 // ---------- try to determine file type from filesize ----------
2524 checked_free(filename);
2525 filename = getPath2(getCurrentLevelDir(), basename);
2527 if (stat(filename, &file_status) == 0)
2529 // check for typical filesize of a Supaplex level package file
2530 if (file_status.st_size == 170496)
2531 return LEVEL_FILE_TYPE_SP;
2534 return LEVEL_FILE_TYPE_UNKNOWN;
2537 static int getFileTypeFromMagicBytes(char *filename, int type)
2541 if ((file = openFile(filename, MODE_READ)))
2543 char chunk_name[CHUNK_ID_LEN + 1];
2545 getFileChunkBE(file, chunk_name, NULL);
2547 if (strEqual(chunk_name, "MMII") ||
2548 strEqual(chunk_name, "MIRR"))
2549 type = LEVEL_FILE_TYPE_MM;
2557 static boolean checkForPackageFromBasename(char *basename)
2559 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2560 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2562 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2565 static char *getSingleLevelBasenameExt(int nr, char *extension)
2567 static char basename[MAX_FILENAME_LEN];
2570 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2572 sprintf(basename, "%03d.%s", nr, extension);
2577 static char *getSingleLevelBasename(int nr)
2579 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2582 static char *getPackedLevelBasename(int type)
2584 static char basename[MAX_FILENAME_LEN];
2585 char *directory = getCurrentLevelDir();
2587 DirectoryEntry *dir_entry;
2589 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2591 if ((dir = openDirectory(directory)) == NULL)
2593 Warn("cannot read current level directory '%s'", directory);
2598 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2600 char *entry_basename = dir_entry->basename;
2601 int entry_type = getFileTypeFromBasename(entry_basename);
2603 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2605 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2608 strcpy(basename, entry_basename);
2615 closeDirectory(dir);
2620 static char *getSingleLevelFilename(int nr)
2622 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2625 #if ENABLE_UNUSED_CODE
2626 static char *getPackedLevelFilename(int type)
2628 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2632 char *getDefaultLevelFilename(int nr)
2634 return getSingleLevelFilename(nr);
2637 #if ENABLE_UNUSED_CODE
2638 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2642 lfi->packed = FALSE;
2644 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2645 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2649 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2650 int type, char *format, ...)
2652 static char basename[MAX_FILENAME_LEN];
2655 va_start(ap, format);
2656 vsprintf(basename, format, ap);
2660 lfi->packed = FALSE;
2662 setString(&lfi->basename, basename);
2663 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2666 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2672 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2673 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2676 static int getFiletypeFromID(char *filetype_id)
2678 char *filetype_id_lower;
2679 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2682 if (filetype_id == NULL)
2683 return LEVEL_FILE_TYPE_UNKNOWN;
2685 filetype_id_lower = getStringToLower(filetype_id);
2687 for (i = 0; filetype_id_list[i].id != NULL; i++)
2689 char *id_lower = getStringToLower(filetype_id_list[i].id);
2691 if (strEqual(filetype_id_lower, id_lower))
2692 filetype = filetype_id_list[i].filetype;
2696 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2700 free(filetype_id_lower);
2705 char *getLocalLevelTemplateFilename(void)
2707 return getDefaultLevelFilename(-1);
2710 char *getGlobalLevelTemplateFilename(void)
2712 // global variable "leveldir_current" must be modified in the loop below
2713 LevelDirTree *leveldir_current_last = leveldir_current;
2714 char *filename = NULL;
2716 // check for template level in path from current to topmost tree node
2718 while (leveldir_current != NULL)
2720 filename = getDefaultLevelFilename(-1);
2722 if (fileExists(filename))
2725 leveldir_current = leveldir_current->node_parent;
2728 // restore global variable "leveldir_current" modified in above loop
2729 leveldir_current = leveldir_current_last;
2734 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2738 // special case: level number is negative => check for level template file
2741 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2742 getSingleLevelBasename(-1));
2744 // replace local level template filename with global template filename
2745 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2747 // no fallback if template file not existing
2751 // special case: check for file name/pattern specified in "levelinfo.conf"
2752 if (leveldir_current->level_filename != NULL)
2754 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2756 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2757 leveldir_current->level_filename, nr);
2759 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2761 if (fileExists(lfi->filename))
2764 else if (leveldir_current->level_filetype != NULL)
2766 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2768 // check for specified native level file with standard file name
2769 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2770 "%03d.%s", nr, LEVELFILE_EXTENSION);
2771 if (fileExists(lfi->filename))
2775 // check for native Rocks'n'Diamonds level file
2776 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2777 "%03d.%s", nr, LEVELFILE_EXTENSION);
2778 if (fileExists(lfi->filename))
2781 // check for native Boulder Dash level file
2782 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2783 if (fileExists(lfi->filename))
2786 // check for Emerald Mine level file (V1)
2787 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2788 'a' + (nr / 10) % 26, '0' + nr % 10);
2789 if (fileExists(lfi->filename))
2791 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2792 'A' + (nr / 10) % 26, '0' + nr % 10);
2793 if (fileExists(lfi->filename))
2796 // check for Emerald Mine level file (V2 to V5)
2797 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2798 if (fileExists(lfi->filename))
2801 // check for Emerald Mine level file (V6 / single mode)
2802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2803 if (fileExists(lfi->filename))
2805 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2806 if (fileExists(lfi->filename))
2809 // check for Emerald Mine level file (V6 / teamwork mode)
2810 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2811 if (fileExists(lfi->filename))
2813 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2814 if (fileExists(lfi->filename))
2817 // check for various packed level file formats
2818 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2819 if (fileExists(lfi->filename))
2822 // no known level file found -- use default values (and fail later)
2823 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2824 "%03d.%s", nr, LEVELFILE_EXTENSION);
2827 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2829 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2830 lfi->type = getFileTypeFromBasename(lfi->basename);
2832 if (lfi->type == LEVEL_FILE_TYPE_RND)
2833 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2836 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2838 // always start with reliable default values
2839 setFileInfoToDefaults(level_file_info);
2841 level_file_info->nr = nr; // set requested level number
2843 determineLevelFileInfo_Filename(level_file_info);
2844 determineLevelFileInfo_Filetype(level_file_info);
2847 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2848 struct LevelFileInfo *lfi_to)
2850 lfi_to->nr = lfi_from->nr;
2851 lfi_to->type = lfi_from->type;
2852 lfi_to->packed = lfi_from->packed;
2854 setString(&lfi_to->basename, lfi_from->basename);
2855 setString(&lfi_to->filename, lfi_from->filename);
2858 // ----------------------------------------------------------------------------
2859 // functions for loading R'n'D level
2860 // ----------------------------------------------------------------------------
2862 int getMappedElement(int element)
2864 // remap some (historic, now obsolete) elements
2868 case EL_PLAYER_OBSOLETE:
2869 element = EL_PLAYER_1;
2872 case EL_KEY_OBSOLETE:
2876 case EL_EM_KEY_1_FILE_OBSOLETE:
2877 element = EL_EM_KEY_1;
2880 case EL_EM_KEY_2_FILE_OBSOLETE:
2881 element = EL_EM_KEY_2;
2884 case EL_EM_KEY_3_FILE_OBSOLETE:
2885 element = EL_EM_KEY_3;
2888 case EL_EM_KEY_4_FILE_OBSOLETE:
2889 element = EL_EM_KEY_4;
2892 case EL_ENVELOPE_OBSOLETE:
2893 element = EL_ENVELOPE_1;
2901 if (element >= NUM_FILE_ELEMENTS)
2903 Warn("invalid level element %d", element);
2905 element = EL_UNKNOWN;
2913 static int getMappedElementByVersion(int element, int game_version)
2915 // remap some elements due to certain game version
2917 if (game_version <= VERSION_IDENT(2,2,0,0))
2919 // map game font elements
2920 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2921 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2922 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2923 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2926 if (game_version < VERSION_IDENT(3,0,0,0))
2928 // map Supaplex gravity tube elements
2929 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2930 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2931 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2932 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2939 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2941 level->file_version = getFileVersion(file);
2942 level->game_version = getFileVersion(file);
2947 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2949 level->creation_date.year = getFile16BitBE(file);
2950 level->creation_date.month = getFile8Bit(file);
2951 level->creation_date.day = getFile8Bit(file);
2953 level->creation_date.src = DATE_SRC_LEVELFILE;
2958 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2960 int initial_player_stepsize;
2961 int initial_player_gravity;
2964 level->fieldx = getFile8Bit(file);
2965 level->fieldy = getFile8Bit(file);
2967 level->time = getFile16BitBE(file);
2968 level->gems_needed = getFile16BitBE(file);
2970 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2971 level->name[i] = getFile8Bit(file);
2972 level->name[MAX_LEVEL_NAME_LEN] = 0;
2974 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2975 level->score[i] = getFile8Bit(file);
2977 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2978 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2979 for (y = 0; y < 3; y++)
2980 for (x = 0; x < 3; x++)
2981 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2983 level->amoeba_speed = getFile8Bit(file);
2984 level->time_magic_wall = getFile8Bit(file);
2985 level->time_wheel = getFile8Bit(file);
2986 level->amoeba_content = getMappedElement(getFile8Bit(file));
2988 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2991 for (i = 0; i < MAX_PLAYERS; i++)
2992 level->initial_player_stepsize[i] = initial_player_stepsize;
2994 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2996 for (i = 0; i < MAX_PLAYERS; i++)
2997 level->initial_player_gravity[i] = initial_player_gravity;
2999 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3000 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3002 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3004 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3005 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3006 level->can_move_into_acid_bits = getFile32BitBE(file);
3007 level->dont_collide_with_bits = getFile8Bit(file);
3009 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3010 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3012 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3013 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3014 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3016 level->game_engine_type = getFile8Bit(file);
3018 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3023 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3027 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3028 level->name[i] = getFile8Bit(file);
3029 level->name[MAX_LEVEL_NAME_LEN] = 0;
3034 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3038 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3039 level->author[i] = getFile8Bit(file);
3040 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3045 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3048 int chunk_size_expected = level->fieldx * level->fieldy;
3050 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3051 stored with 16-bit encoding (and should be twice as big then).
3052 Even worse, playfield data was stored 16-bit when only yamyam content
3053 contained 16-bit elements and vice versa. */
3055 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3056 chunk_size_expected *= 2;
3058 if (chunk_size_expected != chunk_size)
3060 ReadUnusedBytesFromFile(file, chunk_size);
3061 return chunk_size_expected;
3064 for (y = 0; y < level->fieldy; y++)
3065 for (x = 0; x < level->fieldx; x++)
3066 level->field[x][y] =
3067 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3072 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3075 int header_size = 4;
3076 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3077 int chunk_size_expected = header_size + content_size;
3079 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3080 stored with 16-bit encoding (and should be twice as big then).
3081 Even worse, playfield data was stored 16-bit when only yamyam content
3082 contained 16-bit elements and vice versa. */
3084 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3085 chunk_size_expected += content_size;
3087 if (chunk_size_expected != chunk_size)
3089 ReadUnusedBytesFromFile(file, chunk_size);
3090 return chunk_size_expected;
3094 level->num_yamyam_contents = getFile8Bit(file);
3098 // correct invalid number of content fields -- should never happen
3099 if (level->num_yamyam_contents < 1 ||
3100 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3101 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3103 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3104 for (y = 0; y < 3; y++)
3105 for (x = 0; x < 3; x++)
3106 level->yamyam_content[i].e[x][y] =
3107 getMappedElement(level->encoding_16bit_field ?
3108 getFile16BitBE(file) : getFile8Bit(file));
3112 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3117 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3119 element = getMappedElement(getFile16BitBE(file));
3120 num_contents = getFile8Bit(file);
3122 getFile8Bit(file); // content x size (unused)
3123 getFile8Bit(file); // content y size (unused)
3125 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3127 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3128 for (y = 0; y < 3; y++)
3129 for (x = 0; x < 3; x++)
3130 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3132 // correct invalid number of content fields -- should never happen
3133 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3134 num_contents = STD_ELEMENT_CONTENTS;
3136 if (element == EL_YAMYAM)
3138 level->num_yamyam_contents = num_contents;
3140 for (i = 0; i < num_contents; i++)
3141 for (y = 0; y < 3; y++)
3142 for (x = 0; x < 3; x++)
3143 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3145 else if (element == EL_BD_AMOEBA)
3147 level->amoeba_content = content_array[0][0][0];
3151 Warn("cannot load content for element '%d'", element);
3157 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3163 int chunk_size_expected;
3165 element = getMappedElement(getFile16BitBE(file));
3166 if (!IS_ENVELOPE(element))
3167 element = EL_ENVELOPE_1;
3169 envelope_nr = element - EL_ENVELOPE_1;
3171 envelope_len = getFile16BitBE(file);
3173 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3174 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3176 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3178 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3179 if (chunk_size_expected != chunk_size)
3181 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3182 return chunk_size_expected;
3185 for (i = 0; i < envelope_len; i++)
3186 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3191 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3193 int num_changed_custom_elements = getFile16BitBE(file);
3194 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3197 if (chunk_size_expected != chunk_size)
3199 ReadUnusedBytesFromFile(file, chunk_size - 2);
3200 return chunk_size_expected;
3203 for (i = 0; i < num_changed_custom_elements; i++)
3205 int element = getMappedElement(getFile16BitBE(file));
3206 int properties = getFile32BitBE(file);
3208 if (IS_CUSTOM_ELEMENT(element))
3209 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3211 Warn("invalid custom element number %d", element);
3213 // older game versions that wrote level files with CUS1 chunks used
3214 // different default push delay values (not yet stored in level file)
3215 element_info[element].push_delay_fixed = 2;
3216 element_info[element].push_delay_random = 8;
3219 level->file_has_custom_elements = TRUE;
3224 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3226 int num_changed_custom_elements = getFile16BitBE(file);
3227 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3230 if (chunk_size_expected != chunk_size)
3232 ReadUnusedBytesFromFile(file, chunk_size - 2);
3233 return chunk_size_expected;
3236 for (i = 0; i < num_changed_custom_elements; i++)
3238 int element = getMappedElement(getFile16BitBE(file));
3239 int custom_target_element = getMappedElement(getFile16BitBE(file));
3241 if (IS_CUSTOM_ELEMENT(element))
3242 element_info[element].change->target_element = custom_target_element;
3244 Warn("invalid custom element number %d", element);
3247 level->file_has_custom_elements = TRUE;
3252 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3254 int num_changed_custom_elements = getFile16BitBE(file);
3255 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3258 if (chunk_size_expected != chunk_size)
3260 ReadUnusedBytesFromFile(file, chunk_size - 2);
3261 return chunk_size_expected;
3264 for (i = 0; i < num_changed_custom_elements; i++)
3266 int element = getMappedElement(getFile16BitBE(file));
3267 struct ElementInfo *ei = &element_info[element];
3268 unsigned int event_bits;
3270 if (!IS_CUSTOM_ELEMENT(element))
3272 Warn("invalid custom element number %d", element);
3274 element = EL_INTERNAL_DUMMY;
3277 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3278 ei->description[j] = getFile8Bit(file);
3279 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3281 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3283 // some free bytes for future properties and padding
3284 ReadUnusedBytesFromFile(file, 7);
3286 ei->use_gfx_element = getFile8Bit(file);
3287 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3289 ei->collect_score_initial = getFile8Bit(file);
3290 ei->collect_count_initial = getFile8Bit(file);
3292 ei->push_delay_fixed = getFile16BitBE(file);
3293 ei->push_delay_random = getFile16BitBE(file);
3294 ei->move_delay_fixed = getFile16BitBE(file);
3295 ei->move_delay_random = getFile16BitBE(file);
3297 ei->move_pattern = getFile16BitBE(file);
3298 ei->move_direction_initial = getFile8Bit(file);
3299 ei->move_stepsize = getFile8Bit(file);
3301 for (y = 0; y < 3; y++)
3302 for (x = 0; x < 3; x++)
3303 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3305 // bits 0 - 31 of "has_event[]"
3306 event_bits = getFile32BitBE(file);
3307 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3308 if (event_bits & (1u << j))
3309 ei->change->has_event[j] = TRUE;
3311 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3313 ei->change->delay_fixed = getFile16BitBE(file);
3314 ei->change->delay_random = getFile16BitBE(file);
3315 ei->change->delay_frames = getFile16BitBE(file);
3317 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3319 ei->change->explode = getFile8Bit(file);
3320 ei->change->use_target_content = getFile8Bit(file);
3321 ei->change->only_if_complete = getFile8Bit(file);
3322 ei->change->use_random_replace = getFile8Bit(file);
3324 ei->change->random_percentage = getFile8Bit(file);
3325 ei->change->replace_when = getFile8Bit(file);
3327 for (y = 0; y < 3; y++)
3328 for (x = 0; x < 3; x++)
3329 ei->change->target_content.e[x][y] =
3330 getMappedElement(getFile16BitBE(file));
3332 ei->slippery_type = getFile8Bit(file);
3334 // some free bytes for future properties and padding
3335 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3337 // mark that this custom element has been modified
3338 ei->modified_settings = TRUE;
3341 level->file_has_custom_elements = TRUE;
3346 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3348 struct ElementInfo *ei;
3349 int chunk_size_expected;
3353 // ---------- custom element base property values (96 bytes) ----------------
3355 element = getMappedElement(getFile16BitBE(file));
3357 if (!IS_CUSTOM_ELEMENT(element))
3359 Warn("invalid custom element number %d", element);
3361 ReadUnusedBytesFromFile(file, chunk_size - 2);
3366 ei = &element_info[element];
3368 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3369 ei->description[i] = getFile8Bit(file);
3370 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3372 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3374 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3376 ei->num_change_pages = getFile8Bit(file);
3378 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3379 if (chunk_size_expected != chunk_size)
3381 ReadUnusedBytesFromFile(file, chunk_size - 43);
3382 return chunk_size_expected;
3385 ei->ce_value_fixed_initial = getFile16BitBE(file);
3386 ei->ce_value_random_initial = getFile16BitBE(file);
3387 ei->use_last_ce_value = getFile8Bit(file);
3389 ei->use_gfx_element = getFile8Bit(file);
3390 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3392 ei->collect_score_initial = getFile8Bit(file);
3393 ei->collect_count_initial = getFile8Bit(file);
3395 ei->drop_delay_fixed = getFile8Bit(file);
3396 ei->push_delay_fixed = getFile8Bit(file);
3397 ei->drop_delay_random = getFile8Bit(file);
3398 ei->push_delay_random = getFile8Bit(file);
3399 ei->move_delay_fixed = getFile16BitBE(file);
3400 ei->move_delay_random = getFile16BitBE(file);
3402 // bits 0 - 15 of "move_pattern" ...
3403 ei->move_pattern = getFile16BitBE(file);
3404 ei->move_direction_initial = getFile8Bit(file);
3405 ei->move_stepsize = getFile8Bit(file);
3407 ei->slippery_type = getFile8Bit(file);
3409 for (y = 0; y < 3; y++)
3410 for (x = 0; x < 3; x++)
3411 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3413 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3414 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3415 ei->move_leave_type = getFile8Bit(file);
3417 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3418 ei->move_pattern |= (getFile16BitBE(file) << 16);
3420 ei->access_direction = getFile8Bit(file);
3422 ei->explosion_delay = getFile8Bit(file);
3423 ei->ignition_delay = getFile8Bit(file);
3424 ei->explosion_type = getFile8Bit(file);
3426 // some free bytes for future custom property values and padding
3427 ReadUnusedBytesFromFile(file, 1);
3429 // ---------- change page property values (48 bytes) ------------------------
3431 setElementChangePages(ei, ei->num_change_pages);
3433 for (i = 0; i < ei->num_change_pages; i++)
3435 struct ElementChangeInfo *change = &ei->change_page[i];
3436 unsigned int event_bits;
3438 // always start with reliable default values
3439 setElementChangeInfoToDefaults(change);
3441 // bits 0 - 31 of "has_event[]" ...
3442 event_bits = getFile32BitBE(file);
3443 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3444 if (event_bits & (1u << j))
3445 change->has_event[j] = TRUE;
3447 change->target_element = getMappedElement(getFile16BitBE(file));
3449 change->delay_fixed = getFile16BitBE(file);
3450 change->delay_random = getFile16BitBE(file);
3451 change->delay_frames = getFile16BitBE(file);
3453 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3455 change->explode = getFile8Bit(file);
3456 change->use_target_content = getFile8Bit(file);
3457 change->only_if_complete = getFile8Bit(file);
3458 change->use_random_replace = getFile8Bit(file);
3460 change->random_percentage = getFile8Bit(file);
3461 change->replace_when = getFile8Bit(file);
3463 for (y = 0; y < 3; y++)
3464 for (x = 0; x < 3; x++)
3465 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3467 change->can_change = getFile8Bit(file);
3469 change->trigger_side = getFile8Bit(file);
3471 change->trigger_player = getFile8Bit(file);
3472 change->trigger_page = getFile8Bit(file);
3474 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3475 CH_PAGE_ANY : (1 << change->trigger_page));
3477 change->has_action = getFile8Bit(file);
3478 change->action_type = getFile8Bit(file);
3479 change->action_mode = getFile8Bit(file);
3480 change->action_arg = getFile16BitBE(file);
3482 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3483 event_bits = getFile8Bit(file);
3484 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3485 if (event_bits & (1u << (j - 32)))
3486 change->has_event[j] = TRUE;
3489 // mark this custom element as modified
3490 ei->modified_settings = TRUE;
3492 level->file_has_custom_elements = TRUE;
3497 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3499 struct ElementInfo *ei;
3500 struct ElementGroupInfo *group;
3504 element = getMappedElement(getFile16BitBE(file));
3506 if (!IS_GROUP_ELEMENT(element))
3508 Warn("invalid group element number %d", element);
3510 ReadUnusedBytesFromFile(file, chunk_size - 2);
3515 ei = &element_info[element];
3517 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3518 ei->description[i] = getFile8Bit(file);
3519 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3521 group = element_info[element].group;
3523 group->num_elements = getFile8Bit(file);
3525 ei->use_gfx_element = getFile8Bit(file);
3526 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3528 group->choice_mode = getFile8Bit(file);
3530 // some free bytes for future values and padding
3531 ReadUnusedBytesFromFile(file, 3);
3533 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3534 group->element[i] = getMappedElement(getFile16BitBE(file));
3536 // mark this group element as modified
3537 element_info[element].modified_settings = TRUE;
3539 level->file_has_custom_elements = TRUE;
3544 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3545 int element, int real_element)
3547 int micro_chunk_size = 0;
3548 int conf_type = getFile8Bit(file);
3549 int byte_mask = conf_type & CONF_MASK_BYTES;
3550 boolean element_found = FALSE;
3553 micro_chunk_size += 1;
3555 if (byte_mask == CONF_MASK_MULTI_BYTES)
3557 int num_bytes = getFile16BitBE(file);
3558 byte *buffer = checked_malloc(num_bytes);
3560 ReadBytesFromFile(file, buffer, num_bytes);
3562 for (i = 0; conf[i].data_type != -1; i++)
3564 if (conf[i].element == element &&
3565 conf[i].conf_type == conf_type)
3567 int data_type = conf[i].data_type;
3568 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3569 int max_num_entities = conf[i].max_num_entities;
3571 if (num_entities > max_num_entities)
3573 Warn("truncating number of entities for element %d from %d to %d",
3574 element, num_entities, max_num_entities);
3576 num_entities = max_num_entities;
3579 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3580 data_type == TYPE_CONTENT_LIST))
3582 // for element and content lists, zero entities are not allowed
3583 Warn("found empty list of entities for element %d", element);
3585 // do not set "num_entities" here to prevent reading behind buffer
3587 *(int *)(conf[i].num_entities) = 1; // at least one is required
3591 *(int *)(conf[i].num_entities) = num_entities;
3594 element_found = TRUE;
3596 if (data_type == TYPE_STRING)
3598 char *string = (char *)(conf[i].value);
3601 for (j = 0; j < max_num_entities; j++)
3602 string[j] = (j < num_entities ? buffer[j] : '\0');
3604 else if (data_type == TYPE_ELEMENT_LIST)
3606 int *element_array = (int *)(conf[i].value);
3609 for (j = 0; j < num_entities; j++)
3611 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3613 else if (data_type == TYPE_CONTENT_LIST)
3615 struct Content *content= (struct Content *)(conf[i].value);
3618 for (c = 0; c < num_entities; c++)
3619 for (y = 0; y < 3; y++)
3620 for (x = 0; x < 3; x++)
3621 content[c].e[x][y] =
3622 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3625 element_found = FALSE;
3631 checked_free(buffer);
3633 micro_chunk_size += 2 + num_bytes;
3635 else // constant size configuration data (1, 2 or 4 bytes)
3637 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3638 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3639 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3641 for (i = 0; conf[i].data_type != -1; i++)
3643 if (conf[i].element == element &&
3644 conf[i].conf_type == conf_type)
3646 int data_type = conf[i].data_type;
3648 if (data_type == TYPE_ELEMENT)
3649 value = getMappedElement(value);
3651 if (data_type == TYPE_BOOLEAN)
3652 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3654 *(int *) (conf[i].value) = value;
3656 element_found = TRUE;
3662 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3667 char *error_conf_chunk_bytes =
3668 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3669 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3670 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3671 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3672 int error_element = real_element;
3674 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3675 error_conf_chunk_bytes, error_conf_chunk_token,
3676 error_element, EL_NAME(error_element));
3679 return micro_chunk_size;
3682 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3684 int real_chunk_size = 0;
3686 li = *level; // copy level data into temporary buffer
3688 while (!checkEndOfFile(file))
3690 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3692 if (real_chunk_size >= chunk_size)
3696 *level = li; // copy temporary buffer back to level data
3698 return real_chunk_size;
3701 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3703 int real_chunk_size = 0;
3705 li = *level; // copy level data into temporary buffer
3707 while (!checkEndOfFile(file))
3709 int element = getMappedElement(getFile16BitBE(file));
3711 real_chunk_size += 2;
3712 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3714 if (real_chunk_size >= chunk_size)
3718 *level = li; // copy temporary buffer back to level data
3720 return real_chunk_size;
3723 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3725 int real_chunk_size = 0;
3727 li = *level; // copy level data into temporary buffer
3729 while (!checkEndOfFile(file))
3731 int element = getMappedElement(getFile16BitBE(file));
3733 real_chunk_size += 2;
3734 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3736 if (real_chunk_size >= chunk_size)
3740 *level = li; // copy temporary buffer back to level data
3742 return real_chunk_size;
3745 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3747 int element = getMappedElement(getFile16BitBE(file));
3748 int envelope_nr = element - EL_ENVELOPE_1;
3749 int real_chunk_size = 2;
3751 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3753 while (!checkEndOfFile(file))
3755 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3758 if (real_chunk_size >= chunk_size)
3762 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3764 return real_chunk_size;
3767 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3769 int element = getMappedElement(getFile16BitBE(file));
3770 int real_chunk_size = 2;
3771 struct ElementInfo *ei = &element_info[element];
3774 xx_ei = *ei; // copy element data into temporary buffer
3776 xx_ei.num_change_pages = -1;
3778 while (!checkEndOfFile(file))
3780 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3782 if (xx_ei.num_change_pages != -1)
3785 if (real_chunk_size >= chunk_size)
3791 if (ei->num_change_pages == -1)
3793 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3796 ei->num_change_pages = 1;
3798 setElementChangePages(ei, 1);
3799 setElementChangeInfoToDefaults(ei->change);
3801 return real_chunk_size;
3804 // initialize number of change pages stored for this custom element
3805 setElementChangePages(ei, ei->num_change_pages);
3806 for (i = 0; i < ei->num_change_pages; i++)
3807 setElementChangeInfoToDefaults(&ei->change_page[i]);
3809 // start with reading properties for the first change page
3810 xx_current_change_page = 0;
3812 while (!checkEndOfFile(file))
3814 // level file might contain invalid change page number
3815 if (xx_current_change_page >= ei->num_change_pages)
3818 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3820 xx_change = *change; // copy change data into temporary buffer
3822 resetEventBits(); // reset bits; change page might have changed
3824 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3827 *change = xx_change;
3829 setEventFlagsFromEventBits(change);
3831 if (real_chunk_size >= chunk_size)
3835 level->file_has_custom_elements = TRUE;
3837 return real_chunk_size;
3840 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3842 int element = getMappedElement(getFile16BitBE(file));
3843 int real_chunk_size = 2;
3844 struct ElementInfo *ei = &element_info[element];
3845 struct ElementGroupInfo *group = ei->group;
3850 xx_ei = *ei; // copy element data into temporary buffer
3851 xx_group = *group; // copy group data into temporary buffer
3853 while (!checkEndOfFile(file))
3855 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3858 if (real_chunk_size >= chunk_size)
3865 level->file_has_custom_elements = TRUE;
3867 return real_chunk_size;
3870 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3872 int element = getMappedElement(getFile16BitBE(file));
3873 int real_chunk_size = 2;
3874 struct ElementInfo *ei = &element_info[element];
3876 xx_ei = *ei; // copy element data into temporary buffer
3878 while (!checkEndOfFile(file))
3880 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3883 if (real_chunk_size >= chunk_size)
3889 level->file_has_custom_elements = TRUE;
3891 return real_chunk_size;
3894 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3895 struct LevelFileInfo *level_file_info,
3896 boolean level_info_only)
3898 char *filename = level_file_info->filename;
3899 char cookie[MAX_LINE_LEN];
3900 char chunk_name[CHUNK_ID_LEN + 1];
3904 if (!(file = openFile(filename, MODE_READ)))
3906 level->no_valid_file = TRUE;
3907 level->no_level_file = TRUE;
3909 if (level_info_only)
3912 Warn("cannot read level '%s' -- using empty level", filename);
3914 if (!setup.editor.use_template_for_new_levels)
3917 // if level file not found, try to initialize level data from template
3918 filename = getGlobalLevelTemplateFilename();
3920 if (!(file = openFile(filename, MODE_READ)))
3923 // default: for empty levels, use level template for custom elements
3924 level->use_custom_template = TRUE;
3926 level->no_valid_file = FALSE;
3929 getFileChunkBE(file, chunk_name, NULL);
3930 if (strEqual(chunk_name, "RND1"))
3932 getFile32BitBE(file); // not used
3934 getFileChunkBE(file, chunk_name, NULL);
3935 if (!strEqual(chunk_name, "CAVE"))
3937 level->no_valid_file = TRUE;
3939 Warn("unknown format of level file '%s'", filename);
3946 else // check for pre-2.0 file format with cookie string
3948 strcpy(cookie, chunk_name);
3949 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3951 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3952 cookie[strlen(cookie) - 1] = '\0';
3954 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3956 level->no_valid_file = TRUE;
3958 Warn("unknown format of level file '%s'", filename);
3965 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3967 level->no_valid_file = TRUE;
3969 Warn("unsupported version of level file '%s'", filename);
3976 // pre-2.0 level files have no game version, so use file version here
3977 level->game_version = level->file_version;
3980 if (level->file_version < FILE_VERSION_1_2)
3982 // level files from versions before 1.2.0 without chunk structure
3983 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3984 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3992 int (*loader)(File *, int, struct LevelInfo *);
3996 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3997 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3998 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3999 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4000 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4001 { "INFO", -1, LoadLevel_INFO },
4002 { "BODY", -1, LoadLevel_BODY },
4003 { "CONT", -1, LoadLevel_CONT },
4004 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4005 { "CNT3", -1, LoadLevel_CNT3 },
4006 { "CUS1", -1, LoadLevel_CUS1 },
4007 { "CUS2", -1, LoadLevel_CUS2 },
4008 { "CUS3", -1, LoadLevel_CUS3 },
4009 { "CUS4", -1, LoadLevel_CUS4 },
4010 { "GRP1", -1, LoadLevel_GRP1 },
4011 { "CONF", -1, LoadLevel_CONF },
4012 { "ELEM", -1, LoadLevel_ELEM },
4013 { "NOTE", -1, LoadLevel_NOTE },
4014 { "CUSX", -1, LoadLevel_CUSX },
4015 { "GRPX", -1, LoadLevel_GRPX },
4016 { "EMPX", -1, LoadLevel_EMPX },
4021 while (getFileChunkBE(file, chunk_name, &chunk_size))
4025 while (chunk_info[i].name != NULL &&
4026 !strEqual(chunk_name, chunk_info[i].name))
4029 if (chunk_info[i].name == NULL)
4031 Warn("unknown chunk '%s' in level file '%s'",
4032 chunk_name, filename);
4034 ReadUnusedBytesFromFile(file, chunk_size);
4036 else if (chunk_info[i].size != -1 &&
4037 chunk_info[i].size != chunk_size)
4039 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4040 chunk_size, chunk_name, filename);
4042 ReadUnusedBytesFromFile(file, chunk_size);
4046 // call function to load this level chunk
4047 int chunk_size_expected =
4048 (chunk_info[i].loader)(file, chunk_size, level);
4050 if (chunk_size_expected < 0)
4052 Warn("error reading chunk '%s' in level file '%s'",
4053 chunk_name, filename);
4058 // the size of some chunks cannot be checked before reading other
4059 // chunks first (like "HEAD" and "BODY") that contain some header
4060 // information, so check them here
4061 if (chunk_size_expected != chunk_size)
4063 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4064 chunk_size, chunk_name, filename);
4076 // ----------------------------------------------------------------------------
4077 // functions for loading BD level
4078 // ----------------------------------------------------------------------------
4080 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4082 struct LevelInfo_BD *level_bd = level->native_bd_level;
4083 GdCave *cave = NULL; // will be changed below
4084 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4085 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4088 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4090 // cave and map newly allocated when set to defaults above
4091 cave = level_bd->cave;
4094 cave->intermission = level->bd_intermission;
4097 cave->level_time[0] = level->time;
4098 cave->level_diamonds[0] = level->gems_needed;
4101 cave->scheduling = level->bd_scheduling_type;
4102 cave->pal_timing = level->bd_pal_timing;
4103 cave->level_speed[0] = level->bd_cycle_delay_ms;
4104 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4105 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4106 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4109 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4110 cave->diamond_value = level->score[SC_EMERALD];
4111 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4113 // compatibility settings
4114 cave->lineshift = level->bd_line_shifting_borders;
4115 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4116 cave->short_explosions = level->bd_short_explosions;
4117 cave->gravity_affects_all = level->bd_gravity_affects_all;
4119 // player properties
4120 cave->diagonal_movements = level->bd_diagonal_movements;
4121 cave->active_is_first_found = level->bd_topmost_player_active;
4122 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4123 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4124 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4125 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4127 // element properties
4128 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4129 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4130 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4131 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4132 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4133 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4134 cave->level_magic_wall_time[0] = level->time_magic_wall;
4135 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4136 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4138 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4139 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4140 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4141 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4142 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4143 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4144 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4146 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4147 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4148 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4149 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4150 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4151 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4152 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4153 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4154 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4155 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4156 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4158 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4159 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4160 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4161 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4162 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4163 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4165 cave->slime_predictable = level->bd_slime_is_predictable;
4166 cave->slime_correct_random = level->bd_slime_correct_random;
4167 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4168 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4169 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4170 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4171 cave->slime_eats_1 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4172 cave->slime_converts_1 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4173 cave->slime_eats_2 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4174 cave->slime_converts_2 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4175 cave->slime_eats_3 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4176 cave->slime_converts_3 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4178 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4179 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4180 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4182 cave->biter_delay_frame = level->bd_biter_move_delay;
4183 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4185 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4187 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4189 cave->replicators_active = level->bd_replicators_active;
4190 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4192 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4193 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4195 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4197 cave->nut_turns_to_when_crushed = map_element_RND_to_BD_cave(level->bd_nut_content);
4199 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4200 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4201 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4203 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4204 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4207 strncpy(cave->name, level->name, sizeof(GdString));
4208 cave->name[sizeof(GdString) - 1] = '\0';
4210 // playfield elements
4211 for (x = 0; x < cave->w; x++)
4212 for (y = 0; y < cave->h; y++)
4213 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4216 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4218 struct LevelInfo_BD *level_bd = level->native_bd_level;
4219 GdCave *cave = level_bd->cave;
4220 int bd_level_nr = level_bd->level_nr;
4223 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4224 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4227 level->bd_intermission = cave->intermission;
4230 level->time = cave->level_time[bd_level_nr];
4231 level->gems_needed = cave->level_diamonds[bd_level_nr];
4234 level->bd_scheduling_type = cave->scheduling;
4235 level->bd_pal_timing = cave->pal_timing;
4236 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4237 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4238 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4239 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4242 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4243 level->score[SC_EMERALD] = cave->diamond_value;
4244 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4246 // compatibility settings
4247 level->bd_line_shifting_borders = cave->lineshift;
4248 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4249 level->bd_short_explosions = cave->short_explosions;
4250 level->bd_gravity_affects_all = cave->gravity_affects_all;
4252 // player properties
4253 level->bd_diagonal_movements = cave->diagonal_movements;
4254 level->bd_topmost_player_active = cave->active_is_first_found;
4255 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4256 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4257 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4258 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4260 // element properties
4261 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4262 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4263 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4264 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4265 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4266 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4267 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4268 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4269 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4271 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4272 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4273 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4274 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4275 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4276 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4277 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4279 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4280 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4281 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4282 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4283 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4284 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4285 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4286 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4287 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4288 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4289 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4291 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4292 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4293 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4294 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4295 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4296 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4298 level->bd_slime_is_predictable = cave->slime_predictable;
4299 level->bd_slime_correct_random = cave->slime_correct_random;
4300 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4301 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4302 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4303 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4304 level->bd_slime_eats_element_1 = map_element_BD_to_RND_cave(cave->slime_eats_1);
4305 level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4306 level->bd_slime_eats_element_2 = map_element_BD_to_RND_cave(cave->slime_eats_2);
4307 level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4308 level->bd_slime_eats_element_3 = map_element_BD_to_RND_cave(cave->slime_eats_3);
4309 level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4311 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4312 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4313 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4315 level->bd_biter_move_delay = cave->biter_delay_frame;
4316 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4318 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4320 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4322 level->bd_replicators_active = cave->replicators_active;
4323 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4325 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4326 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4328 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4330 level->bd_nut_content = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4332 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4333 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4334 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4336 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4337 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4340 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4342 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4343 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4345 // playfield elements
4346 for (x = 0; x < level->fieldx; x++)
4347 for (y = 0; y < level->fieldy; y++)
4348 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4350 checked_free(cave_name);
4353 static void setTapeInfoToDefaults(void);
4355 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4357 struct LevelInfo_BD *level_bd = level->native_bd_level;
4358 GdCave *cave = level_bd->cave;
4359 GdReplay *replay = level_bd->replay;
4365 // always start with reliable default values
4366 setTapeInfoToDefaults();
4368 tape.level_nr = level_nr; // (currently not used)
4369 tape.random_seed = replay->seed;
4371 TapeSetDateFromIsoDateString(replay->date);
4374 tape.pos[tape.counter].delay = 0;
4376 tape.bd_replay = TRUE;
4378 // all time calculations only used to display approximate tape time
4379 int cave_speed = cave->speed;
4380 int milliseconds_game = 0;
4381 int milliseconds_elapsed = 20;
4383 for (i = 0; i < replay->movements->len; i++)
4385 int replay_action = replay->movements->data[i];
4386 int tape_action = map_action_BD_to_RND(replay_action);
4387 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4388 boolean success = 0;
4392 success = TapeAddAction(action);
4394 milliseconds_game += milliseconds_elapsed;
4396 if (milliseconds_game >= cave_speed)
4398 milliseconds_game -= cave_speed;
4405 tape.pos[tape.counter].delay = 0;
4406 tape.pos[tape.counter].action[0] = 0;
4410 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4416 TapeHaltRecording();
4420 // ----------------------------------------------------------------------------
4421 // functions for loading EM level
4422 // ----------------------------------------------------------------------------
4424 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4426 static int ball_xy[8][2] =
4437 struct LevelInfo_EM *level_em = level->native_em_level;
4438 struct CAVE *cav = level_em->cav;
4441 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4442 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4444 cav->time_seconds = level->time;
4445 cav->gems_needed = level->gems_needed;
4447 cav->emerald_score = level->score[SC_EMERALD];
4448 cav->diamond_score = level->score[SC_DIAMOND];
4449 cav->alien_score = level->score[SC_ROBOT];
4450 cav->tank_score = level->score[SC_SPACESHIP];
4451 cav->bug_score = level->score[SC_BUG];
4452 cav->eater_score = level->score[SC_YAMYAM];
4453 cav->nut_score = level->score[SC_NUT];
4454 cav->dynamite_score = level->score[SC_DYNAMITE];
4455 cav->key_score = level->score[SC_KEY];
4456 cav->exit_score = level->score[SC_TIME_BONUS];
4458 cav->num_eater_arrays = level->num_yamyam_contents;
4460 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4461 for (y = 0; y < 3; y++)
4462 for (x = 0; x < 3; x++)
4463 cav->eater_array[i][y * 3 + x] =
4464 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4466 cav->amoeba_time = level->amoeba_speed;
4467 cav->wonderwall_time = level->time_magic_wall;
4468 cav->wheel_time = level->time_wheel;
4470 cav->android_move_time = level->android_move_time;
4471 cav->android_clone_time = level->android_clone_time;
4472 cav->ball_random = level->ball_random;
4473 cav->ball_active = level->ball_active_initial;
4474 cav->ball_time = level->ball_time;
4475 cav->num_ball_arrays = level->num_ball_contents;
4477 cav->lenses_score = level->lenses_score;
4478 cav->magnify_score = level->magnify_score;
4479 cav->slurp_score = level->slurp_score;
4481 cav->lenses_time = level->lenses_time;
4482 cav->magnify_time = level->magnify_time;
4484 cav->wind_time = 9999;
4485 cav->wind_direction =
4486 map_direction_RND_to_EM(level->wind_direction_initial);
4488 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4489 for (j = 0; j < 8; j++)
4490 cav->ball_array[i][j] =
4491 map_element_RND_to_EM_cave(level->ball_content[i].
4492 e[ball_xy[j][0]][ball_xy[j][1]]);
4494 map_android_clone_elements_RND_to_EM(level);
4496 // first fill the complete playfield with the empty space element
4497 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4498 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4499 cav->cave[x][y] = Cblank;
4501 // then copy the real level contents from level file into the playfield
4502 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4504 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4506 if (level->field[x][y] == EL_AMOEBA_DEAD)
4507 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4509 cav->cave[x][y] = new_element;
4512 for (i = 0; i < MAX_PLAYERS; i++)
4514 cav->player_x[i] = -1;
4515 cav->player_y[i] = -1;
4518 // initialize player positions and delete players from the playfield
4519 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4521 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4523 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4525 cav->player_x[player_nr] = x;
4526 cav->player_y[player_nr] = y;
4528 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4533 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4535 static int ball_xy[8][2] =
4546 struct LevelInfo_EM *level_em = level->native_em_level;
4547 struct CAVE *cav = level_em->cav;
4550 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4551 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4553 level->time = cav->time_seconds;
4554 level->gems_needed = cav->gems_needed;
4556 sprintf(level->name, "Level %d", level->file_info.nr);
4558 level->score[SC_EMERALD] = cav->emerald_score;
4559 level->score[SC_DIAMOND] = cav->diamond_score;
4560 level->score[SC_ROBOT] = cav->alien_score;
4561 level->score[SC_SPACESHIP] = cav->tank_score;
4562 level->score[SC_BUG] = cav->bug_score;
4563 level->score[SC_YAMYAM] = cav->eater_score;
4564 level->score[SC_NUT] = cav->nut_score;
4565 level->score[SC_DYNAMITE] = cav->dynamite_score;
4566 level->score[SC_KEY] = cav->key_score;
4567 level->score[SC_TIME_BONUS] = cav->exit_score;
4569 level->num_yamyam_contents = cav->num_eater_arrays;
4571 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4572 for (y = 0; y < 3; y++)
4573 for (x = 0; x < 3; x++)
4574 level->yamyam_content[i].e[x][y] =
4575 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4577 level->amoeba_speed = cav->amoeba_time;
4578 level->time_magic_wall = cav->wonderwall_time;
4579 level->time_wheel = cav->wheel_time;
4581 level->android_move_time = cav->android_move_time;
4582 level->android_clone_time = cav->android_clone_time;
4583 level->ball_random = cav->ball_random;
4584 level->ball_active_initial = cav->ball_active;
4585 level->ball_time = cav->ball_time;
4586 level->num_ball_contents = cav->num_ball_arrays;
4588 level->lenses_score = cav->lenses_score;
4589 level->magnify_score = cav->magnify_score;
4590 level->slurp_score = cav->slurp_score;
4592 level->lenses_time = cav->lenses_time;
4593 level->magnify_time = cav->magnify_time;
4595 level->wind_direction_initial =
4596 map_direction_EM_to_RND(cav->wind_direction);
4598 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4599 for (j = 0; j < 8; j++)
4600 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4601 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4603 map_android_clone_elements_EM_to_RND(level);
4605 // convert the playfield (some elements need special treatment)
4606 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4608 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4610 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4611 new_element = EL_AMOEBA_DEAD;
4613 level->field[x][y] = new_element;
4616 for (i = 0; i < MAX_PLAYERS; i++)
4618 // in case of all players set to the same field, use the first player
4619 int nr = MAX_PLAYERS - i - 1;
4620 int jx = cav->player_x[nr];
4621 int jy = cav->player_y[nr];
4623 if (jx != -1 && jy != -1)
4624 level->field[jx][jy] = EL_PLAYER_1 + nr;
4627 // time score is counted for each 10 seconds left in Emerald Mine levels
4628 level->time_score_base = 10;
4632 // ----------------------------------------------------------------------------
4633 // functions for loading SP level
4634 // ----------------------------------------------------------------------------
4636 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4638 struct LevelInfo_SP *level_sp = level->native_sp_level;
4639 LevelInfoType *header = &level_sp->header;
4642 level_sp->width = level->fieldx;
4643 level_sp->height = level->fieldy;
4645 for (x = 0; x < level->fieldx; x++)
4646 for (y = 0; y < level->fieldy; y++)
4647 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4649 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4651 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4652 header->LevelTitle[i] = level->name[i];
4653 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4655 header->InfotronsNeeded = level->gems_needed;
4657 header->SpecialPortCount = 0;
4659 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4661 boolean gravity_port_found = FALSE;
4662 boolean gravity_port_valid = FALSE;
4663 int gravity_port_flag;
4664 int gravity_port_base_element;
4665 int element = level->field[x][y];
4667 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4668 element <= EL_SP_GRAVITY_ON_PORT_UP)
4670 gravity_port_found = TRUE;
4671 gravity_port_valid = TRUE;
4672 gravity_port_flag = 1;
4673 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4675 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4676 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4678 gravity_port_found = TRUE;
4679 gravity_port_valid = TRUE;
4680 gravity_port_flag = 0;
4681 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4683 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4684 element <= EL_SP_GRAVITY_PORT_UP)
4686 // change R'n'D style gravity inverting special port to normal port
4687 // (there are no gravity inverting ports in native Supaplex engine)
4689 gravity_port_found = TRUE;
4690 gravity_port_valid = FALSE;
4691 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4694 if (gravity_port_found)
4696 if (gravity_port_valid &&
4697 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4699 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4701 port->PortLocation = (y * level->fieldx + x) * 2;
4702 port->Gravity = gravity_port_flag;
4704 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4706 header->SpecialPortCount++;
4710 // change special gravity port to normal port
4712 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4715 level_sp->playfield[x][y] = element - EL_SP_START;
4720 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4722 struct LevelInfo_SP *level_sp = level->native_sp_level;
4723 LevelInfoType *header = &level_sp->header;
4724 boolean num_invalid_elements = 0;
4727 level->fieldx = level_sp->width;
4728 level->fieldy = level_sp->height;
4730 for (x = 0; x < level->fieldx; x++)
4732 for (y = 0; y < level->fieldy; y++)
4734 int element_old = level_sp->playfield[x][y];
4735 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4737 if (element_new == EL_UNKNOWN)
4739 num_invalid_elements++;
4741 Debug("level:native:SP", "invalid element %d at position %d, %d",
4745 level->field[x][y] = element_new;
4749 if (num_invalid_elements > 0)
4750 Warn("found %d invalid elements%s", num_invalid_elements,
4751 (!options.debug ? " (use '--debug' for more details)" : ""));
4753 for (i = 0; i < MAX_PLAYERS; i++)
4754 level->initial_player_gravity[i] =
4755 (header->InitialGravity == 1 ? TRUE : FALSE);
4757 // skip leading spaces
4758 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4759 if (header->LevelTitle[i] != ' ')
4763 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4764 level->name[j] = header->LevelTitle[i];
4765 level->name[j] = '\0';
4767 // cut trailing spaces
4769 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4770 level->name[j - 1] = '\0';
4772 level->gems_needed = header->InfotronsNeeded;
4774 for (i = 0; i < header->SpecialPortCount; i++)
4776 SpecialPortType *port = &header->SpecialPort[i];
4777 int port_location = port->PortLocation;
4778 int gravity = port->Gravity;
4779 int port_x, port_y, port_element;
4781 port_x = (port_location / 2) % level->fieldx;
4782 port_y = (port_location / 2) / level->fieldx;
4784 if (port_x < 0 || port_x >= level->fieldx ||
4785 port_y < 0 || port_y >= level->fieldy)
4787 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4792 port_element = level->field[port_x][port_y];
4794 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4795 port_element > EL_SP_GRAVITY_PORT_UP)
4797 Warn("no special port at position (%d, %d)", port_x, port_y);
4802 // change previous (wrong) gravity inverting special port to either
4803 // gravity enabling special port or gravity disabling special port
4804 level->field[port_x][port_y] +=
4805 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4806 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4809 // change special gravity ports without database entries to normal ports
4810 for (x = 0; x < level->fieldx; x++)
4811 for (y = 0; y < level->fieldy; y++)
4812 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4813 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4814 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4816 level->time = 0; // no time limit
4817 level->amoeba_speed = 0;
4818 level->time_magic_wall = 0;
4819 level->time_wheel = 0;
4820 level->amoeba_content = EL_EMPTY;
4822 // original Supaplex does not use score values -- rate by playing time
4823 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4824 level->score[i] = 0;
4826 level->rate_time_over_score = TRUE;
4828 // there are no yamyams in supaplex levels
4829 for (i = 0; i < level->num_yamyam_contents; i++)
4830 for (x = 0; x < 3; x++)
4831 for (y = 0; y < 3; y++)
4832 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4835 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4837 struct LevelInfo_SP *level_sp = level->native_sp_level;
4838 struct DemoInfo_SP *demo = &level_sp->demo;
4841 // always start with reliable default values
4842 demo->is_available = FALSE;
4845 if (TAPE_IS_EMPTY(tape))
4848 demo->level_nr = tape.level_nr; // (currently not used)
4850 level_sp->header.DemoRandomSeed = tape.random_seed;
4854 for (i = 0; i < tape.length; i++)
4856 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4857 int demo_repeat = tape.pos[i].delay;
4858 int demo_entries = (demo_repeat + 15) / 16;
4860 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4862 Warn("tape truncated: size exceeds maximum SP demo size %d",
4868 for (j = 0; j < demo_repeat / 16; j++)
4869 demo->data[demo->length++] = 0xf0 | demo_action;
4871 if (demo_repeat % 16)
4872 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4875 demo->is_available = TRUE;
4878 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4880 struct LevelInfo_SP *level_sp = level->native_sp_level;
4881 struct DemoInfo_SP *demo = &level_sp->demo;
4882 char *filename = level->file_info.filename;
4885 // always start with reliable default values
4886 setTapeInfoToDefaults();
4888 if (!demo->is_available)
4891 tape.level_nr = demo->level_nr; // (currently not used)
4892 tape.random_seed = level_sp->header.DemoRandomSeed;
4894 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4897 tape.pos[tape.counter].delay = 0;
4899 for (i = 0; i < demo->length; i++)
4901 int demo_action = demo->data[i] & 0x0f;
4902 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4903 int tape_action = map_key_SP_to_RND(demo_action);
4904 int tape_repeat = demo_repeat + 1;
4905 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4906 boolean success = 0;
4909 for (j = 0; j < tape_repeat; j++)
4910 success = TapeAddAction(action);
4914 Warn("SP demo truncated: size exceeds maximum tape size %d",
4921 TapeHaltRecording();
4925 // ----------------------------------------------------------------------------
4926 // functions for loading MM level
4927 // ----------------------------------------------------------------------------
4929 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4931 struct LevelInfo_MM *level_mm = level->native_mm_level;
4934 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4935 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4937 level_mm->time = level->time;
4938 level_mm->kettles_needed = level->gems_needed;
4939 level_mm->auto_count_kettles = level->auto_count_gems;
4941 level_mm->mm_laser_red = level->mm_laser_red;
4942 level_mm->mm_laser_green = level->mm_laser_green;
4943 level_mm->mm_laser_blue = level->mm_laser_blue;
4945 level_mm->df_laser_red = level->df_laser_red;
4946 level_mm->df_laser_green = level->df_laser_green;
4947 level_mm->df_laser_blue = level->df_laser_blue;
4949 strcpy(level_mm->name, level->name);
4950 strcpy(level_mm->author, level->author);
4952 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4953 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4954 level_mm->score[SC_KEY] = level->score[SC_KEY];
4955 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4956 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4958 level_mm->amoeba_speed = level->amoeba_speed;
4959 level_mm->time_fuse = level->mm_time_fuse;
4960 level_mm->time_bomb = level->mm_time_bomb;
4961 level_mm->time_ball = level->mm_time_ball;
4962 level_mm->time_block = level->mm_time_block;
4964 level_mm->num_ball_contents = level->num_mm_ball_contents;
4965 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4966 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4967 level_mm->explode_ball = level->explode_mm_ball;
4969 for (i = 0; i < level->num_mm_ball_contents; i++)
4970 level_mm->ball_content[i] =
4971 map_element_RND_to_MM(level->mm_ball_content[i]);
4973 for (x = 0; x < level->fieldx; x++)
4974 for (y = 0; y < level->fieldy; y++)
4976 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4979 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4981 struct LevelInfo_MM *level_mm = level->native_mm_level;
4984 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4985 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4987 level->time = level_mm->time;
4988 level->gems_needed = level_mm->kettles_needed;
4989 level->auto_count_gems = level_mm->auto_count_kettles;
4991 level->mm_laser_red = level_mm->mm_laser_red;
4992 level->mm_laser_green = level_mm->mm_laser_green;
4993 level->mm_laser_blue = level_mm->mm_laser_blue;
4995 level->df_laser_red = level_mm->df_laser_red;
4996 level->df_laser_green = level_mm->df_laser_green;
4997 level->df_laser_blue = level_mm->df_laser_blue;
4999 strcpy(level->name, level_mm->name);
5001 // only overwrite author from 'levelinfo.conf' if author defined in level
5002 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5003 strcpy(level->author, level_mm->author);
5005 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5006 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5007 level->score[SC_KEY] = level_mm->score[SC_KEY];
5008 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5009 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5011 level->amoeba_speed = level_mm->amoeba_speed;
5012 level->mm_time_fuse = level_mm->time_fuse;
5013 level->mm_time_bomb = level_mm->time_bomb;
5014 level->mm_time_ball = level_mm->time_ball;
5015 level->mm_time_block = level_mm->time_block;
5017 level->num_mm_ball_contents = level_mm->num_ball_contents;
5018 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5019 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5020 level->explode_mm_ball = level_mm->explode_ball;
5022 for (i = 0; i < level->num_mm_ball_contents; i++)
5023 level->mm_ball_content[i] =
5024 map_element_MM_to_RND(level_mm->ball_content[i]);
5026 for (x = 0; x < level->fieldx; x++)
5027 for (y = 0; y < level->fieldy; y++)
5028 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5032 // ----------------------------------------------------------------------------
5033 // functions for loading DC level
5034 // ----------------------------------------------------------------------------
5036 #define DC_LEVEL_HEADER_SIZE 344
5038 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5041 static int last_data_encoded;
5045 int diff_hi, diff_lo;
5046 int data_hi, data_lo;
5047 unsigned short data_decoded;
5051 last_data_encoded = 0;
5058 diff = data_encoded - last_data_encoded;
5059 diff_hi = diff & ~0xff;
5060 diff_lo = diff & 0xff;
5064 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5065 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5066 data_hi = data_hi & 0xff00;
5068 data_decoded = data_hi | data_lo;
5070 last_data_encoded = data_encoded;
5072 offset1 = (offset1 + 1) % 31;
5073 offset2 = offset2 & 0xff;
5075 return data_decoded;
5078 static int getMappedElement_DC(int element)
5086 // 0x0117 - 0x036e: (?)
5089 // 0x042d - 0x0684: (?)
5105 element = EL_CRYSTAL;
5108 case 0x0e77: // quicksand (boulder)
5109 element = EL_QUICKSAND_FAST_FULL;
5112 case 0x0e99: // slow quicksand (boulder)
5113 element = EL_QUICKSAND_FULL;
5117 element = EL_EM_EXIT_OPEN;
5121 element = EL_EM_EXIT_CLOSED;
5125 element = EL_EM_STEEL_EXIT_OPEN;
5129 element = EL_EM_STEEL_EXIT_CLOSED;
5132 case 0x0f4f: // dynamite (lit 1)
5133 element = EL_EM_DYNAMITE_ACTIVE;
5136 case 0x0f57: // dynamite (lit 2)
5137 element = EL_EM_DYNAMITE_ACTIVE;
5140 case 0x0f5f: // dynamite (lit 3)
5141 element = EL_EM_DYNAMITE_ACTIVE;
5144 case 0x0f67: // dynamite (lit 4)
5145 element = EL_EM_DYNAMITE_ACTIVE;
5152 element = EL_AMOEBA_WET;
5156 element = EL_AMOEBA_DROP;
5160 element = EL_DC_MAGIC_WALL;
5164 element = EL_SPACESHIP_UP;
5168 element = EL_SPACESHIP_DOWN;
5172 element = EL_SPACESHIP_LEFT;
5176 element = EL_SPACESHIP_RIGHT;
5180 element = EL_BUG_UP;
5184 element = EL_BUG_DOWN;
5188 element = EL_BUG_LEFT;
5192 element = EL_BUG_RIGHT;
5196 element = EL_MOLE_UP;
5200 element = EL_MOLE_DOWN;
5204 element = EL_MOLE_LEFT;
5208 element = EL_MOLE_RIGHT;
5216 element = EL_YAMYAM_UP;
5220 element = EL_SWITCHGATE_OPEN;
5224 element = EL_SWITCHGATE_CLOSED;
5228 element = EL_DC_SWITCHGATE_SWITCH_UP;
5232 element = EL_TIMEGATE_CLOSED;
5235 case 0x144c: // conveyor belt switch (green)
5236 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5239 case 0x144f: // conveyor belt switch (red)
5240 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5243 case 0x1452: // conveyor belt switch (blue)
5244 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5248 element = EL_CONVEYOR_BELT_3_MIDDLE;
5252 element = EL_CONVEYOR_BELT_3_LEFT;
5256 element = EL_CONVEYOR_BELT_3_RIGHT;
5260 element = EL_CONVEYOR_BELT_1_MIDDLE;
5264 element = EL_CONVEYOR_BELT_1_LEFT;
5268 element = EL_CONVEYOR_BELT_1_RIGHT;
5272 element = EL_CONVEYOR_BELT_4_MIDDLE;
5276 element = EL_CONVEYOR_BELT_4_LEFT;
5280 element = EL_CONVEYOR_BELT_4_RIGHT;
5284 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5288 element = EL_EXPANDABLE_WALL_VERTICAL;
5292 element = EL_EXPANDABLE_WALL_ANY;
5295 case 0x14ce: // growing steel wall (left/right)
5296 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5299 case 0x14df: // growing steel wall (up/down)
5300 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5303 case 0x14e8: // growing steel wall (up/down/left/right)
5304 element = EL_EXPANDABLE_STEELWALL_ANY;
5308 element = EL_SHIELD_DEADLY;
5312 element = EL_EXTRA_TIME;
5320 element = EL_EMPTY_SPACE;
5323 case 0x1578: // quicksand (empty)
5324 element = EL_QUICKSAND_FAST_EMPTY;
5327 case 0x1579: // slow quicksand (empty)
5328 element = EL_QUICKSAND_EMPTY;
5338 element = EL_EM_DYNAMITE;
5341 case 0x15a1: // key (red)
5342 element = EL_EM_KEY_1;
5345 case 0x15a2: // key (yellow)
5346 element = EL_EM_KEY_2;
5349 case 0x15a3: // key (blue)
5350 element = EL_EM_KEY_4;
5353 case 0x15a4: // key (green)
5354 element = EL_EM_KEY_3;
5357 case 0x15a5: // key (white)
5358 element = EL_DC_KEY_WHITE;
5362 element = EL_WALL_SLIPPERY;
5369 case 0x15a8: // wall (not round)
5373 case 0x15a9: // (blue)
5374 element = EL_CHAR_A;
5377 case 0x15aa: // (blue)
5378 element = EL_CHAR_B;
5381 case 0x15ab: // (blue)
5382 element = EL_CHAR_C;
5385 case 0x15ac: // (blue)
5386 element = EL_CHAR_D;
5389 case 0x15ad: // (blue)
5390 element = EL_CHAR_E;
5393 case 0x15ae: // (blue)
5394 element = EL_CHAR_F;
5397 case 0x15af: // (blue)
5398 element = EL_CHAR_G;
5401 case 0x15b0: // (blue)
5402 element = EL_CHAR_H;
5405 case 0x15b1: // (blue)
5406 element = EL_CHAR_I;
5409 case 0x15b2: // (blue)
5410 element = EL_CHAR_J;
5413 case 0x15b3: // (blue)
5414 element = EL_CHAR_K;
5417 case 0x15b4: // (blue)
5418 element = EL_CHAR_L;
5421 case 0x15b5: // (blue)
5422 element = EL_CHAR_M;
5425 case 0x15b6: // (blue)
5426 element = EL_CHAR_N;
5429 case 0x15b7: // (blue)
5430 element = EL_CHAR_O;
5433 case 0x15b8: // (blue)
5434 element = EL_CHAR_P;
5437 case 0x15b9: // (blue)
5438 element = EL_CHAR_Q;
5441 case 0x15ba: // (blue)
5442 element = EL_CHAR_R;
5445 case 0x15bb: // (blue)
5446 element = EL_CHAR_S;
5449 case 0x15bc: // (blue)
5450 element = EL_CHAR_T;
5453 case 0x15bd: // (blue)
5454 element = EL_CHAR_U;
5457 case 0x15be: // (blue)
5458 element = EL_CHAR_V;
5461 case 0x15bf: // (blue)
5462 element = EL_CHAR_W;
5465 case 0x15c0: // (blue)
5466 element = EL_CHAR_X;
5469 case 0x15c1: // (blue)
5470 element = EL_CHAR_Y;
5473 case 0x15c2: // (blue)
5474 element = EL_CHAR_Z;
5477 case 0x15c3: // (blue)
5478 element = EL_CHAR_AUMLAUT;
5481 case 0x15c4: // (blue)
5482 element = EL_CHAR_OUMLAUT;
5485 case 0x15c5: // (blue)
5486 element = EL_CHAR_UUMLAUT;
5489 case 0x15c6: // (blue)
5490 element = EL_CHAR_0;
5493 case 0x15c7: // (blue)
5494 element = EL_CHAR_1;
5497 case 0x15c8: // (blue)
5498 element = EL_CHAR_2;
5501 case 0x15c9: // (blue)
5502 element = EL_CHAR_3;
5505 case 0x15ca: // (blue)
5506 element = EL_CHAR_4;
5509 case 0x15cb: // (blue)
5510 element = EL_CHAR_5;
5513 case 0x15cc: // (blue)
5514 element = EL_CHAR_6;
5517 case 0x15cd: // (blue)
5518 element = EL_CHAR_7;
5521 case 0x15ce: // (blue)
5522 element = EL_CHAR_8;
5525 case 0x15cf: // (blue)
5526 element = EL_CHAR_9;
5529 case 0x15d0: // (blue)
5530 element = EL_CHAR_PERIOD;
5533 case 0x15d1: // (blue)
5534 element = EL_CHAR_EXCLAM;
5537 case 0x15d2: // (blue)
5538 element = EL_CHAR_COLON;
5541 case 0x15d3: // (blue)
5542 element = EL_CHAR_LESS;
5545 case 0x15d4: // (blue)
5546 element = EL_CHAR_GREATER;
5549 case 0x15d5: // (blue)
5550 element = EL_CHAR_QUESTION;
5553 case 0x15d6: // (blue)
5554 element = EL_CHAR_COPYRIGHT;
5557 case 0x15d7: // (blue)
5558 element = EL_CHAR_UP;
5561 case 0x15d8: // (blue)
5562 element = EL_CHAR_DOWN;
5565 case 0x15d9: // (blue)
5566 element = EL_CHAR_BUTTON;
5569 case 0x15da: // (blue)
5570 element = EL_CHAR_PLUS;
5573 case 0x15db: // (blue)
5574 element = EL_CHAR_MINUS;
5577 case 0x15dc: // (blue)
5578 element = EL_CHAR_APOSTROPHE;
5581 case 0x15dd: // (blue)
5582 element = EL_CHAR_PARENLEFT;
5585 case 0x15de: // (blue)
5586 element = EL_CHAR_PARENRIGHT;
5589 case 0x15df: // (green)
5590 element = EL_CHAR_A;
5593 case 0x15e0: // (green)
5594 element = EL_CHAR_B;
5597 case 0x15e1: // (green)
5598 element = EL_CHAR_C;
5601 case 0x15e2: // (green)
5602 element = EL_CHAR_D;
5605 case 0x15e3: // (green)
5606 element = EL_CHAR_E;
5609 case 0x15e4: // (green)
5610 element = EL_CHAR_F;
5613 case 0x15e5: // (green)
5614 element = EL_CHAR_G;
5617 case 0x15e6: // (green)
5618 element = EL_CHAR_H;
5621 case 0x15e7: // (green)
5622 element = EL_CHAR_I;
5625 case 0x15e8: // (green)
5626 element = EL_CHAR_J;
5629 case 0x15e9: // (green)
5630 element = EL_CHAR_K;
5633 case 0x15ea: // (green)
5634 element = EL_CHAR_L;
5637 case 0x15eb: // (green)
5638 element = EL_CHAR_M;
5641 case 0x15ec: // (green)
5642 element = EL_CHAR_N;
5645 case 0x15ed: // (green)
5646 element = EL_CHAR_O;
5649 case 0x15ee: // (green)
5650 element = EL_CHAR_P;
5653 case 0x15ef: // (green)
5654 element = EL_CHAR_Q;
5657 case 0x15f0: // (green)
5658 element = EL_CHAR_R;
5661 case 0x15f1: // (green)
5662 element = EL_CHAR_S;
5665 case 0x15f2: // (green)
5666 element = EL_CHAR_T;
5669 case 0x15f3: // (green)
5670 element = EL_CHAR_U;
5673 case 0x15f4: // (green)
5674 element = EL_CHAR_V;
5677 case 0x15f5: // (green)
5678 element = EL_CHAR_W;
5681 case 0x15f6: // (green)
5682 element = EL_CHAR_X;
5685 case 0x15f7: // (green)
5686 element = EL_CHAR_Y;
5689 case 0x15f8: // (green)
5690 element = EL_CHAR_Z;
5693 case 0x15f9: // (green)
5694 element = EL_CHAR_AUMLAUT;
5697 case 0x15fa: // (green)
5698 element = EL_CHAR_OUMLAUT;
5701 case 0x15fb: // (green)
5702 element = EL_CHAR_UUMLAUT;
5705 case 0x15fc: // (green)
5706 element = EL_CHAR_0;
5709 case 0x15fd: // (green)
5710 element = EL_CHAR_1;
5713 case 0x15fe: // (green)
5714 element = EL_CHAR_2;
5717 case 0x15ff: // (green)
5718 element = EL_CHAR_3;
5721 case 0x1600: // (green)
5722 element = EL_CHAR_4;
5725 case 0x1601: // (green)
5726 element = EL_CHAR_5;
5729 case 0x1602: // (green)
5730 element = EL_CHAR_6;
5733 case 0x1603: // (green)
5734 element = EL_CHAR_7;
5737 case 0x1604: // (green)
5738 element = EL_CHAR_8;
5741 case 0x1605: // (green)
5742 element = EL_CHAR_9;
5745 case 0x1606: // (green)
5746 element = EL_CHAR_PERIOD;
5749 case 0x1607: // (green)
5750 element = EL_CHAR_EXCLAM;
5753 case 0x1608: // (green)
5754 element = EL_CHAR_COLON;
5757 case 0x1609: // (green)
5758 element = EL_CHAR_LESS;
5761 case 0x160a: // (green)
5762 element = EL_CHAR_GREATER;
5765 case 0x160b: // (green)
5766 element = EL_CHAR_QUESTION;
5769 case 0x160c: // (green)
5770 element = EL_CHAR_COPYRIGHT;
5773 case 0x160d: // (green)
5774 element = EL_CHAR_UP;
5777 case 0x160e: // (green)
5778 element = EL_CHAR_DOWN;
5781 case 0x160f: // (green)
5782 element = EL_CHAR_BUTTON;
5785 case 0x1610: // (green)
5786 element = EL_CHAR_PLUS;
5789 case 0x1611: // (green)
5790 element = EL_CHAR_MINUS;
5793 case 0x1612: // (green)
5794 element = EL_CHAR_APOSTROPHE;
5797 case 0x1613: // (green)
5798 element = EL_CHAR_PARENLEFT;
5801 case 0x1614: // (green)
5802 element = EL_CHAR_PARENRIGHT;
5805 case 0x1615: // (blue steel)
5806 element = EL_STEEL_CHAR_A;
5809 case 0x1616: // (blue steel)
5810 element = EL_STEEL_CHAR_B;
5813 case 0x1617: // (blue steel)
5814 element = EL_STEEL_CHAR_C;
5817 case 0x1618: // (blue steel)
5818 element = EL_STEEL_CHAR_D;
5821 case 0x1619: // (blue steel)
5822 element = EL_STEEL_CHAR_E;
5825 case 0x161a: // (blue steel)
5826 element = EL_STEEL_CHAR_F;
5829 case 0x161b: // (blue steel)
5830 element = EL_STEEL_CHAR_G;
5833 case 0x161c: // (blue steel)
5834 element = EL_STEEL_CHAR_H;
5837 case 0x161d: // (blue steel)
5838 element = EL_STEEL_CHAR_I;
5841 case 0x161e: // (blue steel)
5842 element = EL_STEEL_CHAR_J;
5845 case 0x161f: // (blue steel)
5846 element = EL_STEEL_CHAR_K;
5849 case 0x1620: // (blue steel)
5850 element = EL_STEEL_CHAR_L;
5853 case 0x1621: // (blue steel)
5854 element = EL_STEEL_CHAR_M;
5857 case 0x1622: // (blue steel)
5858 element = EL_STEEL_CHAR_N;
5861 case 0x1623: // (blue steel)
5862 element = EL_STEEL_CHAR_O;
5865 case 0x1624: // (blue steel)
5866 element = EL_STEEL_CHAR_P;
5869 case 0x1625: // (blue steel)
5870 element = EL_STEEL_CHAR_Q;
5873 case 0x1626: // (blue steel)
5874 element = EL_STEEL_CHAR_R;
5877 case 0x1627: // (blue steel)
5878 element = EL_STEEL_CHAR_S;
5881 case 0x1628: // (blue steel)
5882 element = EL_STEEL_CHAR_T;
5885 case 0x1629: // (blue steel)
5886 element = EL_STEEL_CHAR_U;
5889 case 0x162a: // (blue steel)
5890 element = EL_STEEL_CHAR_V;
5893 case 0x162b: // (blue steel)
5894 element = EL_STEEL_CHAR_W;
5897 case 0x162c: // (blue steel)
5898 element = EL_STEEL_CHAR_X;
5901 case 0x162d: // (blue steel)
5902 element = EL_STEEL_CHAR_Y;
5905 case 0x162e: // (blue steel)
5906 element = EL_STEEL_CHAR_Z;
5909 case 0x162f: // (blue steel)
5910 element = EL_STEEL_CHAR_AUMLAUT;
5913 case 0x1630: // (blue steel)
5914 element = EL_STEEL_CHAR_OUMLAUT;
5917 case 0x1631: // (blue steel)
5918 element = EL_STEEL_CHAR_UUMLAUT;
5921 case 0x1632: // (blue steel)
5922 element = EL_STEEL_CHAR_0;
5925 case 0x1633: // (blue steel)
5926 element = EL_STEEL_CHAR_1;
5929 case 0x1634: // (blue steel)
5930 element = EL_STEEL_CHAR_2;
5933 case 0x1635: // (blue steel)
5934 element = EL_STEEL_CHAR_3;
5937 case 0x1636: // (blue steel)
5938 element = EL_STEEL_CHAR_4;
5941 case 0x1637: // (blue steel)
5942 element = EL_STEEL_CHAR_5;
5945 case 0x1638: // (blue steel)
5946 element = EL_STEEL_CHAR_6;
5949 case 0x1639: // (blue steel)
5950 element = EL_STEEL_CHAR_7;
5953 case 0x163a: // (blue steel)
5954 element = EL_STEEL_CHAR_8;
5957 case 0x163b: // (blue steel)
5958 element = EL_STEEL_CHAR_9;
5961 case 0x163c: // (blue steel)
5962 element = EL_STEEL_CHAR_PERIOD;
5965 case 0x163d: // (blue steel)
5966 element = EL_STEEL_CHAR_EXCLAM;
5969 case 0x163e: // (blue steel)
5970 element = EL_STEEL_CHAR_COLON;
5973 case 0x163f: // (blue steel)
5974 element = EL_STEEL_CHAR_LESS;
5977 case 0x1640: // (blue steel)
5978 element = EL_STEEL_CHAR_GREATER;
5981 case 0x1641: // (blue steel)
5982 element = EL_STEEL_CHAR_QUESTION;
5985 case 0x1642: // (blue steel)
5986 element = EL_STEEL_CHAR_COPYRIGHT;
5989 case 0x1643: // (blue steel)
5990 element = EL_STEEL_CHAR_UP;
5993 case 0x1644: // (blue steel)
5994 element = EL_STEEL_CHAR_DOWN;
5997 case 0x1645: // (blue steel)
5998 element = EL_STEEL_CHAR_BUTTON;
6001 case 0x1646: // (blue steel)
6002 element = EL_STEEL_CHAR_PLUS;
6005 case 0x1647: // (blue steel)
6006 element = EL_STEEL_CHAR_MINUS;
6009 case 0x1648: // (blue steel)
6010 element = EL_STEEL_CHAR_APOSTROPHE;
6013 case 0x1649: // (blue steel)
6014 element = EL_STEEL_CHAR_PARENLEFT;
6017 case 0x164a: // (blue steel)
6018 element = EL_STEEL_CHAR_PARENRIGHT;
6021 case 0x164b: // (green steel)
6022 element = EL_STEEL_CHAR_A;
6025 case 0x164c: // (green steel)
6026 element = EL_STEEL_CHAR_B;
6029 case 0x164d: // (green steel)
6030 element = EL_STEEL_CHAR_C;
6033 case 0x164e: // (green steel)
6034 element = EL_STEEL_CHAR_D;
6037 case 0x164f: // (green steel)
6038 element = EL_STEEL_CHAR_E;
6041 case 0x1650: // (green steel)
6042 element = EL_STEEL_CHAR_F;
6045 case 0x1651: // (green steel)
6046 element = EL_STEEL_CHAR_G;
6049 case 0x1652: // (green steel)
6050 element = EL_STEEL_CHAR_H;
6053 case 0x1653: // (green steel)
6054 element = EL_STEEL_CHAR_I;
6057 case 0x1654: // (green steel)
6058 element = EL_STEEL_CHAR_J;
6061 case 0x1655: // (green steel)
6062 element = EL_STEEL_CHAR_K;
6065 case 0x1656: // (green steel)
6066 element = EL_STEEL_CHAR_L;
6069 case 0x1657: // (green steel)
6070 element = EL_STEEL_CHAR_M;
6073 case 0x1658: // (green steel)
6074 element = EL_STEEL_CHAR_N;
6077 case 0x1659: // (green steel)
6078 element = EL_STEEL_CHAR_O;
6081 case 0x165a: // (green steel)
6082 element = EL_STEEL_CHAR_P;
6085 case 0x165b: // (green steel)
6086 element = EL_STEEL_CHAR_Q;
6089 case 0x165c: // (green steel)
6090 element = EL_STEEL_CHAR_R;
6093 case 0x165d: // (green steel)
6094 element = EL_STEEL_CHAR_S;
6097 case 0x165e: // (green steel)
6098 element = EL_STEEL_CHAR_T;
6101 case 0x165f: // (green steel)
6102 element = EL_STEEL_CHAR_U;
6105 case 0x1660: // (green steel)
6106 element = EL_STEEL_CHAR_V;
6109 case 0x1661: // (green steel)
6110 element = EL_STEEL_CHAR_W;
6113 case 0x1662: // (green steel)
6114 element = EL_STEEL_CHAR_X;
6117 case 0x1663: // (green steel)
6118 element = EL_STEEL_CHAR_Y;
6121 case 0x1664: // (green steel)
6122 element = EL_STEEL_CHAR_Z;
6125 case 0x1665: // (green steel)
6126 element = EL_STEEL_CHAR_AUMLAUT;
6129 case 0x1666: // (green steel)
6130 element = EL_STEEL_CHAR_OUMLAUT;
6133 case 0x1667: // (green steel)
6134 element = EL_STEEL_CHAR_UUMLAUT;
6137 case 0x1668: // (green steel)
6138 element = EL_STEEL_CHAR_0;
6141 case 0x1669: // (green steel)
6142 element = EL_STEEL_CHAR_1;
6145 case 0x166a: // (green steel)
6146 element = EL_STEEL_CHAR_2;
6149 case 0x166b: // (green steel)
6150 element = EL_STEEL_CHAR_3;
6153 case 0x166c: // (green steel)
6154 element = EL_STEEL_CHAR_4;
6157 case 0x166d: // (green steel)
6158 element = EL_STEEL_CHAR_5;
6161 case 0x166e: // (green steel)
6162 element = EL_STEEL_CHAR_6;
6165 case 0x166f: // (green steel)
6166 element = EL_STEEL_CHAR_7;
6169 case 0x1670: // (green steel)
6170 element = EL_STEEL_CHAR_8;
6173 case 0x1671: // (green steel)
6174 element = EL_STEEL_CHAR_9;
6177 case 0x1672: // (green steel)
6178 element = EL_STEEL_CHAR_PERIOD;
6181 case 0x1673: // (green steel)
6182 element = EL_STEEL_CHAR_EXCLAM;
6185 case 0x1674: // (green steel)
6186 element = EL_STEEL_CHAR_COLON;
6189 case 0x1675: // (green steel)
6190 element = EL_STEEL_CHAR_LESS;
6193 case 0x1676: // (green steel)
6194 element = EL_STEEL_CHAR_GREATER;
6197 case 0x1677: // (green steel)
6198 element = EL_STEEL_CHAR_QUESTION;
6201 case 0x1678: // (green steel)
6202 element = EL_STEEL_CHAR_COPYRIGHT;
6205 case 0x1679: // (green steel)
6206 element = EL_STEEL_CHAR_UP;
6209 case 0x167a: // (green steel)
6210 element = EL_STEEL_CHAR_DOWN;
6213 case 0x167b: // (green steel)
6214 element = EL_STEEL_CHAR_BUTTON;
6217 case 0x167c: // (green steel)
6218 element = EL_STEEL_CHAR_PLUS;
6221 case 0x167d: // (green steel)
6222 element = EL_STEEL_CHAR_MINUS;
6225 case 0x167e: // (green steel)
6226 element = EL_STEEL_CHAR_APOSTROPHE;
6229 case 0x167f: // (green steel)
6230 element = EL_STEEL_CHAR_PARENLEFT;
6233 case 0x1680: // (green steel)
6234 element = EL_STEEL_CHAR_PARENRIGHT;
6237 case 0x1681: // gate (red)
6238 element = EL_EM_GATE_1;
6241 case 0x1682: // secret gate (red)
6242 element = EL_EM_GATE_1_GRAY;
6245 case 0x1683: // gate (yellow)
6246 element = EL_EM_GATE_2;
6249 case 0x1684: // secret gate (yellow)
6250 element = EL_EM_GATE_2_GRAY;
6253 case 0x1685: // gate (blue)
6254 element = EL_EM_GATE_4;
6257 case 0x1686: // secret gate (blue)
6258 element = EL_EM_GATE_4_GRAY;
6261 case 0x1687: // gate (green)
6262 element = EL_EM_GATE_3;
6265 case 0x1688: // secret gate (green)
6266 element = EL_EM_GATE_3_GRAY;
6269 case 0x1689: // gate (white)
6270 element = EL_DC_GATE_WHITE;
6273 case 0x168a: // secret gate (white)
6274 element = EL_DC_GATE_WHITE_GRAY;
6277 case 0x168b: // secret gate (no key)
6278 element = EL_DC_GATE_FAKE_GRAY;
6282 element = EL_ROBOT_WHEEL;
6286 element = EL_DC_TIMEGATE_SWITCH;
6290 element = EL_ACID_POOL_BOTTOM;
6294 element = EL_ACID_POOL_TOPLEFT;
6298 element = EL_ACID_POOL_TOPRIGHT;
6302 element = EL_ACID_POOL_BOTTOMLEFT;
6306 element = EL_ACID_POOL_BOTTOMRIGHT;
6310 element = EL_STEELWALL;
6314 element = EL_STEELWALL_SLIPPERY;
6317 case 0x1695: // steel wall (not round)
6318 element = EL_STEELWALL;
6321 case 0x1696: // steel wall (left)
6322 element = EL_DC_STEELWALL_1_LEFT;
6325 case 0x1697: // steel wall (bottom)
6326 element = EL_DC_STEELWALL_1_BOTTOM;
6329 case 0x1698: // steel wall (right)
6330 element = EL_DC_STEELWALL_1_RIGHT;
6333 case 0x1699: // steel wall (top)
6334 element = EL_DC_STEELWALL_1_TOP;
6337 case 0x169a: // steel wall (left/bottom)
6338 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6341 case 0x169b: // steel wall (right/bottom)
6342 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6345 case 0x169c: // steel wall (right/top)
6346 element = EL_DC_STEELWALL_1_TOPRIGHT;
6349 case 0x169d: // steel wall (left/top)
6350 element = EL_DC_STEELWALL_1_TOPLEFT;
6353 case 0x169e: // steel wall (right/bottom small)
6354 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6357 case 0x169f: // steel wall (left/bottom small)
6358 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6361 case 0x16a0: // steel wall (right/top small)
6362 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6365 case 0x16a1: // steel wall (left/top small)
6366 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6369 case 0x16a2: // steel wall (left/right)
6370 element = EL_DC_STEELWALL_1_VERTICAL;
6373 case 0x16a3: // steel wall (top/bottom)
6374 element = EL_DC_STEELWALL_1_HORIZONTAL;
6377 case 0x16a4: // steel wall 2 (left end)
6378 element = EL_DC_STEELWALL_2_LEFT;
6381 case 0x16a5: // steel wall 2 (right end)
6382 element = EL_DC_STEELWALL_2_RIGHT;
6385 case 0x16a6: // steel wall 2 (top end)
6386 element = EL_DC_STEELWALL_2_TOP;
6389 case 0x16a7: // steel wall 2 (bottom end)
6390 element = EL_DC_STEELWALL_2_BOTTOM;
6393 case 0x16a8: // steel wall 2 (left/right)
6394 element = EL_DC_STEELWALL_2_HORIZONTAL;
6397 case 0x16a9: // steel wall 2 (up/down)
6398 element = EL_DC_STEELWALL_2_VERTICAL;
6401 case 0x16aa: // steel wall 2 (mid)
6402 element = EL_DC_STEELWALL_2_MIDDLE;
6406 element = EL_SIGN_EXCLAMATION;
6410 element = EL_SIGN_RADIOACTIVITY;
6414 element = EL_SIGN_STOP;
6418 element = EL_SIGN_WHEELCHAIR;
6422 element = EL_SIGN_PARKING;
6426 element = EL_SIGN_NO_ENTRY;
6430 element = EL_SIGN_HEART;
6434 element = EL_SIGN_GIVE_WAY;
6438 element = EL_SIGN_ENTRY_FORBIDDEN;
6442 element = EL_SIGN_EMERGENCY_EXIT;
6446 element = EL_SIGN_YIN_YANG;
6450 element = EL_WALL_EMERALD;
6454 element = EL_WALL_DIAMOND;
6458 element = EL_WALL_PEARL;
6462 element = EL_WALL_CRYSTAL;
6466 element = EL_INVISIBLE_WALL;
6470 element = EL_INVISIBLE_STEELWALL;
6474 // EL_INVISIBLE_SAND
6477 element = EL_LIGHT_SWITCH;
6481 element = EL_ENVELOPE_1;
6485 if (element >= 0x0117 && element <= 0x036e) // (?)
6486 element = EL_DIAMOND;
6487 else if (element >= 0x042d && element <= 0x0684) // (?)
6488 element = EL_EMERALD;
6489 else if (element >= 0x157c && element <= 0x158b)
6491 else if (element >= 0x1590 && element <= 0x159f)
6492 element = EL_DC_LANDMINE;
6493 else if (element >= 0x16bc && element <= 0x16cb)
6494 element = EL_INVISIBLE_SAND;
6497 Warn("unknown Diamond Caves element 0x%04x", element);
6499 element = EL_UNKNOWN;
6504 return getMappedElement(element);
6507 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6509 byte header[DC_LEVEL_HEADER_SIZE];
6511 int envelope_header_pos = 62;
6512 int envelope_content_pos = 94;
6513 int level_name_pos = 251;
6514 int level_author_pos = 292;
6515 int envelope_header_len;
6516 int envelope_content_len;
6518 int level_author_len;
6520 int num_yamyam_contents;
6523 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6525 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6527 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6529 header[i * 2 + 0] = header_word >> 8;
6530 header[i * 2 + 1] = header_word & 0xff;
6533 // read some values from level header to check level decoding integrity
6534 fieldx = header[6] | (header[7] << 8);
6535 fieldy = header[8] | (header[9] << 8);
6536 num_yamyam_contents = header[60] | (header[61] << 8);
6538 // do some simple sanity checks to ensure that level was correctly decoded
6539 if (fieldx < 1 || fieldx > 256 ||
6540 fieldy < 1 || fieldy > 256 ||
6541 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6543 level->no_valid_file = TRUE;
6545 Warn("cannot decode level from stream -- using empty level");
6550 // maximum envelope header size is 31 bytes
6551 envelope_header_len = header[envelope_header_pos];
6552 // maximum envelope content size is 110 (156?) bytes
6553 envelope_content_len = header[envelope_content_pos];
6555 // maximum level title size is 40 bytes
6556 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6557 // maximum level author size is 30 (51?) bytes
6558 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6562 for (i = 0; i < envelope_header_len; i++)
6563 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6564 level->envelope[0].text[envelope_size++] =
6565 header[envelope_header_pos + 1 + i];
6567 if (envelope_header_len > 0 && envelope_content_len > 0)
6569 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6570 level->envelope[0].text[envelope_size++] = '\n';
6571 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6572 level->envelope[0].text[envelope_size++] = '\n';
6575 for (i = 0; i < envelope_content_len; i++)
6576 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6577 level->envelope[0].text[envelope_size++] =
6578 header[envelope_content_pos + 1 + i];
6580 level->envelope[0].text[envelope_size] = '\0';
6582 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6583 level->envelope[0].ysize = 10;
6584 level->envelope[0].autowrap = TRUE;
6585 level->envelope[0].centered = TRUE;
6587 for (i = 0; i < level_name_len; i++)
6588 level->name[i] = header[level_name_pos + 1 + i];
6589 level->name[level_name_len] = '\0';
6591 for (i = 0; i < level_author_len; i++)
6592 level->author[i] = header[level_author_pos + 1 + i];
6593 level->author[level_author_len] = '\0';
6595 num_yamyam_contents = header[60] | (header[61] << 8);
6596 level->num_yamyam_contents =
6597 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6599 for (i = 0; i < num_yamyam_contents; i++)
6601 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6603 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6604 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6606 if (i < MAX_ELEMENT_CONTENTS)
6607 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6611 fieldx = header[6] | (header[7] << 8);
6612 fieldy = header[8] | (header[9] << 8);
6613 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6614 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6616 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6618 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6619 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6621 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6622 level->field[x][y] = getMappedElement_DC(element_dc);
6625 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6626 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6627 level->field[x][y] = EL_PLAYER_1;
6629 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6630 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6631 level->field[x][y] = EL_PLAYER_2;
6633 level->gems_needed = header[18] | (header[19] << 8);
6635 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6636 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6637 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6638 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6639 level->score[SC_NUT] = header[28] | (header[29] << 8);
6640 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6641 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6642 level->score[SC_BUG] = header[34] | (header[35] << 8);
6643 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6644 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6645 level->score[SC_KEY] = header[40] | (header[41] << 8);
6646 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6648 level->time = header[44] | (header[45] << 8);
6650 level->amoeba_speed = header[46] | (header[47] << 8);
6651 level->time_light = header[48] | (header[49] << 8);
6652 level->time_timegate = header[50] | (header[51] << 8);
6653 level->time_wheel = header[52] | (header[53] << 8);
6654 level->time_magic_wall = header[54] | (header[55] << 8);
6655 level->extra_time = header[56] | (header[57] << 8);
6656 level->shield_normal_time = header[58] | (header[59] << 8);
6658 // shield and extra time elements do not have a score
6659 level->score[SC_SHIELD] = 0;
6660 level->extra_time_score = 0;
6662 // set time for normal and deadly shields to the same value
6663 level->shield_deadly_time = level->shield_normal_time;
6665 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6666 // can slip down from flat walls, like normal walls and steel walls
6667 level->em_slippery_gems = TRUE;
6669 // time score is counted for each 10 seconds left in Diamond Caves levels
6670 level->time_score_base = 10;
6673 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6674 struct LevelFileInfo *level_file_info,
6675 boolean level_info_only)
6677 char *filename = level_file_info->filename;
6679 int num_magic_bytes = 8;
6680 char magic_bytes[num_magic_bytes + 1];
6681 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6683 if (!(file = openFile(filename, MODE_READ)))
6685 level->no_valid_file = TRUE;
6687 if (!level_info_only)
6688 Warn("cannot read level '%s' -- using empty level", filename);
6693 // fseek(file, 0x0000, SEEK_SET);
6695 if (level_file_info->packed)
6697 // read "magic bytes" from start of file
6698 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6699 magic_bytes[0] = '\0';
6701 // check "magic bytes" for correct file format
6702 if (!strPrefix(magic_bytes, "DC2"))
6704 level->no_valid_file = TRUE;
6706 Warn("unknown DC level file '%s' -- using empty level", filename);
6711 if (strPrefix(magic_bytes, "DC2Win95") ||
6712 strPrefix(magic_bytes, "DC2Win98"))
6714 int position_first_level = 0x00fa;
6715 int extra_bytes = 4;
6718 // advance file stream to first level inside the level package
6719 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6721 // each block of level data is followed by block of non-level data
6722 num_levels_to_skip *= 2;
6724 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6725 while (num_levels_to_skip >= 0)
6727 // advance file stream to next level inside the level package
6728 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6730 level->no_valid_file = TRUE;
6732 Warn("cannot fseek in file '%s' -- using empty level", filename);
6737 // skip apparently unused extra bytes following each level
6738 ReadUnusedBytesFromFile(file, extra_bytes);
6740 // read size of next level in level package
6741 skip_bytes = getFile32BitLE(file);
6743 num_levels_to_skip--;
6748 level->no_valid_file = TRUE;
6750 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6756 LoadLevelFromFileStream_DC(file, level);
6762 // ----------------------------------------------------------------------------
6763 // functions for loading SB level
6764 // ----------------------------------------------------------------------------
6766 int getMappedElement_SB(int element_ascii, boolean use_ces)
6774 sb_element_mapping[] =
6776 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6777 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6778 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6779 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6780 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6781 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6782 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6783 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6790 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6791 if (element_ascii == sb_element_mapping[i].ascii)
6792 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6794 return EL_UNDEFINED;
6797 static void SetLevelSettings_SB(struct LevelInfo *level)
6801 level->use_step_counter = TRUE;
6804 level->score[SC_TIME_BONUS] = 0;
6805 level->time_score_base = 1;
6806 level->rate_time_over_score = TRUE;
6809 level->auto_exit_sokoban = TRUE;
6812 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6813 struct LevelFileInfo *level_file_info,
6814 boolean level_info_only)
6816 char *filename = level_file_info->filename;
6817 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6818 char last_comment[MAX_LINE_LEN];
6819 char level_name[MAX_LINE_LEN];
6822 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6823 boolean read_continued_line = FALSE;
6824 boolean reading_playfield = FALSE;
6825 boolean got_valid_playfield_line = FALSE;
6826 boolean invalid_playfield_char = FALSE;
6827 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6828 int file_level_nr = 0;
6829 int x = 0, y = 0; // initialized to make compilers happy
6831 last_comment[0] = '\0';
6832 level_name[0] = '\0';
6834 if (!(file = openFile(filename, MODE_READ)))
6836 level->no_valid_file = TRUE;
6838 if (!level_info_only)
6839 Warn("cannot read level '%s' -- using empty level", filename);
6844 while (!checkEndOfFile(file))
6846 // level successfully read, but next level may follow here
6847 if (!got_valid_playfield_line && reading_playfield)
6849 // read playfield from single level file -- skip remaining file
6850 if (!level_file_info->packed)
6853 if (file_level_nr >= num_levels_to_skip)
6858 last_comment[0] = '\0';
6859 level_name[0] = '\0';
6861 reading_playfield = FALSE;
6864 got_valid_playfield_line = FALSE;
6866 // read next line of input file
6867 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6870 // cut trailing line break (this can be newline and/or carriage return)
6871 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6872 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6875 // copy raw input line for later use (mainly debugging output)
6876 strcpy(line_raw, line);
6878 if (read_continued_line)
6880 // append new line to existing line, if there is enough space
6881 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6882 strcat(previous_line, line_ptr);
6884 strcpy(line, previous_line); // copy storage buffer to line
6886 read_continued_line = FALSE;
6889 // if the last character is '\', continue at next line
6890 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6892 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6893 strcpy(previous_line, line); // copy line to storage buffer
6895 read_continued_line = TRUE;
6901 if (line[0] == '\0')
6904 // extract comment text from comment line
6907 for (line_ptr = line; *line_ptr; line_ptr++)
6908 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6911 strcpy(last_comment, line_ptr);
6916 // extract level title text from line containing level title
6917 if (line[0] == '\'')
6919 strcpy(level_name, &line[1]);
6921 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6922 level_name[strlen(level_name) - 1] = '\0';
6927 // skip lines containing only spaces (or empty lines)
6928 for (line_ptr = line; *line_ptr; line_ptr++)
6929 if (*line_ptr != ' ')
6931 if (*line_ptr == '\0')
6934 // at this point, we have found a line containing part of a playfield
6936 got_valid_playfield_line = TRUE;
6938 if (!reading_playfield)
6940 reading_playfield = TRUE;
6941 invalid_playfield_char = FALSE;
6943 for (x = 0; x < MAX_LEV_FIELDX; x++)
6944 for (y = 0; y < MAX_LEV_FIELDY; y++)
6945 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6950 // start with topmost tile row
6954 // skip playfield line if larger row than allowed
6955 if (y >= MAX_LEV_FIELDY)
6958 // start with leftmost tile column
6961 // read playfield elements from line
6962 for (line_ptr = line; *line_ptr; line_ptr++)
6964 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6966 // stop parsing playfield line if larger column than allowed
6967 if (x >= MAX_LEV_FIELDX)
6970 if (mapped_sb_element == EL_UNDEFINED)
6972 invalid_playfield_char = TRUE;
6977 level->field[x][y] = mapped_sb_element;
6979 // continue with next tile column
6982 level->fieldx = MAX(x, level->fieldx);
6985 if (invalid_playfield_char)
6987 // if first playfield line, treat invalid lines as comment lines
6989 reading_playfield = FALSE;
6994 // continue with next tile row
7002 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7003 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7005 if (!reading_playfield)
7007 level->no_valid_file = TRUE;
7009 Warn("cannot read level '%s' -- using empty level", filename);
7014 if (*level_name != '\0')
7016 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7017 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7019 else if (*last_comment != '\0')
7021 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7022 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7026 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7029 // set all empty fields beyond the border walls to invisible steel wall
7030 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7032 if ((x == 0 || x == level->fieldx - 1 ||
7033 y == 0 || y == level->fieldy - 1) &&
7034 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7035 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7036 level->field, level->fieldx, level->fieldy);
7039 // set special level settings for Sokoban levels
7040 SetLevelSettings_SB(level);
7042 if (load_xsb_to_ces)
7044 // special global settings can now be set in level template
7045 level->use_custom_template = TRUE;
7050 // -------------------------------------------------------------------------
7051 // functions for handling native levels
7052 // -------------------------------------------------------------------------
7054 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7055 struct LevelFileInfo *level_file_info,
7056 boolean level_info_only)
7060 // determine position of requested level inside level package
7061 if (level_file_info->packed)
7062 pos = level_file_info->nr - leveldir_current->first_level;
7064 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7065 level->no_valid_file = TRUE;
7068 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7069 struct LevelFileInfo *level_file_info,
7070 boolean level_info_only)
7072 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7073 level->no_valid_file = TRUE;
7076 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7077 struct LevelFileInfo *level_file_info,
7078 boolean level_info_only)
7082 // determine position of requested level inside level package
7083 if (level_file_info->packed)
7084 pos = level_file_info->nr - leveldir_current->first_level;
7086 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7087 level->no_valid_file = TRUE;
7090 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7091 struct LevelFileInfo *level_file_info,
7092 boolean level_info_only)
7094 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7095 level->no_valid_file = TRUE;
7098 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7100 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7101 CopyNativeLevel_RND_to_BD(level);
7102 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7103 CopyNativeLevel_RND_to_EM(level);
7104 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7105 CopyNativeLevel_RND_to_SP(level);
7106 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7107 CopyNativeLevel_RND_to_MM(level);
7110 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7112 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7113 CopyNativeLevel_BD_to_RND(level);
7114 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7115 CopyNativeLevel_EM_to_RND(level);
7116 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7117 CopyNativeLevel_SP_to_RND(level);
7118 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7119 CopyNativeLevel_MM_to_RND(level);
7122 void SaveNativeLevel(struct LevelInfo *level)
7124 // saving native level files only supported for some game engines
7125 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7126 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7129 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7130 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7131 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7132 char *filename = getLevelFilenameFromBasename(basename);
7134 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7137 boolean success = FALSE;
7139 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7141 CopyNativeLevel_RND_to_BD(level);
7142 // CopyNativeTape_RND_to_BD(level);
7144 success = SaveNativeLevel_BD(filename);
7146 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7148 CopyNativeLevel_RND_to_SP(level);
7149 CopyNativeTape_RND_to_SP(level);
7151 success = SaveNativeLevel_SP(filename);
7155 Request("Native level file saved!", REQ_CONFIRM);
7157 Request("Failed to save native level file!", REQ_CONFIRM);
7161 // ----------------------------------------------------------------------------
7162 // functions for loading generic level
7163 // ----------------------------------------------------------------------------
7165 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7166 struct LevelFileInfo *level_file_info,
7167 boolean level_info_only)
7169 // always start with reliable default values
7170 setLevelInfoToDefaults(level, level_info_only, TRUE);
7172 switch (level_file_info->type)
7174 case LEVEL_FILE_TYPE_RND:
7175 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7178 case LEVEL_FILE_TYPE_BD:
7179 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7180 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7183 case LEVEL_FILE_TYPE_EM:
7184 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7185 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7188 case LEVEL_FILE_TYPE_SP:
7189 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7190 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7193 case LEVEL_FILE_TYPE_MM:
7194 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7195 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7198 case LEVEL_FILE_TYPE_DC:
7199 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7202 case LEVEL_FILE_TYPE_SB:
7203 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7207 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7211 // if level file is invalid, restore level structure to default values
7212 if (level->no_valid_file)
7213 setLevelInfoToDefaults(level, level_info_only, FALSE);
7215 if (check_special_flags("use_native_bd_game_engine"))
7216 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7218 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7219 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7221 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7222 CopyNativeLevel_Native_to_RND(level);
7225 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7227 static struct LevelFileInfo level_file_info;
7229 // always start with reliable default values
7230 setFileInfoToDefaults(&level_file_info);
7232 level_file_info.nr = 0; // unknown level number
7233 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7235 setString(&level_file_info.filename, filename);
7237 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7240 static void LoadLevel_InitVersion(struct LevelInfo *level)
7244 if (leveldir_current == NULL) // only when dumping level
7247 // all engine modifications also valid for levels which use latest engine
7248 if (level->game_version < VERSION_IDENT(3,2,0,5))
7250 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7251 level->time_score_base = 10;
7254 if (leveldir_current->latest_engine)
7256 // ---------- use latest game engine --------------------------------------
7258 /* For all levels which are forced to use the latest game engine version
7259 (normally all but user contributed, private and undefined levels), set
7260 the game engine version to the actual version; this allows for actual
7261 corrections in the game engine to take effect for existing, converted
7262 levels (from "classic" or other existing games) to make the emulation
7263 of the corresponding game more accurate, while (hopefully) not breaking
7264 existing levels created from other players. */
7266 level->game_version = GAME_VERSION_ACTUAL;
7268 /* Set special EM style gems behaviour: EM style gems slip down from
7269 normal, steel and growing wall. As this is a more fundamental change,
7270 it seems better to set the default behaviour to "off" (as it is more
7271 natural) and make it configurable in the level editor (as a property
7272 of gem style elements). Already existing converted levels (neither
7273 private nor contributed levels) are changed to the new behaviour. */
7275 if (level->file_version < FILE_VERSION_2_0)
7276 level->em_slippery_gems = TRUE;
7281 // ---------- use game engine the level was created with --------------------
7283 /* For all levels which are not forced to use the latest game engine
7284 version (normally user contributed, private and undefined levels),
7285 use the version of the game engine the levels were created for.
7287 Since 2.0.1, the game engine version is now directly stored
7288 in the level file (chunk "VERS"), so there is no need anymore
7289 to set the game version from the file version (except for old,
7290 pre-2.0 levels, where the game version is still taken from the
7291 file format version used to store the level -- see above). */
7293 // player was faster than enemies in 1.0.0 and before
7294 if (level->file_version == FILE_VERSION_1_0)
7295 for (i = 0; i < MAX_PLAYERS; i++)
7296 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7298 // default behaviour for EM style gems was "slippery" only in 2.0.1
7299 if (level->game_version == VERSION_IDENT(2,0,1,0))
7300 level->em_slippery_gems = TRUE;
7302 // springs could be pushed over pits before (pre-release version) 2.2.0
7303 if (level->game_version < VERSION_IDENT(2,2,0,0))
7304 level->use_spring_bug = TRUE;
7306 if (level->game_version < VERSION_IDENT(3,2,0,5))
7308 // time orb caused limited time in endless time levels before 3.2.0-5
7309 level->use_time_orb_bug = TRUE;
7311 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7312 level->block_snap_field = FALSE;
7314 // extra time score was same value as time left score before 3.2.0-5
7315 level->extra_time_score = level->score[SC_TIME_BONUS];
7318 if (level->game_version < VERSION_IDENT(3,2,0,7))
7320 // default behaviour for snapping was "not continuous" before 3.2.0-7
7321 level->continuous_snapping = FALSE;
7324 // only few elements were able to actively move into acid before 3.1.0
7325 // trigger settings did not exist before 3.1.0; set to default "any"
7326 if (level->game_version < VERSION_IDENT(3,1,0,0))
7328 // correct "can move into acid" settings (all zero in old levels)
7330 level->can_move_into_acid_bits = 0; // nothing can move into acid
7331 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7333 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7334 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7335 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7336 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7338 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7339 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7341 // correct trigger settings (stored as zero == "none" in old levels)
7343 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7345 int element = EL_CUSTOM_START + i;
7346 struct ElementInfo *ei = &element_info[element];
7348 for (j = 0; j < ei->num_change_pages; j++)
7350 struct ElementChangeInfo *change = &ei->change_page[j];
7352 change->trigger_player = CH_PLAYER_ANY;
7353 change->trigger_page = CH_PAGE_ANY;
7358 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7360 int element = EL_CUSTOM_256;
7361 struct ElementInfo *ei = &element_info[element];
7362 struct ElementChangeInfo *change = &ei->change_page[0];
7364 /* This is needed to fix a problem that was caused by a bugfix in function
7365 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7366 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7367 not replace walkable elements, but instead just placed the player on it,
7368 without placing the Sokoban field under the player). Unfortunately, this
7369 breaks "Snake Bite" style levels when the snake is halfway through a door
7370 that just closes (the snake head is still alive and can be moved in this
7371 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7372 player (without Sokoban element) which then gets killed as designed). */
7374 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7375 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7376 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7377 change->target_element = EL_PLAYER_1;
7380 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7381 if (level->game_version < VERSION_IDENT(3,2,5,0))
7383 /* This is needed to fix a problem that was caused by a bugfix in function
7384 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7385 corrects the behaviour when a custom element changes to another custom
7386 element with a higher element number that has change actions defined.
7387 Normally, only one change per frame is allowed for custom elements.
7388 Therefore, it is checked if a custom element already changed in the
7389 current frame; if it did, subsequent changes are suppressed.
7390 Unfortunately, this is only checked for element changes, but not for
7391 change actions, which are still executed. As the function above loops
7392 through all custom elements from lower to higher, an element change
7393 resulting in a lower CE number won't be checked again, while a target
7394 element with a higher number will also be checked, and potential change
7395 actions will get executed for this CE, too (which is wrong), while
7396 further changes are ignored (which is correct). As this bugfix breaks
7397 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7398 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7399 behaviour for existing levels and tapes that make use of this bug */
7401 level->use_action_after_change_bug = TRUE;
7404 // not centering level after relocating player was default only in 3.2.3
7405 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7406 level->shifted_relocation = TRUE;
7408 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7409 if (level->game_version < VERSION_IDENT(3,2,6,0))
7410 level->em_explodes_by_fire = TRUE;
7412 // levels were solved by the first player entering an exit up to 4.1.0.0
7413 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7414 level->solved_by_one_player = TRUE;
7416 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7417 if (level->game_version < VERSION_IDENT(4,1,1,1))
7418 level->use_life_bugs = TRUE;
7420 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7421 if (level->game_version < VERSION_IDENT(4,1,1,1))
7422 level->sb_objects_needed = FALSE;
7424 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7425 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7426 level->finish_dig_collect = FALSE;
7428 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7429 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7430 level->keep_walkable_ce = TRUE;
7433 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7435 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7438 // check if this level is (not) a Sokoban level
7439 for (y = 0; y < level->fieldy; y++)
7440 for (x = 0; x < level->fieldx; x++)
7441 if (!IS_SB_ELEMENT(Tile[x][y]))
7442 is_sokoban_level = FALSE;
7444 if (is_sokoban_level)
7446 // set special level settings for Sokoban levels
7447 SetLevelSettings_SB(level);
7451 static void LoadLevel_InitSettings(struct LevelInfo *level)
7453 // adjust level settings for (non-native) Sokoban-style levels
7454 LoadLevel_InitSettings_SB(level);
7456 // rename levels with title "nameless level" or if renaming is forced
7457 if (leveldir_current->empty_level_name != NULL &&
7458 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7459 leveldir_current->force_level_name))
7460 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7461 leveldir_current->empty_level_name, level_nr);
7464 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7468 // map elements that have changed in newer versions
7469 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7470 level->game_version);
7471 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7472 for (x = 0; x < 3; x++)
7473 for (y = 0; y < 3; y++)
7474 level->yamyam_content[i].e[x][y] =
7475 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7476 level->game_version);
7480 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7484 // map custom element change events that have changed in newer versions
7485 // (these following values were accidentally changed in version 3.0.1)
7486 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7487 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7489 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7491 int element = EL_CUSTOM_START + i;
7493 // order of checking and copying events to be mapped is important
7494 // (do not change the start and end value -- they are constant)
7495 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7497 if (HAS_CHANGE_EVENT(element, j - 2))
7499 SET_CHANGE_EVENT(element, j - 2, FALSE);
7500 SET_CHANGE_EVENT(element, j, TRUE);
7504 // order of checking and copying events to be mapped is important
7505 // (do not change the start and end value -- they are constant)
7506 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7508 if (HAS_CHANGE_EVENT(element, j - 1))
7510 SET_CHANGE_EVENT(element, j - 1, FALSE);
7511 SET_CHANGE_EVENT(element, j, TRUE);
7517 // initialize "can_change" field for old levels with only one change page
7518 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7520 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7522 int element = EL_CUSTOM_START + i;
7524 if (CAN_CHANGE(element))
7525 element_info[element].change->can_change = TRUE;
7529 // correct custom element values (for old levels without these options)
7530 if (level->game_version < VERSION_IDENT(3,1,1,0))
7532 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7534 int element = EL_CUSTOM_START + i;
7535 struct ElementInfo *ei = &element_info[element];
7537 if (ei->access_direction == MV_NO_DIRECTION)
7538 ei->access_direction = MV_ALL_DIRECTIONS;
7542 // correct custom element values (fix invalid values for all versions)
7545 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7547 int element = EL_CUSTOM_START + i;
7548 struct ElementInfo *ei = &element_info[element];
7550 for (j = 0; j < ei->num_change_pages; j++)
7552 struct ElementChangeInfo *change = &ei->change_page[j];
7554 if (change->trigger_player == CH_PLAYER_NONE)
7555 change->trigger_player = CH_PLAYER_ANY;
7557 if (change->trigger_side == CH_SIDE_NONE)
7558 change->trigger_side = CH_SIDE_ANY;
7563 // initialize "can_explode" field for old levels which did not store this
7564 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7565 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7567 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7569 int element = EL_CUSTOM_START + i;
7571 if (EXPLODES_1X1_OLD(element))
7572 element_info[element].explosion_type = EXPLODES_1X1;
7574 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7575 EXPLODES_SMASHED(element) ||
7576 EXPLODES_IMPACT(element)));
7580 // correct previously hard-coded move delay values for maze runner style
7581 if (level->game_version < VERSION_IDENT(3,1,1,0))
7583 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7585 int element = EL_CUSTOM_START + i;
7587 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7589 // previously hard-coded and therefore ignored
7590 element_info[element].move_delay_fixed = 9;
7591 element_info[element].move_delay_random = 0;
7596 // set some other uninitialized values of custom elements in older levels
7597 if (level->game_version < VERSION_IDENT(3,1,0,0))
7599 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7601 int element = EL_CUSTOM_START + i;
7603 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7605 element_info[element].explosion_delay = 17;
7606 element_info[element].ignition_delay = 8;
7610 // set mouse click change events to work for left/middle/right mouse button
7611 if (level->game_version < VERSION_IDENT(4,2,3,0))
7613 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7615 int element = EL_CUSTOM_START + i;
7616 struct ElementInfo *ei = &element_info[element];
7618 for (j = 0; j < ei->num_change_pages; j++)
7620 struct ElementChangeInfo *change = &ei->change_page[j];
7622 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7623 change->has_event[CE_PRESSED_BY_MOUSE] ||
7624 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7625 change->has_event[CE_MOUSE_PRESSED_ON_X])
7626 change->trigger_side = CH_SIDE_ANY;
7632 static void LoadLevel_InitElements(struct LevelInfo *level)
7634 LoadLevel_InitStandardElements(level);
7636 if (level->file_has_custom_elements)
7637 LoadLevel_InitCustomElements(level);
7639 // initialize element properties for level editor etc.
7640 InitElementPropertiesEngine(level->game_version);
7641 InitElementPropertiesGfxElement();
7644 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7648 // map elements that have changed in newer versions
7649 for (y = 0; y < level->fieldy; y++)
7650 for (x = 0; x < level->fieldx; x++)
7651 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7652 level->game_version);
7654 // clear unused playfield data (nicer if level gets resized in editor)
7655 for (x = 0; x < MAX_LEV_FIELDX; x++)
7656 for (y = 0; y < MAX_LEV_FIELDY; y++)
7657 if (x >= level->fieldx || y >= level->fieldy)
7658 level->field[x][y] = EL_EMPTY;
7660 // copy elements to runtime playfield array
7661 for (x = 0; x < MAX_LEV_FIELDX; x++)
7662 for (y = 0; y < MAX_LEV_FIELDY; y++)
7663 Tile[x][y] = level->field[x][y];
7665 // initialize level size variables for faster access
7666 lev_fieldx = level->fieldx;
7667 lev_fieldy = level->fieldy;
7669 // determine border element for this level
7670 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7671 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7676 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7678 struct LevelFileInfo *level_file_info = &level->file_info;
7680 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7681 CopyNativeLevel_RND_to_Native(level);
7684 static void LoadLevelTemplate_LoadAndInit(void)
7686 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7688 LoadLevel_InitVersion(&level_template);
7689 LoadLevel_InitElements(&level_template);
7690 LoadLevel_InitSettings(&level_template);
7692 ActivateLevelTemplate();
7695 void LoadLevelTemplate(int nr)
7697 if (!fileExists(getGlobalLevelTemplateFilename()))
7699 Warn("no level template found for this level");
7704 setLevelFileInfo(&level_template.file_info, nr);
7706 LoadLevelTemplate_LoadAndInit();
7709 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7711 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7713 LoadLevelTemplate_LoadAndInit();
7716 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7718 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7720 if (level.use_custom_template)
7722 if (network_level != NULL)
7723 LoadNetworkLevelTemplate(network_level);
7725 LoadLevelTemplate(-1);
7728 LoadLevel_InitVersion(&level);
7729 LoadLevel_InitElements(&level);
7730 LoadLevel_InitPlayfield(&level);
7731 LoadLevel_InitSettings(&level);
7733 LoadLevel_InitNativeEngines(&level);
7736 void LoadLevel(int nr)
7738 SetLevelSetInfo(leveldir_current->identifier, nr);
7740 setLevelFileInfo(&level.file_info, nr);
7742 LoadLevel_LoadAndInit(NULL);
7745 void LoadLevelInfoOnly(int nr)
7747 setLevelFileInfo(&level.file_info, nr);
7749 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7752 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7754 SetLevelSetInfo(network_level->leveldir_identifier,
7755 network_level->file_info.nr);
7757 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7759 LoadLevel_LoadAndInit(network_level);
7762 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7766 chunk_size += putFileVersion(file, level->file_version);
7767 chunk_size += putFileVersion(file, level->game_version);
7772 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7776 chunk_size += putFile16BitBE(file, level->creation_date.year);
7777 chunk_size += putFile8Bit(file, level->creation_date.month);
7778 chunk_size += putFile8Bit(file, level->creation_date.day);
7783 #if ENABLE_HISTORIC_CHUNKS
7784 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7788 putFile8Bit(file, level->fieldx);
7789 putFile8Bit(file, level->fieldy);
7791 putFile16BitBE(file, level->time);
7792 putFile16BitBE(file, level->gems_needed);
7794 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7795 putFile8Bit(file, level->name[i]);
7797 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7798 putFile8Bit(file, level->score[i]);
7800 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7801 for (y = 0; y < 3; y++)
7802 for (x = 0; x < 3; x++)
7803 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7804 level->yamyam_content[i].e[x][y]));
7805 putFile8Bit(file, level->amoeba_speed);
7806 putFile8Bit(file, level->time_magic_wall);
7807 putFile8Bit(file, level->time_wheel);
7808 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7809 level->amoeba_content));
7810 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7811 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7812 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7813 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7815 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7817 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7818 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7819 putFile32BitBE(file, level->can_move_into_acid_bits);
7820 putFile8Bit(file, level->dont_collide_with_bits);
7822 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7823 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7825 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7826 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7827 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7829 putFile8Bit(file, level->game_engine_type);
7831 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7835 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7840 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7841 chunk_size += putFile8Bit(file, level->name[i]);
7846 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7851 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7852 chunk_size += putFile8Bit(file, level->author[i]);
7857 #if ENABLE_HISTORIC_CHUNKS
7858 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7863 for (y = 0; y < level->fieldy; y++)
7864 for (x = 0; x < level->fieldx; x++)
7865 if (level->encoding_16bit_field)
7866 chunk_size += putFile16BitBE(file, level->field[x][y]);
7868 chunk_size += putFile8Bit(file, level->field[x][y]);
7874 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7879 for (y = 0; y < level->fieldy; y++)
7880 for (x = 0; x < level->fieldx; x++)
7881 chunk_size += putFile16BitBE(file, level->field[x][y]);
7886 #if ENABLE_HISTORIC_CHUNKS
7887 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7891 putFile8Bit(file, EL_YAMYAM);
7892 putFile8Bit(file, level->num_yamyam_contents);
7893 putFile8Bit(file, 0);
7894 putFile8Bit(file, 0);
7896 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7897 for (y = 0; y < 3; y++)
7898 for (x = 0; x < 3; x++)
7899 if (level->encoding_16bit_field)
7900 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7902 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7906 #if ENABLE_HISTORIC_CHUNKS
7907 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7910 int num_contents, content_xsize, content_ysize;
7911 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7913 if (element == EL_YAMYAM)
7915 num_contents = level->num_yamyam_contents;
7919 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7920 for (y = 0; y < 3; y++)
7921 for (x = 0; x < 3; x++)
7922 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7924 else if (element == EL_BD_AMOEBA)
7930 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7931 for (y = 0; y < 3; y++)
7932 for (x = 0; x < 3; x++)
7933 content_array[i][x][y] = EL_EMPTY;
7934 content_array[0][0][0] = level->amoeba_content;
7938 // chunk header already written -- write empty chunk data
7939 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7941 Warn("cannot save content for element '%d'", element);
7946 putFile16BitBE(file, element);
7947 putFile8Bit(file, num_contents);
7948 putFile8Bit(file, content_xsize);
7949 putFile8Bit(file, content_ysize);
7951 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7953 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7954 for (y = 0; y < 3; y++)
7955 for (x = 0; x < 3; x++)
7956 putFile16BitBE(file, content_array[i][x][y]);
7960 #if ENABLE_HISTORIC_CHUNKS
7961 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7963 int envelope_nr = element - EL_ENVELOPE_1;
7964 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7968 chunk_size += putFile16BitBE(file, element);
7969 chunk_size += putFile16BitBE(file, envelope_len);
7970 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7971 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7973 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7974 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7976 for (i = 0; i < envelope_len; i++)
7977 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7983 #if ENABLE_HISTORIC_CHUNKS
7984 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7985 int num_changed_custom_elements)
7989 putFile16BitBE(file, num_changed_custom_elements);
7991 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7993 int element = EL_CUSTOM_START + i;
7995 struct ElementInfo *ei = &element_info[element];
7997 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7999 if (check < num_changed_custom_elements)
8001 putFile16BitBE(file, element);
8002 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8009 if (check != num_changed_custom_elements) // should not happen
8010 Warn("inconsistent number of custom element properties");
8014 #if ENABLE_HISTORIC_CHUNKS
8015 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8016 int num_changed_custom_elements)
8020 putFile16BitBE(file, num_changed_custom_elements);
8022 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8024 int element = EL_CUSTOM_START + i;
8026 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8028 if (check < num_changed_custom_elements)
8030 putFile16BitBE(file, element);
8031 putFile16BitBE(file, element_info[element].change->target_element);
8038 if (check != num_changed_custom_elements) // should not happen
8039 Warn("inconsistent number of custom target elements");
8043 #if ENABLE_HISTORIC_CHUNKS
8044 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8045 int num_changed_custom_elements)
8047 int i, j, x, y, check = 0;
8049 putFile16BitBE(file, num_changed_custom_elements);
8051 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8053 int element = EL_CUSTOM_START + i;
8054 struct ElementInfo *ei = &element_info[element];
8056 if (ei->modified_settings)
8058 if (check < num_changed_custom_elements)
8060 putFile16BitBE(file, element);
8062 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8063 putFile8Bit(file, ei->description[j]);
8065 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8067 // some free bytes for future properties and padding
8068 WriteUnusedBytesToFile(file, 7);
8070 putFile8Bit(file, ei->use_gfx_element);
8071 putFile16BitBE(file, ei->gfx_element_initial);
8073 putFile8Bit(file, ei->collect_score_initial);
8074 putFile8Bit(file, ei->collect_count_initial);
8076 putFile16BitBE(file, ei->push_delay_fixed);
8077 putFile16BitBE(file, ei->push_delay_random);
8078 putFile16BitBE(file, ei->move_delay_fixed);
8079 putFile16BitBE(file, ei->move_delay_random);
8081 putFile16BitBE(file, ei->move_pattern);
8082 putFile8Bit(file, ei->move_direction_initial);
8083 putFile8Bit(file, ei->move_stepsize);
8085 for (y = 0; y < 3; y++)
8086 for (x = 0; x < 3; x++)
8087 putFile16BitBE(file, ei->content.e[x][y]);
8089 putFile32BitBE(file, ei->change->events);
8091 putFile16BitBE(file, ei->change->target_element);
8093 putFile16BitBE(file, ei->change->delay_fixed);
8094 putFile16BitBE(file, ei->change->delay_random);
8095 putFile16BitBE(file, ei->change->delay_frames);
8097 putFile16BitBE(file, ei->change->initial_trigger_element);
8099 putFile8Bit(file, ei->change->explode);
8100 putFile8Bit(file, ei->change->use_target_content);
8101 putFile8Bit(file, ei->change->only_if_complete);
8102 putFile8Bit(file, ei->change->use_random_replace);
8104 putFile8Bit(file, ei->change->random_percentage);
8105 putFile8Bit(file, ei->change->replace_when);
8107 for (y = 0; y < 3; y++)
8108 for (x = 0; x < 3; x++)
8109 putFile16BitBE(file, ei->change->content.e[x][y]);
8111 putFile8Bit(file, ei->slippery_type);
8113 // some free bytes for future properties and padding
8114 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8121 if (check != num_changed_custom_elements) // should not happen
8122 Warn("inconsistent number of custom element properties");
8126 #if ENABLE_HISTORIC_CHUNKS
8127 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8129 struct ElementInfo *ei = &element_info[element];
8132 // ---------- custom element base property values (96 bytes) ----------------
8134 putFile16BitBE(file, element);
8136 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8137 putFile8Bit(file, ei->description[i]);
8139 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8141 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8143 putFile8Bit(file, ei->num_change_pages);
8145 putFile16BitBE(file, ei->ce_value_fixed_initial);
8146 putFile16BitBE(file, ei->ce_value_random_initial);
8147 putFile8Bit(file, ei->use_last_ce_value);
8149 putFile8Bit(file, ei->use_gfx_element);
8150 putFile16BitBE(file, ei->gfx_element_initial);
8152 putFile8Bit(file, ei->collect_score_initial);
8153 putFile8Bit(file, ei->collect_count_initial);
8155 putFile8Bit(file, ei->drop_delay_fixed);
8156 putFile8Bit(file, ei->push_delay_fixed);
8157 putFile8Bit(file, ei->drop_delay_random);
8158 putFile8Bit(file, ei->push_delay_random);
8159 putFile16BitBE(file, ei->move_delay_fixed);
8160 putFile16BitBE(file, ei->move_delay_random);
8162 // bits 0 - 15 of "move_pattern" ...
8163 putFile16BitBE(file, ei->move_pattern & 0xffff);
8164 putFile8Bit(file, ei->move_direction_initial);
8165 putFile8Bit(file, ei->move_stepsize);
8167 putFile8Bit(file, ei->slippery_type);
8169 for (y = 0; y < 3; y++)
8170 for (x = 0; x < 3; x++)
8171 putFile16BitBE(file, ei->content.e[x][y]);
8173 putFile16BitBE(file, ei->move_enter_element);
8174 putFile16BitBE(file, ei->move_leave_element);
8175 putFile8Bit(file, ei->move_leave_type);
8177 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8178 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8180 putFile8Bit(file, ei->access_direction);
8182 putFile8Bit(file, ei->explosion_delay);
8183 putFile8Bit(file, ei->ignition_delay);
8184 putFile8Bit(file, ei->explosion_type);
8186 // some free bytes for future custom property values and padding
8187 WriteUnusedBytesToFile(file, 1);
8189 // ---------- change page property values (48 bytes) ------------------------
8191 for (i = 0; i < ei->num_change_pages; i++)
8193 struct ElementChangeInfo *change = &ei->change_page[i];
8194 unsigned int event_bits;
8196 // bits 0 - 31 of "has_event[]" ...
8198 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8199 if (change->has_event[j])
8200 event_bits |= (1u << j);
8201 putFile32BitBE(file, event_bits);
8203 putFile16BitBE(file, change->target_element);
8205 putFile16BitBE(file, change->delay_fixed);
8206 putFile16BitBE(file, change->delay_random);
8207 putFile16BitBE(file, change->delay_frames);
8209 putFile16BitBE(file, change->initial_trigger_element);
8211 putFile8Bit(file, change->explode);
8212 putFile8Bit(file, change->use_target_content);
8213 putFile8Bit(file, change->only_if_complete);
8214 putFile8Bit(file, change->use_random_replace);
8216 putFile8Bit(file, change->random_percentage);
8217 putFile8Bit(file, change->replace_when);
8219 for (y = 0; y < 3; y++)
8220 for (x = 0; x < 3; x++)
8221 putFile16BitBE(file, change->target_content.e[x][y]);
8223 putFile8Bit(file, change->can_change);
8225 putFile8Bit(file, change->trigger_side);
8227 putFile8Bit(file, change->trigger_player);
8228 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8229 log_2(change->trigger_page)));
8231 putFile8Bit(file, change->has_action);
8232 putFile8Bit(file, change->action_type);
8233 putFile8Bit(file, change->action_mode);
8234 putFile16BitBE(file, change->action_arg);
8236 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8238 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8239 if (change->has_event[j])
8240 event_bits |= (1u << (j - 32));
8241 putFile8Bit(file, event_bits);
8246 #if ENABLE_HISTORIC_CHUNKS
8247 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8249 struct ElementInfo *ei = &element_info[element];
8250 struct ElementGroupInfo *group = ei->group;
8253 putFile16BitBE(file, element);
8255 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8256 putFile8Bit(file, ei->description[i]);
8258 putFile8Bit(file, group->num_elements);
8260 putFile8Bit(file, ei->use_gfx_element);
8261 putFile16BitBE(file, ei->gfx_element_initial);
8263 putFile8Bit(file, group->choice_mode);
8265 // some free bytes for future values and padding
8266 WriteUnusedBytesToFile(file, 3);
8268 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8269 putFile16BitBE(file, group->element[i]);
8273 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8274 boolean write_element)
8276 int save_type = entry->save_type;
8277 int data_type = entry->data_type;
8278 int conf_type = entry->conf_type;
8279 int byte_mask = conf_type & CONF_MASK_BYTES;
8280 int element = entry->element;
8281 int default_value = entry->default_value;
8283 boolean modified = FALSE;
8285 if (byte_mask != CONF_MASK_MULTI_BYTES)
8287 void *value_ptr = entry->value;
8288 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8291 // check if any settings have been modified before saving them
8292 if (value != default_value)
8295 // do not save if explicitly told or if unmodified default settings
8296 if ((save_type == SAVE_CONF_NEVER) ||
8297 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8301 num_bytes += putFile16BitBE(file, element);
8303 num_bytes += putFile8Bit(file, conf_type);
8304 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8305 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8306 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8309 else if (data_type == TYPE_STRING)
8311 char *default_string = entry->default_string;
8312 char *string = (char *)(entry->value);
8313 int string_length = strlen(string);
8316 // check if any settings have been modified before saving them
8317 if (!strEqual(string, default_string))
8320 // do not save if explicitly told or if unmodified default settings
8321 if ((save_type == SAVE_CONF_NEVER) ||
8322 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8326 num_bytes += putFile16BitBE(file, element);
8328 num_bytes += putFile8Bit(file, conf_type);
8329 num_bytes += putFile16BitBE(file, string_length);
8331 for (i = 0; i < string_length; i++)
8332 num_bytes += putFile8Bit(file, string[i]);
8334 else if (data_type == TYPE_ELEMENT_LIST)
8336 int *element_array = (int *)(entry->value);
8337 int num_elements = *(int *)(entry->num_entities);
8340 // check if any settings have been modified before saving them
8341 for (i = 0; i < num_elements; i++)
8342 if (element_array[i] != default_value)
8345 // do not save if explicitly told or if unmodified default settings
8346 if ((save_type == SAVE_CONF_NEVER) ||
8347 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8351 num_bytes += putFile16BitBE(file, element);
8353 num_bytes += putFile8Bit(file, conf_type);
8354 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8356 for (i = 0; i < num_elements; i++)
8357 num_bytes += putFile16BitBE(file, element_array[i]);
8359 else if (data_type == TYPE_CONTENT_LIST)
8361 struct Content *content = (struct Content *)(entry->value);
8362 int num_contents = *(int *)(entry->num_entities);
8365 // check if any settings have been modified before saving them
8366 for (i = 0; i < num_contents; i++)
8367 for (y = 0; y < 3; y++)
8368 for (x = 0; x < 3; x++)
8369 if (content[i].e[x][y] != default_value)
8372 // do not save if explicitly told or if unmodified default settings
8373 if ((save_type == SAVE_CONF_NEVER) ||
8374 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8378 num_bytes += putFile16BitBE(file, element);
8380 num_bytes += putFile8Bit(file, conf_type);
8381 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8383 for (i = 0; i < num_contents; i++)
8384 for (y = 0; y < 3; y++)
8385 for (x = 0; x < 3; x++)
8386 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8392 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8397 li = *level; // copy level data into temporary buffer
8399 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8400 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8405 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8410 li = *level; // copy level data into temporary buffer
8412 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8413 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8418 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8420 int envelope_nr = element - EL_ENVELOPE_1;
8424 chunk_size += putFile16BitBE(file, element);
8426 // copy envelope data into temporary buffer
8427 xx_envelope = level->envelope[envelope_nr];
8429 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8430 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8435 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8437 struct ElementInfo *ei = &element_info[element];
8441 chunk_size += putFile16BitBE(file, element);
8443 xx_ei = *ei; // copy element data into temporary buffer
8445 // set default description string for this specific element
8446 strcpy(xx_default_description, getDefaultElementDescription(ei));
8448 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8449 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8451 for (i = 0; i < ei->num_change_pages; i++)
8453 struct ElementChangeInfo *change = &ei->change_page[i];
8455 xx_current_change_page = i;
8457 xx_change = *change; // copy change data into temporary buffer
8460 setEventBitsFromEventFlags(change);
8462 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8463 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8470 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8472 struct ElementInfo *ei = &element_info[element];
8473 struct ElementGroupInfo *group = ei->group;
8477 chunk_size += putFile16BitBE(file, element);
8479 xx_ei = *ei; // copy element data into temporary buffer
8480 xx_group = *group; // copy group data into temporary buffer
8482 // set default description string for this specific element
8483 strcpy(xx_default_description, getDefaultElementDescription(ei));
8485 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8486 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8491 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8493 struct ElementInfo *ei = &element_info[element];
8497 chunk_size += putFile16BitBE(file, element);
8499 xx_ei = *ei; // copy element data into temporary buffer
8501 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8502 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8507 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8508 boolean save_as_template)
8514 if (!(file = fopen(filename, MODE_WRITE)))
8516 Warn("cannot save level file '%s'", filename);
8521 level->file_version = FILE_VERSION_ACTUAL;
8522 level->game_version = GAME_VERSION_ACTUAL;
8524 level->creation_date = getCurrentDate();
8526 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8527 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8529 chunk_size = SaveLevel_VERS(NULL, level);
8530 putFileChunkBE(file, "VERS", chunk_size);
8531 SaveLevel_VERS(file, level);
8533 chunk_size = SaveLevel_DATE(NULL, level);
8534 putFileChunkBE(file, "DATE", chunk_size);
8535 SaveLevel_DATE(file, level);
8537 chunk_size = SaveLevel_NAME(NULL, level);
8538 putFileChunkBE(file, "NAME", chunk_size);
8539 SaveLevel_NAME(file, level);
8541 chunk_size = SaveLevel_AUTH(NULL, level);
8542 putFileChunkBE(file, "AUTH", chunk_size);
8543 SaveLevel_AUTH(file, level);
8545 chunk_size = SaveLevel_INFO(NULL, level);
8546 putFileChunkBE(file, "INFO", chunk_size);
8547 SaveLevel_INFO(file, level);
8549 chunk_size = SaveLevel_BODY(NULL, level);
8550 putFileChunkBE(file, "BODY", chunk_size);
8551 SaveLevel_BODY(file, level);
8553 chunk_size = SaveLevel_ELEM(NULL, level);
8554 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8556 putFileChunkBE(file, "ELEM", chunk_size);
8557 SaveLevel_ELEM(file, level);
8560 for (i = 0; i < NUM_ENVELOPES; i++)
8562 int element = EL_ENVELOPE_1 + i;
8564 chunk_size = SaveLevel_NOTE(NULL, level, element);
8565 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8567 putFileChunkBE(file, "NOTE", chunk_size);
8568 SaveLevel_NOTE(file, level, element);
8572 // if not using template level, check for non-default custom/group elements
8573 if (!level->use_custom_template || save_as_template)
8575 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8577 int element = EL_CUSTOM_START + i;
8579 chunk_size = SaveLevel_CUSX(NULL, level, element);
8580 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8582 putFileChunkBE(file, "CUSX", chunk_size);
8583 SaveLevel_CUSX(file, level, element);
8587 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8589 int element = EL_GROUP_START + i;
8591 chunk_size = SaveLevel_GRPX(NULL, level, element);
8592 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8594 putFileChunkBE(file, "GRPX", chunk_size);
8595 SaveLevel_GRPX(file, level, element);
8599 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8601 int element = GET_EMPTY_ELEMENT(i);
8603 chunk_size = SaveLevel_EMPX(NULL, level, element);
8604 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8606 putFileChunkBE(file, "EMPX", chunk_size);
8607 SaveLevel_EMPX(file, level, element);
8614 SetFilePermissions(filename, PERMS_PRIVATE);
8617 void SaveLevel(int nr)
8619 char *filename = getDefaultLevelFilename(nr);
8621 SaveLevelFromFilename(&level, filename, FALSE);
8624 void SaveLevelTemplate(void)
8626 char *filename = getLocalLevelTemplateFilename();
8628 SaveLevelFromFilename(&level, filename, TRUE);
8631 boolean SaveLevelChecked(int nr)
8633 char *filename = getDefaultLevelFilename(nr);
8634 boolean new_level = !fileExists(filename);
8635 boolean level_saved = FALSE;
8637 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8642 Request("Level saved!", REQ_CONFIRM);
8650 void DumpLevel(struct LevelInfo *level)
8652 if (level->no_level_file || level->no_valid_file)
8654 Warn("cannot dump -- no valid level file found");
8660 Print("Level xxx (file version %08d, game version %08d)\n",
8661 level->file_version, level->game_version);
8664 Print("Level author: '%s'\n", level->author);
8665 Print("Level title: '%s'\n", level->name);
8667 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8669 Print("Level time: %d seconds\n", level->time);
8670 Print("Gems needed: %d\n", level->gems_needed);
8672 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8673 Print("Time for wheel: %d seconds\n", level->time_wheel);
8674 Print("Time for light: %d seconds\n", level->time_light);
8675 Print("Time for timegate: %d seconds\n", level->time_timegate);
8677 Print("Amoeba speed: %d\n", level->amoeba_speed);
8680 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8681 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8682 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8683 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8684 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8685 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8691 for (i = 0; i < NUM_ENVELOPES; i++)
8693 char *text = level->envelope[i].text;
8694 int text_len = strlen(text);
8695 boolean has_text = FALSE;
8697 for (j = 0; j < text_len; j++)
8698 if (text[j] != ' ' && text[j] != '\n')
8704 Print("Envelope %d:\n'%s'\n", i + 1, text);
8712 void DumpLevels(void)
8714 static LevelDirTree *dumplevel_leveldir = NULL;
8716 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8717 global.dumplevel_leveldir);
8719 if (dumplevel_leveldir == NULL)
8720 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8722 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8723 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8724 Fail("no such level number: %d", global.dumplevel_level_nr);
8726 leveldir_current = dumplevel_leveldir;
8728 LoadLevel(global.dumplevel_level_nr);
8735 // ============================================================================
8736 // tape file functions
8737 // ============================================================================
8739 static void setTapeInfoToDefaults(void)
8743 // always start with reliable default values (empty tape)
8746 // default values (also for pre-1.2 tapes) with only the first player
8747 tape.player_participates[0] = TRUE;
8748 for (i = 1; i < MAX_PLAYERS; i++)
8749 tape.player_participates[i] = FALSE;
8751 // at least one (default: the first) player participates in every tape
8752 tape.num_participating_players = 1;
8754 tape.property_bits = TAPE_PROPERTY_NONE;
8756 tape.level_nr = level_nr;
8758 tape.changed = FALSE;
8759 tape.solved = FALSE;
8761 tape.recording = FALSE;
8762 tape.playing = FALSE;
8763 tape.pausing = FALSE;
8765 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8766 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8768 tape.no_info_chunk = TRUE;
8769 tape.no_valid_file = FALSE;
8772 static int getTapePosSize(struct TapeInfo *tape)
8774 int tape_pos_size = 0;
8776 if (tape->use_key_actions)
8777 tape_pos_size += tape->num_participating_players;
8779 if (tape->use_mouse_actions)
8780 tape_pos_size += 3; // x and y position and mouse button mask
8782 tape_pos_size += 1; // tape action delay value
8784 return tape_pos_size;
8787 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8789 tape->use_key_actions = FALSE;
8790 tape->use_mouse_actions = FALSE;
8792 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8793 tape->use_key_actions = TRUE;
8795 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8796 tape->use_mouse_actions = TRUE;
8799 static int getTapeActionValue(struct TapeInfo *tape)
8801 return (tape->use_key_actions &&
8802 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8803 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8804 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8805 TAPE_ACTIONS_DEFAULT);
8808 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8810 tape->file_version = getFileVersion(file);
8811 tape->game_version = getFileVersion(file);
8816 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8820 tape->random_seed = getFile32BitBE(file);
8821 tape->date = getFile32BitBE(file);
8822 tape->length = getFile32BitBE(file);
8824 // read header fields that are new since version 1.2
8825 if (tape->file_version >= FILE_VERSION_1_2)
8827 byte store_participating_players = getFile8Bit(file);
8830 // since version 1.2, tapes store which players participate in the tape
8831 tape->num_participating_players = 0;
8832 for (i = 0; i < MAX_PLAYERS; i++)
8834 tape->player_participates[i] = FALSE;
8836 if (store_participating_players & (1 << i))
8838 tape->player_participates[i] = TRUE;
8839 tape->num_participating_players++;
8843 setTapeActionFlags(tape, getFile8Bit(file));
8845 tape->property_bits = getFile8Bit(file);
8846 tape->solved = getFile8Bit(file);
8848 engine_version = getFileVersion(file);
8849 if (engine_version > 0)
8850 tape->engine_version = engine_version;
8852 tape->engine_version = tape->game_version;
8858 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8860 tape->scr_fieldx = getFile8Bit(file);
8861 tape->scr_fieldy = getFile8Bit(file);
8866 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8868 char *level_identifier = NULL;
8869 int level_identifier_size;
8872 tape->no_info_chunk = FALSE;
8874 level_identifier_size = getFile16BitBE(file);
8876 level_identifier = checked_malloc(level_identifier_size);
8878 for (i = 0; i < level_identifier_size; i++)
8879 level_identifier[i] = getFile8Bit(file);
8881 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8882 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8884 checked_free(level_identifier);
8886 tape->level_nr = getFile16BitBE(file);
8888 chunk_size = 2 + level_identifier_size + 2;
8893 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8896 int tape_pos_size = getTapePosSize(tape);
8897 int chunk_size_expected = tape_pos_size * tape->length;
8899 if (chunk_size_expected != chunk_size)
8901 ReadUnusedBytesFromFile(file, chunk_size);
8902 return chunk_size_expected;
8905 for (i = 0; i < tape->length; i++)
8907 if (i >= MAX_TAPE_LEN)
8909 Warn("tape truncated -- size exceeds maximum tape size %d",
8912 // tape too large; read and ignore remaining tape data from this chunk
8913 for (;i < tape->length; i++)
8914 ReadUnusedBytesFromFile(file, tape_pos_size);
8919 if (tape->use_key_actions)
8921 for (j = 0; j < MAX_PLAYERS; j++)
8923 tape->pos[i].action[j] = MV_NONE;
8925 if (tape->player_participates[j])
8926 tape->pos[i].action[j] = getFile8Bit(file);
8930 if (tape->use_mouse_actions)
8932 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8933 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8934 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8937 tape->pos[i].delay = getFile8Bit(file);
8939 if (tape->file_version == FILE_VERSION_1_0)
8941 // eliminate possible diagonal moves in old tapes
8942 // this is only for backward compatibility
8944 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8945 byte action = tape->pos[i].action[0];
8946 int k, num_moves = 0;
8948 for (k = 0; k < 4; k++)
8950 if (action & joy_dir[k])
8952 tape->pos[i + num_moves].action[0] = joy_dir[k];
8954 tape->pos[i + num_moves].delay = 0;
8963 tape->length += num_moves;
8966 else if (tape->file_version < FILE_VERSION_2_0)
8968 // convert pre-2.0 tapes to new tape format
8970 if (tape->pos[i].delay > 1)
8973 tape->pos[i + 1] = tape->pos[i];
8974 tape->pos[i + 1].delay = 1;
8977 for (j = 0; j < MAX_PLAYERS; j++)
8978 tape->pos[i].action[j] = MV_NONE;
8979 tape->pos[i].delay--;
8986 if (checkEndOfFile(file))
8990 if (i != tape->length)
8991 chunk_size = tape_pos_size * i;
8996 static void LoadTape_SokobanSolution(char *filename)
8999 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9001 if (!(file = openFile(filename, MODE_READ)))
9003 tape.no_valid_file = TRUE;
9008 while (!checkEndOfFile(file))
9010 unsigned char c = getByteFromFile(file);
9012 if (checkEndOfFile(file))
9019 tape.pos[tape.length].action[0] = MV_UP;
9020 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9026 tape.pos[tape.length].action[0] = MV_DOWN;
9027 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9033 tape.pos[tape.length].action[0] = MV_LEFT;
9034 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9040 tape.pos[tape.length].action[0] = MV_RIGHT;
9041 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9049 // ignore white-space characters
9053 tape.no_valid_file = TRUE;
9055 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9063 if (tape.no_valid_file)
9066 tape.length_frames = GetTapeLengthFrames();
9067 tape.length_seconds = GetTapeLengthSeconds();
9070 void LoadTapeFromFilename(char *filename)
9072 char cookie[MAX_LINE_LEN];
9073 char chunk_name[CHUNK_ID_LEN + 1];
9077 // always start with reliable default values
9078 setTapeInfoToDefaults();
9080 if (strSuffix(filename, ".sln"))
9082 LoadTape_SokobanSolution(filename);
9087 if (!(file = openFile(filename, MODE_READ)))
9089 tape.no_valid_file = TRUE;
9094 getFileChunkBE(file, chunk_name, NULL);
9095 if (strEqual(chunk_name, "RND1"))
9097 getFile32BitBE(file); // not used
9099 getFileChunkBE(file, chunk_name, NULL);
9100 if (!strEqual(chunk_name, "TAPE"))
9102 tape.no_valid_file = TRUE;
9104 Warn("unknown format of tape file '%s'", filename);
9111 else // check for pre-2.0 file format with cookie string
9113 strcpy(cookie, chunk_name);
9114 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9116 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9117 cookie[strlen(cookie) - 1] = '\0';
9119 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9121 tape.no_valid_file = TRUE;
9123 Warn("unknown format of tape file '%s'", filename);
9130 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9132 tape.no_valid_file = TRUE;
9134 Warn("unsupported version of tape file '%s'", filename);
9141 // pre-2.0 tape files have no game version, so use file version here
9142 tape.game_version = tape.file_version;
9145 if (tape.file_version < FILE_VERSION_1_2)
9147 // tape files from versions before 1.2.0 without chunk structure
9148 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9149 LoadTape_BODY(file, 2 * tape.length, &tape);
9157 int (*loader)(File *, int, struct TapeInfo *);
9161 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9162 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9163 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9164 { "INFO", -1, LoadTape_INFO },
9165 { "BODY", -1, LoadTape_BODY },
9169 while (getFileChunkBE(file, chunk_name, &chunk_size))
9173 while (chunk_info[i].name != NULL &&
9174 !strEqual(chunk_name, chunk_info[i].name))
9177 if (chunk_info[i].name == NULL)
9179 Warn("unknown chunk '%s' in tape file '%s'",
9180 chunk_name, filename);
9182 ReadUnusedBytesFromFile(file, chunk_size);
9184 else if (chunk_info[i].size != -1 &&
9185 chunk_info[i].size != chunk_size)
9187 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9188 chunk_size, chunk_name, filename);
9190 ReadUnusedBytesFromFile(file, chunk_size);
9194 // call function to load this tape chunk
9195 int chunk_size_expected =
9196 (chunk_info[i].loader)(file, chunk_size, &tape);
9198 // the size of some chunks cannot be checked before reading other
9199 // chunks first (like "HEAD" and "BODY") that contain some header
9200 // information, so check them here
9201 if (chunk_size_expected != chunk_size)
9203 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9204 chunk_size, chunk_name, filename);
9212 tape.length_frames = GetTapeLengthFrames();
9213 tape.length_seconds = GetTapeLengthSeconds();
9216 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9218 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9220 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9221 tape.engine_version);
9225 void LoadTape(int nr)
9227 char *filename = getTapeFilename(nr);
9229 LoadTapeFromFilename(filename);
9232 void LoadSolutionTape(int nr)
9234 char *filename = getSolutionTapeFilename(nr);
9236 LoadTapeFromFilename(filename);
9238 if (TAPE_IS_EMPTY(tape))
9240 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9241 level.native_bd_level->replay != NULL)
9242 CopyNativeTape_BD_to_RND(&level);
9243 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9244 level.native_sp_level->demo.is_available)
9245 CopyNativeTape_SP_to_RND(&level);
9249 void LoadScoreTape(char *score_tape_basename, int nr)
9251 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9253 LoadTapeFromFilename(filename);
9256 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9258 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9260 LoadTapeFromFilename(filename);
9263 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9265 // chunk required for team mode tapes with non-default screen size
9266 return (tape->num_participating_players > 1 &&
9267 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9268 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9271 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9273 putFileVersion(file, tape->file_version);
9274 putFileVersion(file, tape->game_version);
9277 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9280 byte store_participating_players = 0;
9282 // set bits for participating players for compact storage
9283 for (i = 0; i < MAX_PLAYERS; i++)
9284 if (tape->player_participates[i])
9285 store_participating_players |= (1 << i);
9287 putFile32BitBE(file, tape->random_seed);
9288 putFile32BitBE(file, tape->date);
9289 putFile32BitBE(file, tape->length);
9291 putFile8Bit(file, store_participating_players);
9293 putFile8Bit(file, getTapeActionValue(tape));
9295 putFile8Bit(file, tape->property_bits);
9296 putFile8Bit(file, tape->solved);
9298 putFileVersion(file, tape->engine_version);
9301 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9303 putFile8Bit(file, tape->scr_fieldx);
9304 putFile8Bit(file, tape->scr_fieldy);
9307 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9309 int level_identifier_size = strlen(tape->level_identifier) + 1;
9312 putFile16BitBE(file, level_identifier_size);
9314 for (i = 0; i < level_identifier_size; i++)
9315 putFile8Bit(file, tape->level_identifier[i]);
9317 putFile16BitBE(file, tape->level_nr);
9320 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9324 for (i = 0; i < tape->length; i++)
9326 if (tape->use_key_actions)
9328 for (j = 0; j < MAX_PLAYERS; j++)
9329 if (tape->player_participates[j])
9330 putFile8Bit(file, tape->pos[i].action[j]);
9333 if (tape->use_mouse_actions)
9335 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9336 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9337 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9340 putFile8Bit(file, tape->pos[i].delay);
9344 void SaveTapeToFilename(char *filename)
9348 int info_chunk_size;
9349 int body_chunk_size;
9351 if (!(file = fopen(filename, MODE_WRITE)))
9353 Warn("cannot save level recording file '%s'", filename);
9358 tape_pos_size = getTapePosSize(&tape);
9360 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9361 body_chunk_size = tape_pos_size * tape.length;
9363 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9364 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9366 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9367 SaveTape_VERS(file, &tape);
9369 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9370 SaveTape_HEAD(file, &tape);
9372 if (checkSaveTape_SCRN(&tape))
9374 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9375 SaveTape_SCRN(file, &tape);
9378 putFileChunkBE(file, "INFO", info_chunk_size);
9379 SaveTape_INFO(file, &tape);
9381 putFileChunkBE(file, "BODY", body_chunk_size);
9382 SaveTape_BODY(file, &tape);
9386 SetFilePermissions(filename, PERMS_PRIVATE);
9389 static void SaveTapeExt(char *filename)
9393 tape.file_version = FILE_VERSION_ACTUAL;
9394 tape.game_version = GAME_VERSION_ACTUAL;
9396 tape.num_participating_players = 0;
9398 // count number of participating players
9399 for (i = 0; i < MAX_PLAYERS; i++)
9400 if (tape.player_participates[i])
9401 tape.num_participating_players++;
9403 SaveTapeToFilename(filename);
9405 tape.changed = FALSE;
9408 void SaveTape(int nr)
9410 char *filename = getTapeFilename(nr);
9412 InitTapeDirectory(leveldir_current->subdir);
9414 SaveTapeExt(filename);
9417 void SaveScoreTape(int nr)
9419 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9421 // used instead of "leveldir_current->subdir" (for network games)
9422 InitScoreTapeDirectory(levelset.identifier, nr);
9424 SaveTapeExt(filename);
9427 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9428 unsigned int req_state_added)
9430 char *filename = getTapeFilename(nr);
9431 boolean new_tape = !fileExists(filename);
9432 boolean tape_saved = FALSE;
9434 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9439 Request(msg_saved, REQ_CONFIRM | req_state_added);
9447 boolean SaveTapeChecked(int nr)
9449 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9452 boolean SaveTapeChecked_LevelSolved(int nr)
9454 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9455 "Level solved! Tape saved!", REQ_STAY_OPEN);
9458 void DumpTape(struct TapeInfo *tape)
9460 int tape_frame_counter;
9463 if (tape->no_valid_file)
9465 Warn("cannot dump -- no valid tape file found");
9472 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9473 tape->level_nr, tape->file_version, tape->game_version);
9474 Print(" (effective engine version %08d)\n",
9475 tape->engine_version);
9476 Print("Level series identifier: '%s'\n", tape->level_identifier);
9478 Print("Solution tape: %s\n",
9479 tape->solved ? "yes" :
9480 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9482 Print("Special tape properties: ");
9483 if (tape->property_bits == TAPE_PROPERTY_NONE)
9485 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9486 Print("[em_random_bug]");
9487 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9488 Print("[game_speed]");
9489 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9491 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9492 Print("[single_step]");
9493 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9494 Print("[snapshot]");
9495 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9496 Print("[replayed]");
9497 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9498 Print("[tas_keys]");
9499 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9500 Print("[small_graphics]");
9503 int year2 = tape->date / 10000;
9504 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9505 int month_index_raw = (tape->date / 100) % 100;
9506 int month_index = month_index_raw % 12; // prevent invalid index
9507 int month = month_index + 1;
9508 int day = tape->date % 100;
9510 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9514 tape_frame_counter = 0;
9516 for (i = 0; i < tape->length; i++)
9518 if (i >= MAX_TAPE_LEN)
9523 for (j = 0; j < MAX_PLAYERS; j++)
9525 if (tape->player_participates[j])
9527 int action = tape->pos[i].action[j];
9529 Print("%d:%02x ", j, action);
9530 Print("[%c%c%c%c|%c%c] - ",
9531 (action & JOY_LEFT ? '<' : ' '),
9532 (action & JOY_RIGHT ? '>' : ' '),
9533 (action & JOY_UP ? '^' : ' '),
9534 (action & JOY_DOWN ? 'v' : ' '),
9535 (action & JOY_BUTTON_1 ? '1' : ' '),
9536 (action & JOY_BUTTON_2 ? '2' : ' '));
9540 Print("(%03d) ", tape->pos[i].delay);
9541 Print("[%05d]\n", tape_frame_counter);
9543 tape_frame_counter += tape->pos[i].delay;
9549 void DumpTapes(void)
9551 static LevelDirTree *dumptape_leveldir = NULL;
9553 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9554 global.dumptape_leveldir);
9556 if (dumptape_leveldir == NULL)
9557 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9559 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9560 global.dumptape_level_nr > dumptape_leveldir->last_level)
9561 Fail("no such level number: %d", global.dumptape_level_nr);
9563 leveldir_current = dumptape_leveldir;
9565 if (options.mytapes)
9566 LoadTape(global.dumptape_level_nr);
9568 LoadSolutionTape(global.dumptape_level_nr);
9576 // ============================================================================
9577 // score file functions
9578 // ============================================================================
9580 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9584 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9586 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9587 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9588 scores->entry[i].score = 0;
9589 scores->entry[i].time = 0;
9591 scores->entry[i].id = -1;
9592 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9593 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9594 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9595 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9596 strcpy(scores->entry[i].country_code, "??");
9599 scores->num_entries = 0;
9600 scores->last_added = -1;
9601 scores->last_added_local = -1;
9603 scores->updated = FALSE;
9604 scores->uploaded = FALSE;
9605 scores->tape_downloaded = FALSE;
9606 scores->force_last_added = FALSE;
9608 // The following values are intentionally not reset here:
9612 // - continue_playing
9613 // - continue_on_return
9616 static void setScoreInfoToDefaults(void)
9618 setScoreInfoToDefaultsExt(&scores);
9621 static void setServerScoreInfoToDefaults(void)
9623 setScoreInfoToDefaultsExt(&server_scores);
9626 static void LoadScore_OLD(int nr)
9629 char *filename = getScoreFilename(nr);
9630 char cookie[MAX_LINE_LEN];
9631 char line[MAX_LINE_LEN];
9635 if (!(file = fopen(filename, MODE_READ)))
9638 // check file identifier
9639 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9641 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9642 cookie[strlen(cookie) - 1] = '\0';
9644 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9646 Warn("unknown format of score file '%s'", filename);
9653 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9655 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9656 Warn("fscanf() failed; %s", strerror(errno));
9658 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9661 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9662 line[strlen(line) - 1] = '\0';
9664 for (line_ptr = line; *line_ptr; line_ptr++)
9666 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9668 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9669 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9678 static void ConvertScore_OLD(void)
9680 // only convert score to time for levels that rate playing time over score
9681 if (!level.rate_time_over_score)
9684 // convert old score to playing time for score-less levels (like Supaplex)
9685 int time_final_max = 999;
9688 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9690 int score = scores.entry[i].score;
9692 if (score > 0 && score < time_final_max)
9693 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9697 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9699 scores->file_version = getFileVersion(file);
9700 scores->game_version = getFileVersion(file);
9705 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9707 char *level_identifier = NULL;
9708 int level_identifier_size;
9711 level_identifier_size = getFile16BitBE(file);
9713 level_identifier = checked_malloc(level_identifier_size);
9715 for (i = 0; i < level_identifier_size; i++)
9716 level_identifier[i] = getFile8Bit(file);
9718 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9719 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9721 checked_free(level_identifier);
9723 scores->level_nr = getFile16BitBE(file);
9724 scores->num_entries = getFile16BitBE(file);
9726 chunk_size = 2 + level_identifier_size + 2 + 2;
9731 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9735 for (i = 0; i < scores->num_entries; i++)
9737 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9738 scores->entry[i].name[j] = getFile8Bit(file);
9740 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9743 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9748 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9752 for (i = 0; i < scores->num_entries; i++)
9753 scores->entry[i].score = getFile16BitBE(file);
9755 chunk_size = scores->num_entries * 2;
9760 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9764 for (i = 0; i < scores->num_entries; i++)
9765 scores->entry[i].score = getFile32BitBE(file);
9767 chunk_size = scores->num_entries * 4;
9772 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9776 for (i = 0; i < scores->num_entries; i++)
9777 scores->entry[i].time = getFile32BitBE(file);
9779 chunk_size = scores->num_entries * 4;
9784 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9788 for (i = 0; i < scores->num_entries; i++)
9790 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9791 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9793 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9796 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9801 void LoadScore(int nr)
9803 char *filename = getScoreFilename(nr);
9804 char cookie[MAX_LINE_LEN];
9805 char chunk_name[CHUNK_ID_LEN + 1];
9807 boolean old_score_file_format = FALSE;
9810 // always start with reliable default values
9811 setScoreInfoToDefaults();
9813 if (!(file = openFile(filename, MODE_READ)))
9816 getFileChunkBE(file, chunk_name, NULL);
9817 if (strEqual(chunk_name, "RND1"))
9819 getFile32BitBE(file); // not used
9821 getFileChunkBE(file, chunk_name, NULL);
9822 if (!strEqual(chunk_name, "SCOR"))
9824 Warn("unknown format of score file '%s'", filename);
9831 else // check for old file format with cookie string
9833 strcpy(cookie, chunk_name);
9834 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9836 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9837 cookie[strlen(cookie) - 1] = '\0';
9839 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9841 Warn("unknown format of score file '%s'", filename);
9848 old_score_file_format = TRUE;
9851 if (old_score_file_format)
9853 // score files from versions before 4.2.4.0 without chunk structure
9856 // convert score to time, if possible (mainly for Supaplex levels)
9865 int (*loader)(File *, int, struct ScoreInfo *);
9869 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9870 { "INFO", -1, LoadScore_INFO },
9871 { "NAME", -1, LoadScore_NAME },
9872 { "SCOR", -1, LoadScore_SCOR },
9873 { "SC4R", -1, LoadScore_SC4R },
9874 { "TIME", -1, LoadScore_TIME },
9875 { "TAPE", -1, LoadScore_TAPE },
9880 while (getFileChunkBE(file, chunk_name, &chunk_size))
9884 while (chunk_info[i].name != NULL &&
9885 !strEqual(chunk_name, chunk_info[i].name))
9888 if (chunk_info[i].name == NULL)
9890 Warn("unknown chunk '%s' in score file '%s'",
9891 chunk_name, filename);
9893 ReadUnusedBytesFromFile(file, chunk_size);
9895 else if (chunk_info[i].size != -1 &&
9896 chunk_info[i].size != chunk_size)
9898 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9899 chunk_size, chunk_name, filename);
9901 ReadUnusedBytesFromFile(file, chunk_size);
9905 // call function to load this score chunk
9906 int chunk_size_expected =
9907 (chunk_info[i].loader)(file, chunk_size, &scores);
9909 // the size of some chunks cannot be checked before reading other
9910 // chunks first (like "HEAD" and "BODY") that contain some header
9911 // information, so check them here
9912 if (chunk_size_expected != chunk_size)
9914 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9915 chunk_size, chunk_name, filename);
9924 #if ENABLE_HISTORIC_CHUNKS
9925 void SaveScore_OLD(int nr)
9928 char *filename = getScoreFilename(nr);
9931 // used instead of "leveldir_current->subdir" (for network games)
9932 InitScoreDirectory(levelset.identifier);
9934 if (!(file = fopen(filename, MODE_WRITE)))
9936 Warn("cannot save score for level %d", nr);
9941 fprintf(file, "%s\n\n", SCORE_COOKIE);
9943 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9944 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9948 SetFilePermissions(filename, PERMS_PRIVATE);
9952 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9954 putFileVersion(file, scores->file_version);
9955 putFileVersion(file, scores->game_version);
9958 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9960 int level_identifier_size = strlen(scores->level_identifier) + 1;
9963 putFile16BitBE(file, level_identifier_size);
9965 for (i = 0; i < level_identifier_size; i++)
9966 putFile8Bit(file, scores->level_identifier[i]);
9968 putFile16BitBE(file, scores->level_nr);
9969 putFile16BitBE(file, scores->num_entries);
9972 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9976 for (i = 0; i < scores->num_entries; i++)
9978 int name_size = strlen(scores->entry[i].name);
9980 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9981 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9985 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9989 for (i = 0; i < scores->num_entries; i++)
9990 putFile16BitBE(file, scores->entry[i].score);
9993 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9997 for (i = 0; i < scores->num_entries; i++)
9998 putFile32BitBE(file, scores->entry[i].score);
10001 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10005 for (i = 0; i < scores->num_entries; i++)
10006 putFile32BitBE(file, scores->entry[i].time);
10009 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10013 for (i = 0; i < scores->num_entries; i++)
10015 int size = strlen(scores->entry[i].tape_basename);
10017 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10018 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10022 static void SaveScoreToFilename(char *filename)
10025 int info_chunk_size;
10026 int name_chunk_size;
10027 int scor_chunk_size;
10028 int sc4r_chunk_size;
10029 int time_chunk_size;
10030 int tape_chunk_size;
10031 boolean has_large_score_values;
10034 if (!(file = fopen(filename, MODE_WRITE)))
10036 Warn("cannot save score file '%s'", filename);
10041 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10042 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10043 scor_chunk_size = scores.num_entries * 2;
10044 sc4r_chunk_size = scores.num_entries * 4;
10045 time_chunk_size = scores.num_entries * 4;
10046 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10048 has_large_score_values = FALSE;
10049 for (i = 0; i < scores.num_entries; i++)
10050 if (scores.entry[i].score > 0xffff)
10051 has_large_score_values = TRUE;
10053 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10054 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10056 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10057 SaveScore_VERS(file, &scores);
10059 putFileChunkBE(file, "INFO", info_chunk_size);
10060 SaveScore_INFO(file, &scores);
10062 putFileChunkBE(file, "NAME", name_chunk_size);
10063 SaveScore_NAME(file, &scores);
10065 if (has_large_score_values)
10067 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10068 SaveScore_SC4R(file, &scores);
10072 putFileChunkBE(file, "SCOR", scor_chunk_size);
10073 SaveScore_SCOR(file, &scores);
10076 putFileChunkBE(file, "TIME", time_chunk_size);
10077 SaveScore_TIME(file, &scores);
10079 putFileChunkBE(file, "TAPE", tape_chunk_size);
10080 SaveScore_TAPE(file, &scores);
10084 SetFilePermissions(filename, PERMS_PRIVATE);
10087 void SaveScore(int nr)
10089 char *filename = getScoreFilename(nr);
10092 // used instead of "leveldir_current->subdir" (for network games)
10093 InitScoreDirectory(levelset.identifier);
10095 scores.file_version = FILE_VERSION_ACTUAL;
10096 scores.game_version = GAME_VERSION_ACTUAL;
10098 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10099 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10100 scores.level_nr = level_nr;
10102 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10103 if (scores.entry[i].score == 0 &&
10104 scores.entry[i].time == 0 &&
10105 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10108 scores.num_entries = i;
10110 if (scores.num_entries == 0)
10113 SaveScoreToFilename(filename);
10116 static void LoadServerScoreFromCache(int nr)
10118 struct ScoreEntry score_entry;
10127 { &score_entry.score, FALSE, 0 },
10128 { &score_entry.time, FALSE, 0 },
10129 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10130 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10131 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10132 { &score_entry.id, FALSE, 0 },
10133 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10134 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10135 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10136 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10140 char *filename = getScoreCacheFilename(nr);
10141 SetupFileHash *score_hash = loadSetupFileHash(filename);
10144 server_scores.num_entries = 0;
10146 if (score_hash == NULL)
10149 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10151 score_entry = server_scores.entry[i];
10153 for (j = 0; score_mapping[j].value != NULL; j++)
10157 sprintf(token, "%02d.%d", i, j);
10159 char *value = getHashEntry(score_hash, token);
10164 if (score_mapping[j].is_string)
10166 char *score_value = (char *)score_mapping[j].value;
10167 int value_size = score_mapping[j].string_size;
10169 strncpy(score_value, value, value_size);
10170 score_value[value_size] = '\0';
10174 int *score_value = (int *)score_mapping[j].value;
10176 *score_value = atoi(value);
10179 server_scores.num_entries = i + 1;
10182 server_scores.entry[i] = score_entry;
10185 freeSetupFileHash(score_hash);
10188 void LoadServerScore(int nr, boolean download_score)
10190 if (!setup.use_api_server)
10193 // always start with reliable default values
10194 setServerScoreInfoToDefaults();
10196 // 1st step: load server scores from cache file (which may not exist)
10197 // (this should prevent reading it while the thread is writing to it)
10198 LoadServerScoreFromCache(nr);
10200 if (download_score && runtime.use_api_server)
10202 // 2nd step: download server scores from score server to cache file
10203 // (as thread, as it might time out if the server is not reachable)
10204 ApiGetScoreAsThread(nr);
10208 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10210 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10212 // if score tape not uploaded, ask for uploading missing tapes later
10213 if (!setup.has_remaining_tapes)
10214 setup.ask_for_remaining_tapes = TRUE;
10216 setup.provide_uploading_tapes = TRUE;
10217 setup.has_remaining_tapes = TRUE;
10219 SaveSetup_ServerSetup();
10222 void SaveServerScore(int nr, boolean tape_saved)
10224 if (!runtime.use_api_server)
10226 PrepareScoreTapesForUpload(leveldir_current->subdir);
10231 ApiAddScoreAsThread(nr, tape_saved, NULL);
10234 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10235 char *score_tape_filename)
10237 if (!runtime.use_api_server)
10240 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10243 void LoadLocalAndServerScore(int nr, boolean download_score)
10245 int last_added_local = scores.last_added_local;
10246 boolean force_last_added = scores.force_last_added;
10248 // needed if only showing server scores
10249 setScoreInfoToDefaults();
10251 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10254 // restore last added local score entry (before merging server scores)
10255 scores.last_added = scores.last_added_local = last_added_local;
10257 if (setup.use_api_server &&
10258 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10260 // load server scores from cache file and trigger update from server
10261 LoadServerScore(nr, download_score);
10263 // merge local scores with scores from server
10264 MergeServerScore();
10267 if (force_last_added)
10268 scores.force_last_added = force_last_added;
10272 // ============================================================================
10273 // setup file functions
10274 // ============================================================================
10276 #define TOKEN_STR_PLAYER_PREFIX "player_"
10279 static struct TokenInfo global_setup_tokens[] =
10283 &setup.player_name, "player_name"
10287 &setup.multiple_users, "multiple_users"
10291 &setup.sound, "sound"
10295 &setup.sound_loops, "repeating_sound_loops"
10299 &setup.sound_music, "background_music"
10303 &setup.sound_simple, "simple_sound_effects"
10307 &setup.toons, "toons"
10311 &setup.global_animations, "global_animations"
10315 &setup.scroll_delay, "scroll_delay"
10319 &setup.forced_scroll_delay, "forced_scroll_delay"
10323 &setup.scroll_delay_value, "scroll_delay_value"
10327 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10331 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10335 &setup.fade_screens, "fade_screens"
10339 &setup.autorecord, "automatic_tape_recording"
10343 &setup.autorecord_after_replay, "autorecord_after_replay"
10347 &setup.auto_pause_on_start, "auto_pause_on_start"
10351 &setup.show_titlescreen, "show_titlescreen"
10355 &setup.quick_doors, "quick_doors"
10359 &setup.team_mode, "team_mode"
10363 &setup.handicap, "handicap"
10367 &setup.skip_levels, "skip_levels"
10371 &setup.increment_levels, "increment_levels"
10375 &setup.auto_play_next_level, "auto_play_next_level"
10379 &setup.count_score_after_game, "count_score_after_game"
10383 &setup.show_scores_after_game, "show_scores_after_game"
10387 &setup.time_limit, "time_limit"
10391 &setup.fullscreen, "fullscreen"
10395 &setup.window_scaling_percent, "window_scaling_percent"
10399 &setup.window_scaling_quality, "window_scaling_quality"
10403 &setup.screen_rendering_mode, "screen_rendering_mode"
10407 &setup.vsync_mode, "vsync_mode"
10411 &setup.ask_on_escape, "ask_on_escape"
10415 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10419 &setup.ask_on_game_over, "ask_on_game_over"
10423 &setup.ask_on_quit_game, "ask_on_quit_game"
10427 &setup.ask_on_quit_program, "ask_on_quit_program"
10431 &setup.quick_switch, "quick_player_switch"
10435 &setup.input_on_focus, "input_on_focus"
10439 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10443 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10447 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10451 &setup.game_speed_extended, "game_speed_extended"
10455 &setup.game_frame_delay, "game_frame_delay"
10459 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10463 &setup.bd_skip_hatching, "bd_skip_hatching"
10467 &setup.bd_scroll_delay, "bd_scroll_delay"
10471 &setup.bd_smooth_movements, "bd_smooth_movements"
10475 &setup.sp_show_border_elements, "sp_show_border_elements"
10479 &setup.small_game_graphics, "small_game_graphics"
10483 &setup.show_load_save_buttons, "show_load_save_buttons"
10487 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10491 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10495 &setup.graphics_set, "graphics_set"
10499 &setup.sounds_set, "sounds_set"
10503 &setup.music_set, "music_set"
10507 &setup.override_level_graphics, "override_level_graphics"
10511 &setup.override_level_sounds, "override_level_sounds"
10515 &setup.override_level_music, "override_level_music"
10519 &setup.volume_simple, "volume_simple"
10523 &setup.volume_loops, "volume_loops"
10527 &setup.volume_music, "volume_music"
10531 &setup.network_mode, "network_mode"
10535 &setup.network_player_nr, "network_player"
10539 &setup.network_server_hostname, "network_server_hostname"
10543 &setup.touch.control_type, "touch.control_type"
10547 &setup.touch.move_distance, "touch.move_distance"
10551 &setup.touch.drop_distance, "touch.drop_distance"
10555 &setup.touch.transparency, "touch.transparency"
10559 &setup.touch.draw_outlined, "touch.draw_outlined"
10563 &setup.touch.draw_pressed, "touch.draw_pressed"
10567 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10571 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10575 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10579 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10583 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10587 static struct TokenInfo auto_setup_tokens[] =
10591 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10595 static struct TokenInfo server_setup_tokens[] =
10599 &setup.player_uuid, "player_uuid"
10603 &setup.player_version, "player_version"
10607 &setup.use_api_server, TEST_PREFIX "use_api_server"
10611 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10615 &setup.api_server_password, TEST_PREFIX "api_server_password"
10619 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10623 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10627 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10631 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10635 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10639 static struct TokenInfo editor_setup_tokens[] =
10643 &setup.editor.el_classic, "editor.el_classic"
10647 &setup.editor.el_custom, "editor.el_custom"
10651 &setup.editor.el_user_defined, "editor.el_user_defined"
10655 &setup.editor.el_dynamic, "editor.el_dynamic"
10659 &setup.editor.el_headlines, "editor.el_headlines"
10663 &setup.editor.show_element_token, "editor.show_element_token"
10667 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10671 static struct TokenInfo editor_cascade_setup_tokens[] =
10675 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10679 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10683 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10687 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10691 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10695 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10699 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10703 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10707 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10711 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10715 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10719 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10723 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10727 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10731 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10735 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10739 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10743 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10747 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10751 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10755 static struct TokenInfo shortcut_setup_tokens[] =
10759 &setup.shortcut.save_game, "shortcut.save_game"
10763 &setup.shortcut.load_game, "shortcut.load_game"
10767 &setup.shortcut.restart_game, "shortcut.restart_game"
10771 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10775 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10779 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10783 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10787 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10791 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10795 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10799 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10803 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10807 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10811 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10815 &setup.shortcut.tape_record, "shortcut.tape_record"
10819 &setup.shortcut.tape_play, "shortcut.tape_play"
10823 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10827 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10831 &setup.shortcut.sound_music, "shortcut.sound_music"
10835 &setup.shortcut.snap_left, "shortcut.snap_left"
10839 &setup.shortcut.snap_right, "shortcut.snap_right"
10843 &setup.shortcut.snap_up, "shortcut.snap_up"
10847 &setup.shortcut.snap_down, "shortcut.snap_down"
10851 static struct SetupInputInfo setup_input;
10852 static struct TokenInfo player_setup_tokens[] =
10856 &setup_input.use_joystick, ".use_joystick"
10860 &setup_input.joy.device_name, ".joy.device_name"
10864 &setup_input.joy.xleft, ".joy.xleft"
10868 &setup_input.joy.xmiddle, ".joy.xmiddle"
10872 &setup_input.joy.xright, ".joy.xright"
10876 &setup_input.joy.yupper, ".joy.yupper"
10880 &setup_input.joy.ymiddle, ".joy.ymiddle"
10884 &setup_input.joy.ylower, ".joy.ylower"
10888 &setup_input.joy.snap, ".joy.snap_field"
10892 &setup_input.joy.drop, ".joy.place_bomb"
10896 &setup_input.key.left, ".key.move_left"
10900 &setup_input.key.right, ".key.move_right"
10904 &setup_input.key.up, ".key.move_up"
10908 &setup_input.key.down, ".key.move_down"
10912 &setup_input.key.snap, ".key.snap_field"
10916 &setup_input.key.drop, ".key.place_bomb"
10920 static struct TokenInfo system_setup_tokens[] =
10924 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10928 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10932 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10936 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10940 static struct TokenInfo internal_setup_tokens[] =
10944 &setup.internal.program_title, "program_title"
10948 &setup.internal.program_version, "program_version"
10952 &setup.internal.program_author, "program_author"
10956 &setup.internal.program_email, "program_email"
10960 &setup.internal.program_website, "program_website"
10964 &setup.internal.program_copyright, "program_copyright"
10968 &setup.internal.program_company, "program_company"
10972 &setup.internal.program_icon_file, "program_icon_file"
10976 &setup.internal.default_graphics_set, "default_graphics_set"
10980 &setup.internal.default_sounds_set, "default_sounds_set"
10984 &setup.internal.default_music_set, "default_music_set"
10988 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10992 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10996 &setup.internal.fallback_music_file, "fallback_music_file"
11000 &setup.internal.default_level_series, "default_level_series"
11004 &setup.internal.default_window_width, "default_window_width"
11008 &setup.internal.default_window_height, "default_window_height"
11012 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11016 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11020 &setup.internal.create_user_levelset, "create_user_levelset"
11024 &setup.internal.info_screens_from_main, "info_screens_from_main"
11028 &setup.internal.menu_game, "menu_game"
11032 &setup.internal.menu_engines, "menu_engines"
11036 &setup.internal.menu_editor, "menu_editor"
11040 &setup.internal.menu_graphics, "menu_graphics"
11044 &setup.internal.menu_sound, "menu_sound"
11048 &setup.internal.menu_artwork, "menu_artwork"
11052 &setup.internal.menu_input, "menu_input"
11056 &setup.internal.menu_touch, "menu_touch"
11060 &setup.internal.menu_shortcuts, "menu_shortcuts"
11064 &setup.internal.menu_exit, "menu_exit"
11068 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11072 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11076 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11080 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11084 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11088 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11092 &setup.internal.info_title, "info_title"
11096 &setup.internal.info_elements, "info_elements"
11100 &setup.internal.info_music, "info_music"
11104 &setup.internal.info_credits, "info_credits"
11108 &setup.internal.info_program, "info_program"
11112 &setup.internal.info_version, "info_version"
11116 &setup.internal.info_levelset, "info_levelset"
11120 &setup.internal.info_exit, "info_exit"
11124 static struct TokenInfo debug_setup_tokens[] =
11128 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11132 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11136 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11140 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11144 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11148 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11152 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11156 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11160 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11164 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11168 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11172 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11176 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11180 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11184 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11188 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11192 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11196 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11200 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11204 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11208 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11211 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11215 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11219 &setup.debug.xsn_mode, "debug.xsn_mode"
11223 &setup.debug.xsn_percent, "debug.xsn_percent"
11227 static struct TokenInfo options_setup_tokens[] =
11231 &setup.options.verbose, "options.verbose"
11235 &setup.options.debug, "options.debug"
11239 &setup.options.debug_mode, "options.debug_mode"
11243 static void setSetupInfoToDefaults(struct SetupInfo *si)
11247 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11249 si->multiple_users = TRUE;
11252 si->sound_loops = TRUE;
11253 si->sound_music = TRUE;
11254 si->sound_simple = TRUE;
11256 si->global_animations = TRUE;
11257 si->scroll_delay = TRUE;
11258 si->forced_scroll_delay = FALSE;
11259 si->scroll_delay_value = STD_SCROLL_DELAY;
11260 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11261 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11262 si->fade_screens = TRUE;
11263 si->autorecord = TRUE;
11264 si->autorecord_after_replay = TRUE;
11265 si->auto_pause_on_start = FALSE;
11266 si->show_titlescreen = TRUE;
11267 si->quick_doors = FALSE;
11268 si->team_mode = FALSE;
11269 si->handicap = TRUE;
11270 si->skip_levels = TRUE;
11271 si->increment_levels = TRUE;
11272 si->auto_play_next_level = TRUE;
11273 si->count_score_after_game = TRUE;
11274 si->show_scores_after_game = TRUE;
11275 si->time_limit = TRUE;
11276 si->fullscreen = FALSE;
11277 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11278 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11279 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11280 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11281 si->ask_on_escape = TRUE;
11282 si->ask_on_escape_editor = TRUE;
11283 si->ask_on_game_over = TRUE;
11284 si->ask_on_quit_game = TRUE;
11285 si->ask_on_quit_program = TRUE;
11286 si->quick_switch = FALSE;
11287 si->input_on_focus = FALSE;
11288 si->prefer_aga_graphics = TRUE;
11289 si->prefer_lowpass_sounds = FALSE;
11290 si->prefer_extra_panel_items = TRUE;
11291 si->game_speed_extended = FALSE;
11292 si->game_frame_delay = GAME_FRAME_DELAY;
11293 si->bd_skip_uncovering = FALSE;
11294 si->bd_skip_hatching = FALSE;
11295 si->bd_scroll_delay = TRUE;
11296 si->bd_smooth_movements = AUTO;
11297 si->sp_show_border_elements = FALSE;
11298 si->small_game_graphics = FALSE;
11299 si->show_load_save_buttons = FALSE;
11300 si->show_undo_redo_buttons = FALSE;
11301 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11303 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11304 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11305 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11307 si->override_level_graphics = FALSE;
11308 si->override_level_sounds = FALSE;
11309 si->override_level_music = FALSE;
11311 si->volume_simple = 100; // percent
11312 si->volume_loops = 100; // percent
11313 si->volume_music = 100; // percent
11315 si->network_mode = FALSE;
11316 si->network_player_nr = 0; // first player
11317 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11319 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11320 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11321 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11322 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11323 si->touch.draw_outlined = TRUE;
11324 si->touch.draw_pressed = TRUE;
11326 for (i = 0; i < 2; i++)
11328 char *default_grid_button[6][2] =
11334 { "111222", " vv " },
11335 { "111222", " vv " }
11337 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11338 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11339 int min_xsize = MIN(6, grid_xsize);
11340 int min_ysize = MIN(6, grid_ysize);
11341 int startx = grid_xsize - min_xsize;
11342 int starty = grid_ysize - min_ysize;
11345 // virtual buttons grid can only be set to defaults if video is initialized
11346 // (this will be repeated if virtual buttons are not loaded from setup file)
11347 if (video.initialized)
11349 si->touch.grid_xsize[i] = grid_xsize;
11350 si->touch.grid_ysize[i] = grid_ysize;
11354 si->touch.grid_xsize[i] = -1;
11355 si->touch.grid_ysize[i] = -1;
11358 for (x = 0; x < MAX_GRID_XSIZE; x++)
11359 for (y = 0; y < MAX_GRID_YSIZE; y++)
11360 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11362 for (x = 0; x < min_xsize; x++)
11363 for (y = 0; y < min_ysize; y++)
11364 si->touch.grid_button[i][x][starty + y] =
11365 default_grid_button[y][0][x];
11367 for (x = 0; x < min_xsize; x++)
11368 for (y = 0; y < min_ysize; y++)
11369 si->touch.grid_button[i][startx + x][starty + y] =
11370 default_grid_button[y][1][x];
11373 si->touch.grid_initialized = video.initialized;
11375 si->touch.overlay_buttons = FALSE;
11377 si->editor.el_boulderdash = TRUE;
11378 si->editor.el_boulderdash_native = TRUE;
11379 si->editor.el_boulderdash_effects = TRUE;
11380 si->editor.el_emerald_mine = TRUE;
11381 si->editor.el_emerald_mine_club = TRUE;
11382 si->editor.el_more = TRUE;
11383 si->editor.el_sokoban = TRUE;
11384 si->editor.el_supaplex = TRUE;
11385 si->editor.el_diamond_caves = TRUE;
11386 si->editor.el_dx_boulderdash = TRUE;
11388 si->editor.el_mirror_magic = TRUE;
11389 si->editor.el_deflektor = TRUE;
11391 si->editor.el_chars = TRUE;
11392 si->editor.el_steel_chars = TRUE;
11394 si->editor.el_classic = TRUE;
11395 si->editor.el_custom = TRUE;
11397 si->editor.el_user_defined = FALSE;
11398 si->editor.el_dynamic = TRUE;
11400 si->editor.el_headlines = TRUE;
11402 si->editor.show_element_token = FALSE;
11404 si->editor.show_read_only_warning = TRUE;
11406 si->editor.use_template_for_new_levels = TRUE;
11408 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11409 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11410 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11411 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11412 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11414 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11415 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11416 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11417 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11418 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11420 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11421 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11422 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11423 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11424 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11425 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11427 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11428 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11429 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11431 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11432 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11433 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11434 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11436 for (i = 0; i < MAX_PLAYERS; i++)
11438 si->input[i].use_joystick = FALSE;
11439 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11440 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11441 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11442 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11443 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11444 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11445 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11446 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11447 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11448 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11449 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11450 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11451 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11452 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11453 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11456 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11457 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11458 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11459 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11461 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11462 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11463 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11464 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11465 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11466 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11467 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11469 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11471 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11472 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11473 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11475 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11476 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11477 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11479 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11480 si->internal.choose_from_top_leveldir = FALSE;
11481 si->internal.show_scaling_in_title = TRUE;
11482 si->internal.create_user_levelset = TRUE;
11483 si->internal.info_screens_from_main = FALSE;
11485 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11486 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11488 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11489 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11490 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11491 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11492 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11493 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11494 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11495 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11496 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11497 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11499 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11500 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11501 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11502 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11503 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11504 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11505 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11506 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11507 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11508 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11510 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11511 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11513 si->debug.show_frames_per_second = FALSE;
11515 si->debug.xsn_mode = AUTO;
11516 si->debug.xsn_percent = 0;
11518 si->options.verbose = FALSE;
11519 si->options.debug = FALSE;
11520 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11522 #if defined(PLATFORM_ANDROID)
11523 si->fullscreen = TRUE;
11524 si->touch.overlay_buttons = TRUE;
11527 setHideSetupEntry(&setup.debug.xsn_mode);
11530 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11532 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11535 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11537 si->player_uuid = NULL; // (will be set later)
11538 si->player_version = 1; // (will be set later)
11540 si->use_api_server = TRUE;
11541 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11542 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11543 si->ask_for_uploading_tapes = TRUE;
11544 si->ask_for_remaining_tapes = FALSE;
11545 si->provide_uploading_tapes = TRUE;
11546 si->ask_for_using_api_server = TRUE;
11547 si->has_remaining_tapes = FALSE;
11550 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11552 si->editor_cascade.el_bd = TRUE;
11553 si->editor_cascade.el_bd_native = TRUE;
11554 si->editor_cascade.el_bd_effects = FALSE;
11555 si->editor_cascade.el_em = TRUE;
11556 si->editor_cascade.el_emc = TRUE;
11557 si->editor_cascade.el_rnd = TRUE;
11558 si->editor_cascade.el_sb = TRUE;
11559 si->editor_cascade.el_sp = TRUE;
11560 si->editor_cascade.el_dc = TRUE;
11561 si->editor_cascade.el_dx = TRUE;
11563 si->editor_cascade.el_mm = TRUE;
11564 si->editor_cascade.el_df = TRUE;
11566 si->editor_cascade.el_chars = FALSE;
11567 si->editor_cascade.el_steel_chars = FALSE;
11568 si->editor_cascade.el_ce = FALSE;
11569 si->editor_cascade.el_ge = FALSE;
11570 si->editor_cascade.el_es = FALSE;
11571 si->editor_cascade.el_ref = FALSE;
11572 si->editor_cascade.el_user = FALSE;
11573 si->editor_cascade.el_dynamic = FALSE;
11576 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11578 static char *getHideSetupToken(void *setup_value)
11580 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11582 if (setup_value != NULL)
11583 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11585 return hide_setup_token;
11588 void setHideSetupEntry(void *setup_value)
11590 char *hide_setup_token = getHideSetupToken(setup_value);
11592 if (hide_setup_hash == NULL)
11593 hide_setup_hash = newSetupFileHash();
11595 if (setup_value != NULL)
11596 setHashEntry(hide_setup_hash, hide_setup_token, "");
11599 void removeHideSetupEntry(void *setup_value)
11601 char *hide_setup_token = getHideSetupToken(setup_value);
11603 if (setup_value != NULL)
11604 removeHashEntry(hide_setup_hash, hide_setup_token);
11607 boolean hideSetupEntry(void *setup_value)
11609 char *hide_setup_token = getHideSetupToken(setup_value);
11611 return (setup_value != NULL &&
11612 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11615 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11616 struct TokenInfo *token_info,
11617 int token_nr, char *token_text)
11619 char *token_hide_text = getStringCat2(token_text, ".hide");
11620 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11622 // set the value of this setup option in the setup option structure
11623 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11625 // check if this setup option should be hidden in the setup menu
11626 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11627 setHideSetupEntry(token_info[token_nr].value);
11629 free(token_hide_text);
11632 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11633 struct TokenInfo *token_info,
11636 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11637 token_info[token_nr].text);
11640 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11644 if (!setup_file_hash)
11647 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11648 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11650 setup.touch.grid_initialized = TRUE;
11651 for (i = 0; i < 2; i++)
11653 int grid_xsize = setup.touch.grid_xsize[i];
11654 int grid_ysize = setup.touch.grid_ysize[i];
11657 // if virtual buttons are not loaded from setup file, repeat initializing
11658 // virtual buttons grid with default values later when video is initialized
11659 if (grid_xsize == -1 ||
11662 setup.touch.grid_initialized = FALSE;
11667 for (y = 0; y < grid_ysize; y++)
11669 char token_string[MAX_LINE_LEN];
11671 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11673 char *value_string = getHashEntry(setup_file_hash, token_string);
11675 if (value_string == NULL)
11678 for (x = 0; x < grid_xsize; x++)
11680 char c = value_string[x];
11682 setup.touch.grid_button[i][x][y] =
11683 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11688 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11689 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11691 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11692 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11694 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11698 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11700 setup_input = setup.input[pnr];
11701 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11703 char full_token[100];
11705 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11706 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11709 setup.input[pnr] = setup_input;
11712 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11713 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11715 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11716 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11718 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11719 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11721 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11722 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11724 setHideRelatedSetupEntries();
11727 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11731 if (!setup_file_hash)
11734 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11735 setSetupInfo(auto_setup_tokens, i,
11736 getHashEntry(setup_file_hash,
11737 auto_setup_tokens[i].text));
11740 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11744 if (!setup_file_hash)
11747 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11748 setSetupInfo(server_setup_tokens, i,
11749 getHashEntry(setup_file_hash,
11750 server_setup_tokens[i].text));
11753 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11757 if (!setup_file_hash)
11760 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11761 setSetupInfo(editor_cascade_setup_tokens, i,
11762 getHashEntry(setup_file_hash,
11763 editor_cascade_setup_tokens[i].text));
11766 void LoadUserNames(void)
11768 int last_user_nr = user.nr;
11771 if (global.user_names != NULL)
11773 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11774 checked_free(global.user_names[i]);
11776 checked_free(global.user_names);
11779 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11781 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11785 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11787 if (setup_file_hash)
11789 char *player_name = getHashEntry(setup_file_hash, "player_name");
11791 global.user_names[i] = getFixedUserName(player_name);
11793 freeSetupFileHash(setup_file_hash);
11796 if (global.user_names[i] == NULL)
11797 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11800 user.nr = last_user_nr;
11803 void LoadSetupFromFilename(char *filename)
11805 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11807 if (setup_file_hash)
11809 decodeSetupFileHash_Default(setup_file_hash);
11811 freeSetupFileHash(setup_file_hash);
11815 Debug("setup", "using default setup values");
11819 static void LoadSetup_SpecialPostProcessing(void)
11821 char *player_name_new;
11823 // needed to work around problems with fixed length strings
11824 player_name_new = getFixedUserName(setup.player_name);
11825 free(setup.player_name);
11826 setup.player_name = player_name_new;
11828 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11829 if (setup.scroll_delay == FALSE)
11831 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11832 setup.scroll_delay = TRUE; // now always "on"
11835 // make sure that scroll delay value stays inside valid range
11836 setup.scroll_delay_value =
11837 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11840 void LoadSetup_Default(void)
11844 // always start with reliable default values
11845 setSetupInfoToDefaults(&setup);
11847 // try to load setup values from default setup file
11848 filename = getDefaultSetupFilename();
11850 if (fileExists(filename))
11851 LoadSetupFromFilename(filename);
11853 // try to load setup values from platform setup file
11854 filename = getPlatformSetupFilename();
11856 if (fileExists(filename))
11857 LoadSetupFromFilename(filename);
11859 // try to load setup values from user setup file
11860 filename = getSetupFilename();
11862 LoadSetupFromFilename(filename);
11864 LoadSetup_SpecialPostProcessing();
11867 void LoadSetup_AutoSetup(void)
11869 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11870 SetupFileHash *setup_file_hash = NULL;
11872 // always start with reliable default values
11873 setSetupInfoToDefaults_AutoSetup(&setup);
11875 setup_file_hash = loadSetupFileHash(filename);
11877 if (setup_file_hash)
11879 decodeSetupFileHash_AutoSetup(setup_file_hash);
11881 freeSetupFileHash(setup_file_hash);
11887 void LoadSetup_ServerSetup(void)
11889 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11890 SetupFileHash *setup_file_hash = NULL;
11892 // always start with reliable default values
11893 setSetupInfoToDefaults_ServerSetup(&setup);
11895 setup_file_hash = loadSetupFileHash(filename);
11897 if (setup_file_hash)
11899 decodeSetupFileHash_ServerSetup(setup_file_hash);
11901 freeSetupFileHash(setup_file_hash);
11906 if (setup.player_uuid == NULL)
11908 // player UUID does not yet exist in setup file
11909 setup.player_uuid = getStringCopy(getUUID());
11910 setup.player_version = 2;
11912 SaveSetup_ServerSetup();
11916 void LoadSetup_EditorCascade(void)
11918 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11919 SetupFileHash *setup_file_hash = NULL;
11921 // always start with reliable default values
11922 setSetupInfoToDefaults_EditorCascade(&setup);
11924 setup_file_hash = loadSetupFileHash(filename);
11926 if (setup_file_hash)
11928 decodeSetupFileHash_EditorCascade(setup_file_hash);
11930 freeSetupFileHash(setup_file_hash);
11936 void LoadSetup(void)
11938 LoadSetup_Default();
11939 LoadSetup_AutoSetup();
11940 LoadSetup_ServerSetup();
11941 LoadSetup_EditorCascade();
11944 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11945 char *mapping_line)
11947 char mapping_guid[MAX_LINE_LEN];
11948 char *mapping_start, *mapping_end;
11950 // get GUID from game controller mapping line: copy complete line
11951 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11952 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11954 // get GUID from game controller mapping line: cut after GUID part
11955 mapping_start = strchr(mapping_guid, ',');
11956 if (mapping_start != NULL)
11957 *mapping_start = '\0';
11959 // cut newline from game controller mapping line
11960 mapping_end = strchr(mapping_line, '\n');
11961 if (mapping_end != NULL)
11962 *mapping_end = '\0';
11964 // add mapping entry to game controller mappings hash
11965 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11968 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11973 if (!(file = fopen(filename, MODE_READ)))
11975 Warn("cannot read game controller mappings file '%s'", filename);
11980 while (!feof(file))
11982 char line[MAX_LINE_LEN];
11984 if (!fgets(line, MAX_LINE_LEN, file))
11987 addGameControllerMappingToHash(mappings_hash, line);
11993 void SaveSetup_Default(void)
11995 char *filename = getSetupFilename();
11999 InitUserDataDirectory();
12001 if (!(file = fopen(filename, MODE_WRITE)))
12003 Warn("cannot write setup file '%s'", filename);
12008 fprintFileHeader(file, SETUP_FILENAME);
12010 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12012 // just to make things nicer :)
12013 if (global_setup_tokens[i].value == &setup.multiple_users ||
12014 global_setup_tokens[i].value == &setup.sound ||
12015 global_setup_tokens[i].value == &setup.graphics_set ||
12016 global_setup_tokens[i].value == &setup.volume_simple ||
12017 global_setup_tokens[i].value == &setup.network_mode ||
12018 global_setup_tokens[i].value == &setup.touch.control_type ||
12019 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12020 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12021 fprintf(file, "\n");
12023 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12026 for (i = 0; i < 2; i++)
12028 int grid_xsize = setup.touch.grid_xsize[i];
12029 int grid_ysize = setup.touch.grid_ysize[i];
12032 fprintf(file, "\n");
12034 for (y = 0; y < grid_ysize; y++)
12036 char token_string[MAX_LINE_LEN];
12037 char value_string[MAX_LINE_LEN];
12039 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12041 for (x = 0; x < grid_xsize; x++)
12043 char c = setup.touch.grid_button[i][x][y];
12045 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12048 value_string[grid_xsize] = '\0';
12050 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12054 fprintf(file, "\n");
12055 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12056 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12058 fprintf(file, "\n");
12059 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12060 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12062 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12066 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12067 fprintf(file, "\n");
12069 setup_input = setup.input[pnr];
12070 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12071 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12074 fprintf(file, "\n");
12075 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12076 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12078 // (internal setup values not saved to user setup file)
12080 fprintf(file, "\n");
12081 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12082 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12083 setup.debug.xsn_mode != AUTO)
12084 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12086 fprintf(file, "\n");
12087 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12088 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12092 SetFilePermissions(filename, PERMS_PRIVATE);
12095 void SaveSetup_AutoSetup(void)
12097 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12101 InitUserDataDirectory();
12103 if (!(file = fopen(filename, MODE_WRITE)))
12105 Warn("cannot write auto setup file '%s'", filename);
12112 fprintFileHeader(file, AUTOSETUP_FILENAME);
12114 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12115 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12119 SetFilePermissions(filename, PERMS_PRIVATE);
12124 void SaveSetup_ServerSetup(void)
12126 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12130 InitUserDataDirectory();
12132 if (!(file = fopen(filename, MODE_WRITE)))
12134 Warn("cannot write server setup file '%s'", filename);
12141 fprintFileHeader(file, SERVERSETUP_FILENAME);
12143 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12145 // just to make things nicer :)
12146 if (server_setup_tokens[i].value == &setup.use_api_server)
12147 fprintf(file, "\n");
12149 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12154 SetFilePermissions(filename, PERMS_PRIVATE);
12159 void SaveSetup_EditorCascade(void)
12161 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12165 InitUserDataDirectory();
12167 if (!(file = fopen(filename, MODE_WRITE)))
12169 Warn("cannot write editor cascade state file '%s'", filename);
12176 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12178 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12179 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12183 SetFilePermissions(filename, PERMS_PRIVATE);
12188 void SaveSetup(void)
12190 SaveSetup_Default();
12191 SaveSetup_AutoSetup();
12192 SaveSetup_ServerSetup();
12193 SaveSetup_EditorCascade();
12196 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12201 if (!(file = fopen(filename, MODE_WRITE)))
12203 Warn("cannot write game controller mappings file '%s'", filename);
12208 BEGIN_HASH_ITERATION(mappings_hash, itr)
12210 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12212 END_HASH_ITERATION(mappings_hash, itr)
12217 void SaveSetup_AddGameControllerMapping(char *mapping)
12219 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12220 SetupFileHash *mappings_hash = newSetupFileHash();
12222 InitUserDataDirectory();
12224 // load existing personal game controller mappings
12225 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12227 // add new mapping to personal game controller mappings
12228 addGameControllerMappingToHash(mappings_hash, mapping);
12230 // save updated personal game controller mappings
12231 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12233 freeSetupFileHash(mappings_hash);
12237 void LoadCustomElementDescriptions(void)
12239 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12240 SetupFileHash *setup_file_hash;
12243 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12245 if (element_info[i].custom_description != NULL)
12247 free(element_info[i].custom_description);
12248 element_info[i].custom_description = NULL;
12252 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12255 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12257 char *token = getStringCat2(element_info[i].token_name, ".name");
12258 char *value = getHashEntry(setup_file_hash, token);
12261 element_info[i].custom_description = getStringCopy(value);
12266 freeSetupFileHash(setup_file_hash);
12269 static int getElementFromToken(char *token)
12271 char *value = getHashEntry(element_token_hash, token);
12274 return atoi(value);
12276 Warn("unknown element token '%s'", token);
12278 return EL_UNDEFINED;
12281 void FreeGlobalAnimEventInfo(void)
12283 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12285 if (gaei->event_list == NULL)
12290 for (i = 0; i < gaei->num_event_lists; i++)
12292 checked_free(gaei->event_list[i]->event_value);
12293 checked_free(gaei->event_list[i]);
12296 checked_free(gaei->event_list);
12298 gaei->event_list = NULL;
12299 gaei->num_event_lists = 0;
12302 static int AddGlobalAnimEventList(void)
12304 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12305 int list_pos = gaei->num_event_lists++;
12307 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12308 sizeof(struct GlobalAnimEventListInfo *));
12310 gaei->event_list[list_pos] =
12311 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12313 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12315 gaeli->event_value = NULL;
12316 gaeli->num_event_values = 0;
12321 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12323 // do not add empty global animation events
12324 if (event_value == ANIM_EVENT_NONE)
12327 // if list position is undefined, create new list
12328 if (list_pos == ANIM_EVENT_UNDEFINED)
12329 list_pos = AddGlobalAnimEventList();
12331 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12332 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12333 int value_pos = gaeli->num_event_values++;
12335 gaeli->event_value = checked_realloc(gaeli->event_value,
12336 gaeli->num_event_values * sizeof(int *));
12338 gaeli->event_value[value_pos] = event_value;
12343 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12345 if (list_pos == ANIM_EVENT_UNDEFINED)
12346 return ANIM_EVENT_NONE;
12348 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12349 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12351 return gaeli->event_value[value_pos];
12354 int GetGlobalAnimEventValueCount(int list_pos)
12356 if (list_pos == ANIM_EVENT_UNDEFINED)
12359 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12360 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12362 return gaeli->num_event_values;
12365 // This function checks if a string <s> of the format "string1, string2, ..."
12366 // exactly contains a string <s_contained>.
12368 static boolean string_has_parameter(char *s, char *s_contained)
12372 if (s == NULL || s_contained == NULL)
12375 if (strlen(s_contained) > strlen(s))
12378 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12380 char next_char = s[strlen(s_contained)];
12382 // check if next character is delimiter or whitespace
12383 if (next_char == ',' || next_char == '\0' ||
12384 next_char == ' ' || next_char == '\t')
12388 // check if string contains another parameter string after a comma
12389 substring = strchr(s, ',');
12390 if (substring == NULL) // string does not contain a comma
12393 // advance string pointer to next character after the comma
12396 // skip potential whitespaces after the comma
12397 while (*substring == ' ' || *substring == '\t')
12400 return string_has_parameter(substring, s_contained);
12403 static int get_anim_parameter_value_ce(char *s)
12406 char *pattern_1 = "ce_change:custom_";
12407 char *pattern_2 = ".page_";
12408 int pattern_1_len = strlen(pattern_1);
12409 char *matching_char = strstr(s_ptr, pattern_1);
12410 int result = ANIM_EVENT_NONE;
12412 if (matching_char == NULL)
12413 return ANIM_EVENT_NONE;
12415 result = ANIM_EVENT_CE_CHANGE;
12417 s_ptr = matching_char + pattern_1_len;
12419 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12420 if (*s_ptr >= '0' && *s_ptr <= '9')
12422 int gic_ce_nr = (*s_ptr++ - '0');
12424 if (*s_ptr >= '0' && *s_ptr <= '9')
12426 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12428 if (*s_ptr >= '0' && *s_ptr <= '9')
12429 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12432 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12433 return ANIM_EVENT_NONE;
12435 // custom element stored as 0 to 255
12438 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12442 // invalid custom element number specified
12444 return ANIM_EVENT_NONE;
12447 // check for change page number ("page_X" or "page_XX") (optional)
12448 if (strPrefix(s_ptr, pattern_2))
12450 s_ptr += strlen(pattern_2);
12452 if (*s_ptr >= '0' && *s_ptr <= '9')
12454 int gic_page_nr = (*s_ptr++ - '0');
12456 if (*s_ptr >= '0' && *s_ptr <= '9')
12457 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12459 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12460 return ANIM_EVENT_NONE;
12462 // change page stored as 1 to 32 (0 means "all change pages")
12464 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12468 // invalid animation part number specified
12470 return ANIM_EVENT_NONE;
12474 // discard result if next character is neither delimiter nor whitespace
12475 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12476 *s_ptr == ' ' || *s_ptr == '\t'))
12477 return ANIM_EVENT_NONE;
12482 static int get_anim_parameter_value(char *s)
12484 int event_value[] =
12492 char *pattern_1[] =
12500 char *pattern_2 = ".part_";
12501 char *matching_char = NULL;
12503 int pattern_1_len = 0;
12504 int result = ANIM_EVENT_NONE;
12507 result = get_anim_parameter_value_ce(s);
12509 if (result != ANIM_EVENT_NONE)
12512 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12514 matching_char = strstr(s_ptr, pattern_1[i]);
12515 pattern_1_len = strlen(pattern_1[i]);
12516 result = event_value[i];
12518 if (matching_char != NULL)
12522 if (matching_char == NULL)
12523 return ANIM_EVENT_NONE;
12525 s_ptr = matching_char + pattern_1_len;
12527 // check for main animation number ("anim_X" or "anim_XX")
12528 if (*s_ptr >= '0' && *s_ptr <= '9')
12530 int gic_anim_nr = (*s_ptr++ - '0');
12532 if (*s_ptr >= '0' && *s_ptr <= '9')
12533 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12535 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12536 return ANIM_EVENT_NONE;
12538 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12542 // invalid main animation number specified
12544 return ANIM_EVENT_NONE;
12547 // check for animation part number ("part_X" or "part_XX") (optional)
12548 if (strPrefix(s_ptr, pattern_2))
12550 s_ptr += strlen(pattern_2);
12552 if (*s_ptr >= '0' && *s_ptr <= '9')
12554 int gic_part_nr = (*s_ptr++ - '0');
12556 if (*s_ptr >= '0' && *s_ptr <= '9')
12557 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12559 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12560 return ANIM_EVENT_NONE;
12562 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12566 // invalid animation part number specified
12568 return ANIM_EVENT_NONE;
12572 // discard result if next character is neither delimiter nor whitespace
12573 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12574 *s_ptr == ' ' || *s_ptr == '\t'))
12575 return ANIM_EVENT_NONE;
12580 static int get_anim_parameter_values(char *s)
12582 int list_pos = ANIM_EVENT_UNDEFINED;
12583 int event_value = ANIM_EVENT_DEFAULT;
12585 if (string_has_parameter(s, "any"))
12586 event_value |= ANIM_EVENT_ANY;
12588 if (string_has_parameter(s, "click:self") ||
12589 string_has_parameter(s, "click") ||
12590 string_has_parameter(s, "self"))
12591 event_value |= ANIM_EVENT_SELF;
12593 if (string_has_parameter(s, "unclick:any"))
12594 event_value |= ANIM_EVENT_UNCLICK_ANY;
12596 // if animation event found, add it to global animation event list
12597 if (event_value != ANIM_EVENT_NONE)
12598 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12602 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12603 event_value = get_anim_parameter_value(s);
12605 // if animation event found, add it to global animation event list
12606 if (event_value != ANIM_EVENT_NONE)
12607 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12609 // continue with next part of the string, starting with next comma
12610 s = strchr(s + 1, ',');
12616 static int get_anim_action_parameter_value(char *token)
12618 // check most common default case first to massively speed things up
12619 if (strEqual(token, ARG_UNDEFINED))
12620 return ANIM_EVENT_ACTION_NONE;
12622 int result = getImageIDFromToken(token);
12626 char *gfx_token = getStringCat2("gfx.", token);
12628 result = getImageIDFromToken(gfx_token);
12630 checked_free(gfx_token);
12635 Key key = getKeyFromX11KeyName(token);
12637 if (key != KSYM_UNDEFINED)
12638 result = -(int)key;
12645 result = get_hash_from_string(token); // unsigned int => int
12646 result = ABS(result); // may be negative now
12647 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12649 setHashEntry(anim_url_hash, int2str(result, 0), token);
12654 result = ANIM_EVENT_ACTION_NONE;
12659 int get_parameter_value(char *value_raw, char *suffix, int type)
12661 char *value = getStringToLower(value_raw);
12662 int result = 0; // probably a save default value
12664 if (strEqual(suffix, ".direction"))
12666 result = (strEqual(value, "left") ? MV_LEFT :
12667 strEqual(value, "right") ? MV_RIGHT :
12668 strEqual(value, "up") ? MV_UP :
12669 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12671 else if (strEqual(suffix, ".position"))
12673 result = (strEqual(value, "left") ? POS_LEFT :
12674 strEqual(value, "right") ? POS_RIGHT :
12675 strEqual(value, "top") ? POS_TOP :
12676 strEqual(value, "upper") ? POS_UPPER :
12677 strEqual(value, "middle") ? POS_MIDDLE :
12678 strEqual(value, "lower") ? POS_LOWER :
12679 strEqual(value, "bottom") ? POS_BOTTOM :
12680 strEqual(value, "any") ? POS_ANY :
12681 strEqual(value, "ce") ? POS_CE :
12682 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12683 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12685 else if (strEqual(suffix, ".align"))
12687 result = (strEqual(value, "left") ? ALIGN_LEFT :
12688 strEqual(value, "right") ? ALIGN_RIGHT :
12689 strEqual(value, "center") ? ALIGN_CENTER :
12690 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12692 else if (strEqual(suffix, ".valign"))
12694 result = (strEqual(value, "top") ? VALIGN_TOP :
12695 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12696 strEqual(value, "middle") ? VALIGN_MIDDLE :
12697 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12699 else if (strEqual(suffix, ".anim_mode"))
12701 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12702 string_has_parameter(value, "loop") ? ANIM_LOOP :
12703 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12704 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12705 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12706 string_has_parameter(value, "random") ? ANIM_RANDOM :
12707 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12708 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12709 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12710 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12711 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12712 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12713 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12714 string_has_parameter(value, "all") ? ANIM_ALL :
12715 string_has_parameter(value, "tiled") ? ANIM_TILED :
12716 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12719 if (string_has_parameter(value, "once"))
12720 result |= ANIM_ONCE;
12722 if (string_has_parameter(value, "reverse"))
12723 result |= ANIM_REVERSE;
12725 if (string_has_parameter(value, "opaque_player"))
12726 result |= ANIM_OPAQUE_PLAYER;
12728 if (string_has_parameter(value, "static_panel"))
12729 result |= ANIM_STATIC_PANEL;
12731 else if (strEqual(suffix, ".init_event") ||
12732 strEqual(suffix, ".anim_event"))
12734 result = get_anim_parameter_values(value);
12736 else if (strEqual(suffix, ".init_delay_action") ||
12737 strEqual(suffix, ".anim_delay_action") ||
12738 strEqual(suffix, ".post_delay_action") ||
12739 strEqual(suffix, ".init_event_action") ||
12740 strEqual(suffix, ".anim_event_action"))
12742 result = get_anim_action_parameter_value(value_raw);
12744 else if (strEqual(suffix, ".class"))
12746 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12747 get_hash_from_string(value));
12749 else if (strEqual(suffix, ".style"))
12751 result = STYLE_DEFAULT;
12753 if (string_has_parameter(value, "accurate_borders"))
12754 result |= STYLE_ACCURATE_BORDERS;
12756 if (string_has_parameter(value, "inner_corners"))
12757 result |= STYLE_INNER_CORNERS;
12759 if (string_has_parameter(value, "reverse"))
12760 result |= STYLE_REVERSE;
12762 if (string_has_parameter(value, "leftmost_position"))
12763 result |= STYLE_LEFTMOST_POSITION;
12765 if (string_has_parameter(value, "block_clicks"))
12766 result |= STYLE_BLOCK;
12768 if (string_has_parameter(value, "passthrough_clicks"))
12769 result |= STYLE_PASSTHROUGH;
12771 if (string_has_parameter(value, "multiple_actions"))
12772 result |= STYLE_MULTIPLE_ACTIONS;
12774 if (string_has_parameter(value, "consume_ce_event"))
12775 result |= STYLE_CONSUME_CE_EVENT;
12777 else if (strEqual(suffix, ".fade_mode"))
12779 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12780 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12781 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12782 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12783 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12784 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12785 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12786 FADE_MODE_DEFAULT);
12788 else if (strEqual(suffix, ".auto_delay_unit"))
12790 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12791 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12792 AUTO_DELAY_UNIT_DEFAULT);
12794 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12796 result = gfx.get_font_from_token_function(value);
12798 else // generic parameter of type integer or boolean
12800 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12801 type == TYPE_INTEGER ? get_integer_from_string(value) :
12802 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12803 ARG_UNDEFINED_VALUE);
12811 static int get_token_parameter_value(char *token, char *value_raw)
12815 if (token == NULL || value_raw == NULL)
12816 return ARG_UNDEFINED_VALUE;
12818 suffix = strrchr(token, '.');
12819 if (suffix == NULL)
12822 if (strEqual(suffix, ".element"))
12823 return getElementFromToken(value_raw);
12825 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12826 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12829 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12830 boolean ignore_defaults)
12834 for (i = 0; image_config_vars[i].token != NULL; i++)
12836 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12838 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12839 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12843 *image_config_vars[i].value =
12844 get_token_parameter_value(image_config_vars[i].token, value);
12848 void InitMenuDesignSettings_Static(void)
12850 // always start with reliable default values from static default config
12851 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12854 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12858 // the following initializes hierarchical values from static configuration
12860 // special case: initialize "ARG_DEFAULT" values in static default config
12861 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12862 titlescreen_initial_first_default.fade_mode =
12863 title_initial_first_default.fade_mode;
12864 titlescreen_initial_first_default.fade_delay =
12865 title_initial_first_default.fade_delay;
12866 titlescreen_initial_first_default.post_delay =
12867 title_initial_first_default.post_delay;
12868 titlescreen_initial_first_default.auto_delay =
12869 title_initial_first_default.auto_delay;
12870 titlescreen_initial_first_default.auto_delay_unit =
12871 title_initial_first_default.auto_delay_unit;
12872 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12873 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12874 titlescreen_first_default.post_delay = title_first_default.post_delay;
12875 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12876 titlescreen_first_default.auto_delay_unit =
12877 title_first_default.auto_delay_unit;
12878 titlemessage_initial_first_default.fade_mode =
12879 title_initial_first_default.fade_mode;
12880 titlemessage_initial_first_default.fade_delay =
12881 title_initial_first_default.fade_delay;
12882 titlemessage_initial_first_default.post_delay =
12883 title_initial_first_default.post_delay;
12884 titlemessage_initial_first_default.auto_delay =
12885 title_initial_first_default.auto_delay;
12886 titlemessage_initial_first_default.auto_delay_unit =
12887 title_initial_first_default.auto_delay_unit;
12888 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12889 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12890 titlemessage_first_default.post_delay = title_first_default.post_delay;
12891 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12892 titlemessage_first_default.auto_delay_unit =
12893 title_first_default.auto_delay_unit;
12895 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12896 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12897 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12898 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12899 titlescreen_initial_default.auto_delay_unit =
12900 title_initial_default.auto_delay_unit;
12901 titlescreen_default.fade_mode = title_default.fade_mode;
12902 titlescreen_default.fade_delay = title_default.fade_delay;
12903 titlescreen_default.post_delay = title_default.post_delay;
12904 titlescreen_default.auto_delay = title_default.auto_delay;
12905 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12906 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12907 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12908 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12909 titlemessage_initial_default.auto_delay_unit =
12910 title_initial_default.auto_delay_unit;
12911 titlemessage_default.fade_mode = title_default.fade_mode;
12912 titlemessage_default.fade_delay = title_default.fade_delay;
12913 titlemessage_default.post_delay = title_default.post_delay;
12914 titlemessage_default.auto_delay = title_default.auto_delay;
12915 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12917 // special case: initialize "ARG_DEFAULT" values in static default config
12918 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12919 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12921 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12922 titlescreen_first[i] = titlescreen_first_default;
12923 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12924 titlemessage_first[i] = titlemessage_first_default;
12926 titlescreen_initial[i] = titlescreen_initial_default;
12927 titlescreen[i] = titlescreen_default;
12928 titlemessage_initial[i] = titlemessage_initial_default;
12929 titlemessage[i] = titlemessage_default;
12932 // special case: initialize "ARG_DEFAULT" values in static default config
12933 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12934 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12936 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12939 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12940 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12941 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12944 // special case: initialize "ARG_DEFAULT" values in static default config
12945 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12946 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12948 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12949 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12950 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12952 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12955 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12959 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12963 struct XY *dst, *src;
12965 game_buttons_xy[] =
12967 { &game.button.save, &game.button.stop },
12968 { &game.button.pause2, &game.button.pause },
12969 { &game.button.load, &game.button.play },
12970 { &game.button.undo, &game.button.stop },
12971 { &game.button.redo, &game.button.play },
12977 // special case: initialize later added SETUP list size from LEVELS value
12978 if (menu.list_size[GAME_MODE_SETUP] == -1)
12979 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12981 // set default position for snapshot buttons to stop/pause/play buttons
12982 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12983 if ((*game_buttons_xy[i].dst).x == -1 &&
12984 (*game_buttons_xy[i].dst).y == -1)
12985 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12987 // --------------------------------------------------------------------------
12988 // dynamic viewports (including playfield margins, borders and alignments)
12989 // --------------------------------------------------------------------------
12991 // dynamic viewports currently only supported for landscape mode
12992 int display_width = MAX(video.display_width, video.display_height);
12993 int display_height = MIN(video.display_width, video.display_height);
12995 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12997 struct RectWithBorder *vp_window = &viewport.window[i];
12998 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12999 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13000 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13001 boolean dynamic_window_width = (vp_window->min_width != -1);
13002 boolean dynamic_window_height = (vp_window->min_height != -1);
13003 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13004 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13006 // adjust window size if min/max width/height is specified
13008 if (vp_window->min_width != -1)
13010 int window_width = display_width;
13012 // when using static window height, use aspect ratio of display
13013 if (vp_window->min_height == -1)
13014 window_width = vp_window->height * display_width / display_height;
13016 vp_window->width = MAX(vp_window->min_width, window_width);
13019 if (vp_window->min_height != -1)
13021 int window_height = display_height;
13023 // when using static window width, use aspect ratio of display
13024 if (vp_window->min_width == -1)
13025 window_height = vp_window->width * display_height / display_width;
13027 vp_window->height = MAX(vp_window->min_height, window_height);
13030 if (vp_window->max_width != -1)
13031 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13033 if (vp_window->max_height != -1)
13034 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13036 int playfield_width = vp_window->width;
13037 int playfield_height = vp_window->height;
13039 // adjust playfield size and position according to specified margins
13041 playfield_width -= vp_playfield->margin_left;
13042 playfield_width -= vp_playfield->margin_right;
13044 playfield_height -= vp_playfield->margin_top;
13045 playfield_height -= vp_playfield->margin_bottom;
13047 // adjust playfield size if min/max width/height is specified
13049 if (vp_playfield->min_width != -1)
13050 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13052 if (vp_playfield->min_height != -1)
13053 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13055 if (vp_playfield->max_width != -1)
13056 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13058 if (vp_playfield->max_height != -1)
13059 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13061 // adjust playfield position according to specified alignment
13063 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13064 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13065 else if (vp_playfield->align == ALIGN_CENTER)
13066 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13067 else if (vp_playfield->align == ALIGN_RIGHT)
13068 vp_playfield->x += playfield_width - vp_playfield->width;
13070 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13071 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13072 else if (vp_playfield->valign == VALIGN_MIDDLE)
13073 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13074 else if (vp_playfield->valign == VALIGN_BOTTOM)
13075 vp_playfield->y += playfield_height - vp_playfield->height;
13077 vp_playfield->x += vp_playfield->margin_left;
13078 vp_playfield->y += vp_playfield->margin_top;
13080 // adjust individual playfield borders if only default border is specified
13082 if (vp_playfield->border_left == -1)
13083 vp_playfield->border_left = vp_playfield->border_size;
13084 if (vp_playfield->border_right == -1)
13085 vp_playfield->border_right = vp_playfield->border_size;
13086 if (vp_playfield->border_top == -1)
13087 vp_playfield->border_top = vp_playfield->border_size;
13088 if (vp_playfield->border_bottom == -1)
13089 vp_playfield->border_bottom = vp_playfield->border_size;
13091 // set dynamic playfield borders if borders are specified as undefined
13092 // (but only if window size was dynamic and playfield size was static)
13094 if (dynamic_window_width && !dynamic_playfield_width)
13096 if (vp_playfield->border_left == -1)
13098 vp_playfield->border_left = (vp_playfield->x -
13099 vp_playfield->margin_left);
13100 vp_playfield->x -= vp_playfield->border_left;
13101 vp_playfield->width += vp_playfield->border_left;
13104 if (vp_playfield->border_right == -1)
13106 vp_playfield->border_right = (vp_window->width -
13108 vp_playfield->width -
13109 vp_playfield->margin_right);
13110 vp_playfield->width += vp_playfield->border_right;
13114 if (dynamic_window_height && !dynamic_playfield_height)
13116 if (vp_playfield->border_top == -1)
13118 vp_playfield->border_top = (vp_playfield->y -
13119 vp_playfield->margin_top);
13120 vp_playfield->y -= vp_playfield->border_top;
13121 vp_playfield->height += vp_playfield->border_top;
13124 if (vp_playfield->border_bottom == -1)
13126 vp_playfield->border_bottom = (vp_window->height -
13128 vp_playfield->height -
13129 vp_playfield->margin_bottom);
13130 vp_playfield->height += vp_playfield->border_bottom;
13134 // adjust playfield size to be a multiple of a defined alignment tile size
13136 int align_size = vp_playfield->align_size;
13137 int playfield_xtiles = vp_playfield->width / align_size;
13138 int playfield_ytiles = vp_playfield->height / align_size;
13139 int playfield_width_corrected = playfield_xtiles * align_size;
13140 int playfield_height_corrected = playfield_ytiles * align_size;
13141 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13142 i == GFX_SPECIAL_ARG_EDITOR);
13144 if (is_playfield_mode &&
13145 dynamic_playfield_width &&
13146 vp_playfield->width != playfield_width_corrected)
13148 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13150 vp_playfield->width = playfield_width_corrected;
13152 if (vp_playfield->align == ALIGN_LEFT)
13154 vp_playfield->border_left += playfield_xdiff;
13156 else if (vp_playfield->align == ALIGN_RIGHT)
13158 vp_playfield->border_right += playfield_xdiff;
13160 else if (vp_playfield->align == ALIGN_CENTER)
13162 int border_left_diff = playfield_xdiff / 2;
13163 int border_right_diff = playfield_xdiff - border_left_diff;
13165 vp_playfield->border_left += border_left_diff;
13166 vp_playfield->border_right += border_right_diff;
13170 if (is_playfield_mode &&
13171 dynamic_playfield_height &&
13172 vp_playfield->height != playfield_height_corrected)
13174 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13176 vp_playfield->height = playfield_height_corrected;
13178 if (vp_playfield->valign == VALIGN_TOP)
13180 vp_playfield->border_top += playfield_ydiff;
13182 else if (vp_playfield->align == VALIGN_BOTTOM)
13184 vp_playfield->border_right += playfield_ydiff;
13186 else if (vp_playfield->align == VALIGN_MIDDLE)
13188 int border_top_diff = playfield_ydiff / 2;
13189 int border_bottom_diff = playfield_ydiff - border_top_diff;
13191 vp_playfield->border_top += border_top_diff;
13192 vp_playfield->border_bottom += border_bottom_diff;
13196 // adjust door positions according to specified alignment
13198 for (j = 0; j < 2; j++)
13200 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13202 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13203 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13204 else if (vp_door->align == ALIGN_CENTER)
13205 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13206 else if (vp_door->align == ALIGN_RIGHT)
13207 vp_door->x += vp_window->width - vp_door->width;
13209 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13210 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13211 else if (vp_door->valign == VALIGN_MIDDLE)
13212 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13213 else if (vp_door->valign == VALIGN_BOTTOM)
13214 vp_door->y += vp_window->height - vp_door->height;
13219 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13223 struct XYTileSize *dst, *src;
13226 editor_buttons_xy[] =
13229 &editor.button.element_left, &editor.palette.element_left,
13230 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13233 &editor.button.element_middle, &editor.palette.element_middle,
13234 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13237 &editor.button.element_right, &editor.palette.element_right,
13238 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13245 // set default position for element buttons to element graphics
13246 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13248 if ((*editor_buttons_xy[i].dst).x == -1 &&
13249 (*editor_buttons_xy[i].dst).y == -1)
13251 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13253 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13255 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13259 // adjust editor palette rows and columns if specified to be dynamic
13261 if (editor.palette.cols == -1)
13263 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13264 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13265 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13267 editor.palette.cols = (vp_width - sc_width) / bt_width;
13269 if (editor.palette.x == -1)
13271 int palette_width = editor.palette.cols * bt_width + sc_width;
13273 editor.palette.x = (vp_width - palette_width) / 2;
13277 if (editor.palette.rows == -1)
13279 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13280 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13281 int tx_height = getFontHeight(FONT_TEXT_2);
13283 editor.palette.rows = (vp_height - tx_height) / bt_height;
13285 if (editor.palette.y == -1)
13287 int palette_height = editor.palette.rows * bt_height + tx_height;
13289 editor.palette.y = (vp_height - palette_height) / 2;
13294 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13295 boolean initialize)
13297 // special case: check if network and preview player positions are redefined,
13298 // to compare this later against the main menu level preview being redefined
13299 struct TokenIntPtrInfo menu_config_players[] =
13301 { "main.network_players.x", &menu.main.network_players.redefined },
13302 { "main.network_players.y", &menu.main.network_players.redefined },
13303 { "main.preview_players.x", &menu.main.preview_players.redefined },
13304 { "main.preview_players.y", &menu.main.preview_players.redefined },
13305 { "preview.x", &preview.redefined },
13306 { "preview.y", &preview.redefined }
13312 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13313 *menu_config_players[i].value = FALSE;
13317 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13318 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13319 *menu_config_players[i].value = TRUE;
13323 static void InitMenuDesignSettings_PreviewPlayers(void)
13325 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13328 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13330 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13333 static void LoadMenuDesignSettingsFromFilename(char *filename)
13335 static struct TitleFadingInfo tfi;
13336 static struct TitleMessageInfo tmi;
13337 static struct TokenInfo title_tokens[] =
13339 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13340 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13341 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13342 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13343 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13347 static struct TokenInfo titlemessage_tokens[] =
13349 { TYPE_INTEGER, &tmi.x, ".x" },
13350 { TYPE_INTEGER, &tmi.y, ".y" },
13351 { TYPE_INTEGER, &tmi.width, ".width" },
13352 { TYPE_INTEGER, &tmi.height, ".height" },
13353 { TYPE_INTEGER, &tmi.chars, ".chars" },
13354 { TYPE_INTEGER, &tmi.lines, ".lines" },
13355 { TYPE_INTEGER, &tmi.align, ".align" },
13356 { TYPE_INTEGER, &tmi.valign, ".valign" },
13357 { TYPE_INTEGER, &tmi.font, ".font" },
13358 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13359 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13360 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13361 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13362 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13363 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13364 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13365 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13366 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13372 struct TitleFadingInfo *info;
13377 // initialize first titles from "enter screen" definitions, if defined
13378 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13379 { &title_first_default, "menu.enter_screen.TITLE" },
13381 // initialize title screens from "next screen" definitions, if defined
13382 { &title_initial_default, "menu.next_screen.TITLE" },
13383 { &title_default, "menu.next_screen.TITLE" },
13389 struct TitleMessageInfo *array;
13392 titlemessage_arrays[] =
13394 // initialize first titles from "enter screen" definitions, if defined
13395 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13396 { titlescreen_first, "menu.enter_screen.TITLE" },
13397 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13398 { titlemessage_first, "menu.enter_screen.TITLE" },
13400 // initialize titles from "next screen" definitions, if defined
13401 { titlescreen_initial, "menu.next_screen.TITLE" },
13402 { titlescreen, "menu.next_screen.TITLE" },
13403 { titlemessage_initial, "menu.next_screen.TITLE" },
13404 { titlemessage, "menu.next_screen.TITLE" },
13406 // overwrite titles with title definitions, if defined
13407 { titlescreen_initial_first, "[title_initial]" },
13408 { titlescreen_first, "[title]" },
13409 { titlemessage_initial_first, "[title_initial]" },
13410 { titlemessage_first, "[title]" },
13412 { titlescreen_initial, "[title_initial]" },
13413 { titlescreen, "[title]" },
13414 { titlemessage_initial, "[title_initial]" },
13415 { titlemessage, "[title]" },
13417 // overwrite titles with title screen/message definitions, if defined
13418 { titlescreen_initial_first, "[titlescreen_initial]" },
13419 { titlescreen_first, "[titlescreen]" },
13420 { titlemessage_initial_first, "[titlemessage_initial]" },
13421 { titlemessage_first, "[titlemessage]" },
13423 { titlescreen_initial, "[titlescreen_initial]" },
13424 { titlescreen, "[titlescreen]" },
13425 { titlemessage_initial, "[titlemessage_initial]" },
13426 { titlemessage, "[titlemessage]" },
13430 SetupFileHash *setup_file_hash;
13433 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13436 // the following initializes hierarchical values from dynamic configuration
13438 // special case: initialize with default values that may be overwritten
13439 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13440 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13442 struct TokenIntPtrInfo menu_config[] =
13444 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13445 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13446 { "menu.list_size", &menu.list_size[i] }
13449 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13451 char *token = menu_config[j].token;
13452 char *value = getHashEntry(setup_file_hash, token);
13455 *menu_config[j].value = get_integer_from_string(value);
13459 // special case: initialize with default values that may be overwritten
13460 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13461 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13463 struct TokenIntPtrInfo menu_config[] =
13465 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13466 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13467 { "menu.list_size.INFO", &menu.list_size_info[i] },
13468 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13469 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13472 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13474 char *token = menu_config[j].token;
13475 char *value = getHashEntry(setup_file_hash, token);
13478 *menu_config[j].value = get_integer_from_string(value);
13482 // special case: initialize with default values that may be overwritten
13483 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13484 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13486 struct TokenIntPtrInfo menu_config[] =
13488 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13489 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13492 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13494 char *token = menu_config[j].token;
13495 char *value = getHashEntry(setup_file_hash, token);
13498 *menu_config[j].value = get_integer_from_string(value);
13502 // special case: initialize with default values that may be overwritten
13503 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13504 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13506 struct TokenIntPtrInfo menu_config[] =
13508 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13509 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13510 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13511 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13512 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13513 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13514 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13515 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13516 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13517 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13520 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13522 char *token = menu_config[j].token;
13523 char *value = getHashEntry(setup_file_hash, token);
13526 *menu_config[j].value = get_integer_from_string(value);
13530 // special case: initialize with default values that may be overwritten
13531 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13532 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13534 struct TokenIntPtrInfo menu_config[] =
13536 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13537 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13538 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13539 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13540 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13541 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13542 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13543 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13544 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13547 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13549 char *token = menu_config[j].token;
13550 char *value = getHashEntry(setup_file_hash, token);
13553 *menu_config[j].value = get_token_parameter_value(token, value);
13557 // special case: initialize with default values that may be overwritten
13558 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13559 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13563 char *token_prefix;
13564 struct RectWithBorder *struct_ptr;
13568 { "viewport.window", &viewport.window[i] },
13569 { "viewport.playfield", &viewport.playfield[i] },
13570 { "viewport.door_1", &viewport.door_1[i] },
13571 { "viewport.door_2", &viewport.door_2[i] }
13574 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13576 struct TokenIntPtrInfo vp_config[] =
13578 { ".x", &vp_struct[j].struct_ptr->x },
13579 { ".y", &vp_struct[j].struct_ptr->y },
13580 { ".width", &vp_struct[j].struct_ptr->width },
13581 { ".height", &vp_struct[j].struct_ptr->height },
13582 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13583 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13584 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13585 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13586 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13587 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13588 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13589 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13590 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13591 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13592 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13593 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13594 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13595 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13596 { ".align", &vp_struct[j].struct_ptr->align },
13597 { ".valign", &vp_struct[j].struct_ptr->valign }
13600 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13602 char *token = getStringCat2(vp_struct[j].token_prefix,
13603 vp_config[k].token);
13604 char *value = getHashEntry(setup_file_hash, token);
13607 *vp_config[k].value = get_token_parameter_value(token, value);
13614 // special case: initialize with default values that may be overwritten
13615 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13616 for (i = 0; title_info[i].info != NULL; i++)
13618 struct TitleFadingInfo *info = title_info[i].info;
13619 char *base_token = title_info[i].text;
13621 for (j = 0; title_tokens[j].type != -1; j++)
13623 char *token = getStringCat2(base_token, title_tokens[j].text);
13624 char *value = getHashEntry(setup_file_hash, token);
13628 int parameter_value = get_token_parameter_value(token, value);
13632 *(int *)title_tokens[j].value = (int)parameter_value;
13641 // special case: initialize with default values that may be overwritten
13642 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13643 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13645 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13646 char *base_token = titlemessage_arrays[i].text;
13648 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13650 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13651 char *value = getHashEntry(setup_file_hash, token);
13655 int parameter_value = get_token_parameter_value(token, value);
13657 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13661 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13662 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13664 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13674 // read (and overwrite with) values that may be specified in config file
13675 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13677 // special case: check if network and preview player positions are redefined
13678 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13680 freeSetupFileHash(setup_file_hash);
13683 void LoadMenuDesignSettings(void)
13685 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13687 InitMenuDesignSettings_Static();
13688 InitMenuDesignSettings_SpecialPreProcessing();
13689 InitMenuDesignSettings_PreviewPlayers();
13691 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13693 // first look for special settings configured in level series config
13694 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13696 if (fileExists(filename_base))
13697 LoadMenuDesignSettingsFromFilename(filename_base);
13700 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13702 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13703 LoadMenuDesignSettingsFromFilename(filename_local);
13705 InitMenuDesignSettings_SpecialPostProcessing();
13708 void LoadMenuDesignSettings_AfterGraphics(void)
13710 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13713 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13714 boolean ignore_defaults)
13718 for (i = 0; sound_config_vars[i].token != NULL; i++)
13720 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13722 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13723 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13727 *sound_config_vars[i].value =
13728 get_token_parameter_value(sound_config_vars[i].token, value);
13732 void InitSoundSettings_Static(void)
13734 // always start with reliable default values from static default config
13735 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13738 static void LoadSoundSettingsFromFilename(char *filename)
13740 SetupFileHash *setup_file_hash;
13742 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13745 // read (and overwrite with) values that may be specified in config file
13746 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13748 freeSetupFileHash(setup_file_hash);
13751 void LoadSoundSettings(void)
13753 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13755 InitSoundSettings_Static();
13757 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13759 // first look for special settings configured in level series config
13760 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13762 if (fileExists(filename_base))
13763 LoadSoundSettingsFromFilename(filename_base);
13766 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13768 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13769 LoadSoundSettingsFromFilename(filename_local);
13772 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13774 char *filename = getEditorSetupFilename();
13775 SetupFileList *setup_file_list, *list;
13776 SetupFileHash *element_hash;
13777 int num_unknown_tokens = 0;
13780 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13783 element_hash = newSetupFileHash();
13785 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13786 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13788 // determined size may be larger than needed (due to unknown elements)
13790 for (list = setup_file_list; list != NULL; list = list->next)
13793 // add space for up to 3 more elements for padding that may be needed
13794 *num_elements += 3;
13796 // free memory for old list of elements, if needed
13797 checked_free(*elements);
13799 // allocate memory for new list of elements
13800 *elements = checked_malloc(*num_elements * sizeof(int));
13803 for (list = setup_file_list; list != NULL; list = list->next)
13805 char *value = getHashEntry(element_hash, list->token);
13807 if (value == NULL) // try to find obsolete token mapping
13809 char *mapped_token = get_mapped_token(list->token);
13811 if (mapped_token != NULL)
13813 value = getHashEntry(element_hash, mapped_token);
13815 free(mapped_token);
13821 (*elements)[(*num_elements)++] = atoi(value);
13825 if (num_unknown_tokens == 0)
13828 Warn("unknown token(s) found in config file:");
13829 Warn("- config file: '%s'", filename);
13831 num_unknown_tokens++;
13834 Warn("- token: '%s'", list->token);
13838 if (num_unknown_tokens > 0)
13841 while (*num_elements % 4) // pad with empty elements, if needed
13842 (*elements)[(*num_elements)++] = EL_EMPTY;
13844 freeSetupFileList(setup_file_list);
13845 freeSetupFileHash(element_hash);
13848 for (i = 0; i < *num_elements; i++)
13849 Debug("editor", "element '%s' [%d]\n",
13850 element_info[(*elements)[i]].token_name, (*elements)[i]);
13854 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13857 SetupFileHash *setup_file_hash = NULL;
13858 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13859 char *filename_music, *filename_prefix, *filename_info;
13865 token_to_value_ptr[] =
13867 { "title_header", &tmp_music_file_info.title_header },
13868 { "artist_header", &tmp_music_file_info.artist_header },
13869 { "album_header", &tmp_music_file_info.album_header },
13870 { "year_header", &tmp_music_file_info.year_header },
13871 { "played_header", &tmp_music_file_info.played_header },
13873 { "title", &tmp_music_file_info.title },
13874 { "artist", &tmp_music_file_info.artist },
13875 { "album", &tmp_music_file_info.album },
13876 { "year", &tmp_music_file_info.year },
13877 { "played", &tmp_music_file_info.played },
13883 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13884 getCustomMusicFilename(basename));
13886 if (filename_music == NULL)
13889 // ---------- try to replace file extension ----------
13891 filename_prefix = getStringCopy(filename_music);
13892 if (strrchr(filename_prefix, '.') != NULL)
13893 *strrchr(filename_prefix, '.') = '\0';
13894 filename_info = getStringCat2(filename_prefix, ".txt");
13896 if (fileExists(filename_info))
13897 setup_file_hash = loadSetupFileHash(filename_info);
13899 free(filename_prefix);
13900 free(filename_info);
13902 if (setup_file_hash == NULL)
13904 // ---------- try to add file extension ----------
13906 filename_prefix = getStringCopy(filename_music);
13907 filename_info = getStringCat2(filename_prefix, ".txt");
13909 if (fileExists(filename_info))
13910 setup_file_hash = loadSetupFileHash(filename_info);
13912 free(filename_prefix);
13913 free(filename_info);
13916 if (setup_file_hash == NULL)
13919 // ---------- music file info found ----------
13921 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13923 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13925 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13927 *token_to_value_ptr[i].value_ptr =
13928 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13931 tmp_music_file_info.basename = getStringCopy(basename);
13932 tmp_music_file_info.music = music;
13933 tmp_music_file_info.is_sound = is_sound;
13935 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13936 *new_music_file_info = tmp_music_file_info;
13938 return new_music_file_info;
13941 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13943 return get_music_file_info_ext(basename, music, FALSE);
13946 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13948 return get_music_file_info_ext(basename, sound, TRUE);
13951 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13952 char *basename, boolean is_sound)
13954 for (; list != NULL; list = list->next)
13955 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13961 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13963 return music_info_listed_ext(list, basename, FALSE);
13966 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13968 return music_info_listed_ext(list, basename, TRUE);
13971 void LoadMusicInfo(void)
13973 int num_music_noconf = getMusicListSize_NoConf();
13974 int num_music = getMusicListSize();
13975 int num_sounds = getSoundListSize();
13976 struct FileInfo *music, *sound;
13977 struct MusicFileInfo *next, **new;
13981 while (music_file_info != NULL)
13983 next = music_file_info->next;
13985 checked_free(music_file_info->basename);
13987 checked_free(music_file_info->title_header);
13988 checked_free(music_file_info->artist_header);
13989 checked_free(music_file_info->album_header);
13990 checked_free(music_file_info->year_header);
13991 checked_free(music_file_info->played_header);
13993 checked_free(music_file_info->title);
13994 checked_free(music_file_info->artist);
13995 checked_free(music_file_info->album);
13996 checked_free(music_file_info->year);
13997 checked_free(music_file_info->played);
13999 free(music_file_info);
14001 music_file_info = next;
14004 new = &music_file_info;
14006 // get (configured or unconfigured) music file info for all levels
14007 for (i = leveldir_current->first_level;
14008 i <= leveldir_current->last_level; i++)
14012 if (levelset.music[i] != MUS_UNDEFINED)
14014 // get music file info for configured level music
14015 music_nr = levelset.music[i];
14017 else if (num_music_noconf > 0)
14019 // get music file info for unconfigured level music
14020 int level_pos = i - leveldir_current->first_level;
14022 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14029 char *basename = getMusicInfoEntryFilename(music_nr);
14031 if (basename == NULL)
14034 if (!music_info_listed(music_file_info, basename))
14036 *new = get_music_file_info(basename, music_nr);
14039 new = &(*new)->next;
14043 // get music file info for all remaining configured music files
14044 for (i = 0; i < num_music; i++)
14046 music = getMusicListEntry(i);
14048 if (music->filename == NULL)
14051 if (strEqual(music->filename, UNDEFINED_FILENAME))
14054 // a configured file may be not recognized as music
14055 if (!FileIsMusic(music->filename))
14058 if (!music_info_listed(music_file_info, music->filename))
14060 *new = get_music_file_info(music->filename, i);
14063 new = &(*new)->next;
14067 // get sound file info for all configured sound files
14068 for (i = 0; i < num_sounds; i++)
14070 sound = getSoundListEntry(i);
14072 if (sound->filename == NULL)
14075 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14078 // a configured file may be not recognized as sound
14079 if (!FileIsSound(sound->filename))
14082 if (!sound_info_listed(music_file_info, sound->filename))
14084 *new = get_sound_file_info(sound->filename, i);
14086 new = &(*new)->next;
14090 // add pointers to previous list nodes
14092 struct MusicFileInfo *node = music_file_info;
14094 while (node != NULL)
14097 node->next->prev = node;
14103 static void add_helpanim_entry(int element, int action, int direction,
14104 int delay, int *num_list_entries)
14106 struct HelpAnimInfo *new_list_entry;
14107 (*num_list_entries)++;
14110 checked_realloc(helpanim_info,
14111 *num_list_entries * sizeof(struct HelpAnimInfo));
14112 new_list_entry = &helpanim_info[*num_list_entries - 1];
14114 new_list_entry->element = element;
14115 new_list_entry->action = action;
14116 new_list_entry->direction = direction;
14117 new_list_entry->delay = delay;
14120 static void print_unknown_token(char *filename, char *token, int token_nr)
14125 Warn("unknown token(s) found in config file:");
14126 Warn("- config file: '%s'", filename);
14129 Warn("- token: '%s'", token);
14132 static void print_unknown_token_end(int token_nr)
14138 void LoadHelpAnimInfo(void)
14140 char *filename = getHelpAnimFilename();
14141 SetupFileList *setup_file_list = NULL, *list;
14142 SetupFileHash *element_hash, *action_hash, *direction_hash;
14143 int num_list_entries = 0;
14144 int num_unknown_tokens = 0;
14147 if (fileExists(filename))
14148 setup_file_list = loadSetupFileList(filename);
14150 if (setup_file_list == NULL)
14152 // use reliable default values from static configuration
14153 SetupFileList *insert_ptr;
14155 insert_ptr = setup_file_list =
14156 newSetupFileList(helpanim_config[0].token,
14157 helpanim_config[0].value);
14159 for (i = 1; helpanim_config[i].token; i++)
14160 insert_ptr = addListEntry(insert_ptr,
14161 helpanim_config[i].token,
14162 helpanim_config[i].value);
14165 element_hash = newSetupFileHash();
14166 action_hash = newSetupFileHash();
14167 direction_hash = newSetupFileHash();
14169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14170 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14172 for (i = 0; i < NUM_ACTIONS; i++)
14173 setHashEntry(action_hash, element_action_info[i].suffix,
14174 i_to_a(element_action_info[i].value));
14176 // do not store direction index (bit) here, but direction value!
14177 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14178 setHashEntry(direction_hash, element_direction_info[i].suffix,
14179 i_to_a(1 << element_direction_info[i].value));
14181 for (list = setup_file_list; list != NULL; list = list->next)
14183 char *element_token, *action_token, *direction_token;
14184 char *element_value, *action_value, *direction_value;
14185 int delay = atoi(list->value);
14187 if (strEqual(list->token, "end"))
14189 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14194 /* first try to break element into element/action/direction parts;
14195 if this does not work, also accept combined "element[.act][.dir]"
14196 elements (like "dynamite.active"), which are unique elements */
14198 if (strchr(list->token, '.') == NULL) // token contains no '.'
14200 element_value = getHashEntry(element_hash, list->token);
14201 if (element_value != NULL) // element found
14202 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14203 &num_list_entries);
14206 // no further suffixes found -- this is not an element
14207 print_unknown_token(filename, list->token, num_unknown_tokens++);
14213 // token has format "<prefix>.<something>"
14215 action_token = strchr(list->token, '.'); // suffix may be action ...
14216 direction_token = action_token; // ... or direction
14218 element_token = getStringCopy(list->token);
14219 *strchr(element_token, '.') = '\0';
14221 element_value = getHashEntry(element_hash, element_token);
14223 if (element_value == NULL) // this is no element
14225 element_value = getHashEntry(element_hash, list->token);
14226 if (element_value != NULL) // combined element found
14227 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14228 &num_list_entries);
14230 print_unknown_token(filename, list->token, num_unknown_tokens++);
14232 free(element_token);
14237 action_value = getHashEntry(action_hash, action_token);
14239 if (action_value != NULL) // action found
14241 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14242 &num_list_entries);
14244 free(element_token);
14249 direction_value = getHashEntry(direction_hash, direction_token);
14251 if (direction_value != NULL) // direction found
14253 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14254 &num_list_entries);
14256 free(element_token);
14261 if (strchr(action_token + 1, '.') == NULL)
14263 // no further suffixes found -- this is not an action nor direction
14265 element_value = getHashEntry(element_hash, list->token);
14266 if (element_value != NULL) // combined element found
14267 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14268 &num_list_entries);
14270 print_unknown_token(filename, list->token, num_unknown_tokens++);
14272 free(element_token);
14277 // token has format "<prefix>.<suffix>.<something>"
14279 direction_token = strchr(action_token + 1, '.');
14281 action_token = getStringCopy(action_token);
14282 *strchr(action_token + 1, '.') = '\0';
14284 action_value = getHashEntry(action_hash, action_token);
14286 if (action_value == NULL) // this is no action
14288 element_value = getHashEntry(element_hash, list->token);
14289 if (element_value != NULL) // combined element found
14290 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14291 &num_list_entries);
14293 print_unknown_token(filename, list->token, num_unknown_tokens++);
14295 free(element_token);
14296 free(action_token);
14301 direction_value = getHashEntry(direction_hash, direction_token);
14303 if (direction_value != NULL) // direction found
14305 add_helpanim_entry(atoi(element_value), atoi(action_value),
14306 atoi(direction_value), delay, &num_list_entries);
14308 free(element_token);
14309 free(action_token);
14314 // this is no direction
14316 element_value = getHashEntry(element_hash, list->token);
14317 if (element_value != NULL) // combined element found
14318 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14319 &num_list_entries);
14321 print_unknown_token(filename, list->token, num_unknown_tokens++);
14323 free(element_token);
14324 free(action_token);
14327 print_unknown_token_end(num_unknown_tokens);
14329 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14330 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14332 freeSetupFileList(setup_file_list);
14333 freeSetupFileHash(element_hash);
14334 freeSetupFileHash(action_hash);
14335 freeSetupFileHash(direction_hash);
14338 for (i = 0; i < num_list_entries; i++)
14339 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14340 EL_NAME(helpanim_info[i].element),
14341 helpanim_info[i].element,
14342 helpanim_info[i].action,
14343 helpanim_info[i].direction,
14344 helpanim_info[i].delay);
14348 void LoadHelpTextInfo(void)
14350 char *filename = getHelpTextFilename();
14353 if (helptext_info != NULL)
14355 freeSetupFileHash(helptext_info);
14356 helptext_info = NULL;
14359 if (fileExists(filename))
14360 helptext_info = loadSetupFileHash(filename);
14362 if (helptext_info == NULL)
14364 // use reliable default values from static configuration
14365 helptext_info = newSetupFileHash();
14367 for (i = 0; helptext_config[i].token; i++)
14368 setHashEntry(helptext_info,
14369 helptext_config[i].token,
14370 helptext_config[i].value);
14374 BEGIN_HASH_ITERATION(helptext_info, itr)
14376 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14377 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14379 END_HASH_ITERATION(hash, itr)
14384 // ----------------------------------------------------------------------------
14386 // ----------------------------------------------------------------------------
14388 #define MAX_NUM_CONVERT_LEVELS 1000
14390 void ConvertLevels(void)
14392 static LevelDirTree *convert_leveldir = NULL;
14393 static int convert_level_nr = -1;
14394 static int num_levels_handled = 0;
14395 static int num_levels_converted = 0;
14396 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14399 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14400 global.convert_leveldir);
14402 if (convert_leveldir == NULL)
14403 Fail("no such level identifier: '%s'", global.convert_leveldir);
14405 leveldir_current = convert_leveldir;
14407 if (global.convert_level_nr != -1)
14409 convert_leveldir->first_level = global.convert_level_nr;
14410 convert_leveldir->last_level = global.convert_level_nr;
14413 convert_level_nr = convert_leveldir->first_level;
14415 PrintLine("=", 79);
14416 Print("Converting levels\n");
14417 PrintLine("-", 79);
14418 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14419 Print("Level series name: '%s'\n", convert_leveldir->name);
14420 Print("Level series author: '%s'\n", convert_leveldir->author);
14421 Print("Number of levels: %d\n", convert_leveldir->levels);
14422 PrintLine("=", 79);
14425 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14426 levels_failed[i] = FALSE;
14428 while (convert_level_nr <= convert_leveldir->last_level)
14430 char *level_filename;
14433 level_nr = convert_level_nr++;
14435 Print("Level %03d: ", level_nr);
14437 LoadLevel(level_nr);
14438 if (level.no_level_file || level.no_valid_file)
14440 Print("(no level)\n");
14444 Print("converting level ... ");
14447 // special case: conversion of some EMC levels as requested by ACME
14448 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14451 level_filename = getDefaultLevelFilename(level_nr);
14452 new_level = !fileExists(level_filename);
14456 SaveLevel(level_nr);
14458 num_levels_converted++;
14460 Print("converted.\n");
14464 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14465 levels_failed[level_nr] = TRUE;
14467 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14470 num_levels_handled++;
14474 PrintLine("=", 79);
14475 Print("Number of levels handled: %d\n", num_levels_handled);
14476 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14477 (num_levels_handled ?
14478 num_levels_converted * 100 / num_levels_handled : 0));
14479 PrintLine("-", 79);
14480 Print("Summary (for automatic parsing by scripts):\n");
14481 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14482 convert_leveldir->identifier, num_levels_converted,
14483 num_levels_handled,
14484 (num_levels_handled ?
14485 num_levels_converted * 100 / num_levels_handled : 0));
14487 if (num_levels_handled != num_levels_converted)
14489 Print(", FAILED:");
14490 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14491 if (levels_failed[i])
14496 PrintLine("=", 79);
14498 CloseAllAndExit(0);
14502 // ----------------------------------------------------------------------------
14503 // create and save images for use in level sketches (raw BMP format)
14504 // ----------------------------------------------------------------------------
14506 void CreateLevelSketchImages(void)
14512 InitElementPropertiesGfxElement();
14514 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14515 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14517 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14519 int element = getMappedElement(i);
14520 char basename1[16];
14521 char basename2[16];
14525 sprintf(basename1, "%04d.bmp", i);
14526 sprintf(basename2, "%04ds.bmp", i);
14528 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14529 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14531 DrawSizedElement(0, 0, element, TILESIZE);
14532 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14534 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14535 Fail("cannot save level sketch image file '%s'", filename1);
14537 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14538 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14540 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14541 Fail("cannot save level sketch image file '%s'", filename2);
14546 // create corresponding SQL statements (for normal and small images)
14549 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14550 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14553 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14554 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14556 // optional: create content for forum level sketch demonstration post
14558 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14561 FreeBitmap(bitmap1);
14562 FreeBitmap(bitmap2);
14565 fprintf(stderr, "\n");
14567 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14569 CloseAllAndExit(0);
14573 // ----------------------------------------------------------------------------
14574 // create and save images for element collecting animations (raw BMP format)
14575 // ----------------------------------------------------------------------------
14577 static boolean createCollectImage(int element)
14579 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14582 void CreateCollectElementImages(void)
14586 int anim_frames = num_steps - 1;
14587 int tile_size = TILESIZE;
14588 int anim_width = tile_size * anim_frames;
14589 int anim_height = tile_size;
14590 int num_collect_images = 0;
14591 int pos_collect_images = 0;
14593 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14594 if (createCollectImage(i))
14595 num_collect_images++;
14597 Info("Creating %d element collecting animation images ...",
14598 num_collect_images);
14600 int dst_width = anim_width * 2;
14601 int dst_height = anim_height * num_collect_images / 2;
14602 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14603 char *basename_bmp = "RocksCollect.bmp";
14604 char *basename_png = "RocksCollect.png";
14605 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14606 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14607 int len_filename_bmp = strlen(filename_bmp);
14608 int len_filename_png = strlen(filename_png);
14609 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14610 char cmd_convert[max_command_len];
14612 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14616 // force using RGBA surface for destination bitmap
14617 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14618 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14620 dst_bitmap->surface =
14621 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14623 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14625 if (!createCollectImage(i))
14628 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14629 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14630 int graphic = el2img(i);
14631 char *token_name = element_info[i].token_name;
14632 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14633 Bitmap *src_bitmap;
14636 Info("- creating collecting image for '%s' ...", token_name);
14638 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14640 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14641 tile_size, tile_size, 0, 0);
14643 // force using RGBA surface for temporary bitmap (using transparent black)
14644 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14645 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14647 tmp_bitmap->surface =
14648 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14650 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14652 for (j = 0; j < anim_frames; j++)
14654 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14655 int frame_size = frame_size_final * num_steps;
14656 int offset = (tile_size - frame_size_final) / 2;
14657 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14659 while (frame_size > frame_size_final)
14663 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14665 FreeBitmap(frame_bitmap);
14667 frame_bitmap = half_bitmap;
14670 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14671 frame_size_final, frame_size_final,
14672 dst_x + j * tile_size + offset, dst_y + offset);
14674 FreeBitmap(frame_bitmap);
14677 tmp_bitmap->surface_masked = NULL;
14679 FreeBitmap(tmp_bitmap);
14681 pos_collect_images++;
14684 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14685 Fail("cannot save element collecting image file '%s'", filename_bmp);
14687 FreeBitmap(dst_bitmap);
14689 Info("Converting image file from BMP to PNG ...");
14691 if (system(cmd_convert) != 0)
14692 Fail("converting image file failed");
14694 unlink(filename_bmp);
14698 CloseAllAndExit(0);
14702 // ----------------------------------------------------------------------------
14703 // create and save images for custom and group elements (raw BMP format)
14704 // ----------------------------------------------------------------------------
14706 void CreateCustomElementImages(char *directory)
14708 char *src_basename = "RocksCE-template.ilbm";
14709 char *dst_basename = "RocksCE.bmp";
14710 char *src_filename = getPath2(directory, src_basename);
14711 char *dst_filename = getPath2(directory, dst_basename);
14712 Bitmap *src_bitmap;
14714 int yoffset_ce = 0;
14715 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14718 InitVideoDefaults();
14720 ReCreateBitmap(&backbuffer, video.width, video.height);
14722 src_bitmap = LoadImage(src_filename);
14724 bitmap = CreateBitmap(TILEX * 16 * 2,
14725 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14735 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14736 TILEX * x, TILEY * y + yoffset_ce);
14738 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14740 TILEX * x + TILEX * 16,
14741 TILEY * y + yoffset_ce);
14743 for (j = 2; j >= 0; j--)
14747 BlitBitmap(src_bitmap, bitmap,
14748 TILEX + c * 7, 0, 6, 10,
14749 TILEX * x + 6 + j * 7,
14750 TILEY * y + 11 + yoffset_ce);
14752 BlitBitmap(src_bitmap, bitmap,
14753 TILEX + c * 8, TILEY, 6, 10,
14754 TILEX * 16 + TILEX * x + 6 + j * 8,
14755 TILEY * y + 10 + yoffset_ce);
14761 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14768 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14769 TILEX * x, TILEY * y + yoffset_ge);
14771 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14773 TILEX * x + TILEX * 16,
14774 TILEY * y + yoffset_ge);
14776 for (j = 1; j >= 0; j--)
14780 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14781 TILEX * x + 6 + j * 10,
14782 TILEY * y + 11 + yoffset_ge);
14784 BlitBitmap(src_bitmap, bitmap,
14785 TILEX + c * 8, TILEY + 12, 6, 10,
14786 TILEX * 16 + TILEX * x + 10 + j * 8,
14787 TILEY * y + 10 + yoffset_ge);
14793 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14794 Fail("cannot save CE graphics file '%s'", dst_filename);
14796 FreeBitmap(bitmap);
14798 CloseAllAndExit(0);