1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
659 EL_BD_MAGIC_WALL, -1,
660 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
661 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
664 EL_BD_MAGIC_WALL, -1,
665 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
666 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
669 EL_BD_MAGIC_WALL, -1,
670 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
671 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
674 EL_BD_MAGIC_WALL, -1,
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
676 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
679 EL_BD_MAGIC_WALL, -1,
680 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
681 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
684 EL_BD_MAGIC_WALL, -1,
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
686 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
689 EL_BD_MAGIC_WALL, -1,
690 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
691 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
696 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
697 &li.bd_clock_extra_time, 30
701 EL_BD_VOODOO_DOLL, -1,
702 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
703 &li.bd_voodoo_collects_diamonds, FALSE
706 EL_BD_VOODOO_DOLL, -1,
707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
708 &li.bd_voodoo_hurt_kills_player, FALSE
711 EL_BD_VOODOO_DOLL, -1,
712 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
713 &li.bd_voodoo_dies_by_rock, FALSE
716 EL_BD_VOODOO_DOLL, -1,
717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
718 &li.bd_voodoo_vanish_by_explosion, TRUE
721 EL_BD_VOODOO_DOLL, -1,
722 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
723 &li.bd_voodoo_penalty_time, 30
728 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
729 &li.bd_slime_is_predictable, TRUE
733 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
734 &li.bd_slime_permeability_rate, 100
738 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
739 &li.bd_slime_permeability_bits_c64, 0
743 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
744 &li.bd_slime_random_seed_c64, -1
748 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
749 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
753 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
754 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
758 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
759 &li.bd_slime_eats_element_2, EL_BD_ROCK
763 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
764 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
768 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
769 &li.bd_slime_eats_element_3, EL_BD_NUT
773 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
774 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
779 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
780 &li.bd_acid_eats_element, EL_BD_SAND
784 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
785 &li.bd_acid_spread_rate, 3
789 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
790 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
795 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
796 &li.bd_biter_move_delay, 0
800 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
801 &li.bd_biter_eats_element, EL_BD_DIAMOND
806 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
807 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
811 EL_BD_EXPANDABLE_WALL_ANY, -1,
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.bd_change_expanding_wall, FALSE
816 EL_BD_EXPANDABLE_WALL_ANY, -1,
817 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
818 &li.bd_expanding_wall_looks_like, EL_BD_WALL
822 EL_BD_REPLICATOR, -1,
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
824 &li.bd_replicators_active, TRUE
827 EL_BD_REPLICATOR, -1,
828 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
829 &li.bd_replicator_create_delay, 4
833 EL_BD_CONVEYOR_LEFT, -1,
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
835 &li.bd_conveyor_belts_active, TRUE
838 EL_BD_CONVEYOR_LEFT, -1,
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
840 &li.bd_conveyor_belts_changed, FALSE
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
846 &li.bd_water_cannot_flow_down, FALSE
851 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
852 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
856 EL_BD_PNEUMATIC_HAMMER, -1,
857 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
858 &li.bd_hammer_walls_break_delay, 5
861 EL_BD_PNEUMATIC_HAMMER, -1,
862 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
863 &li.bd_hammer_walls_reappear, FALSE
866 EL_BD_PNEUMATIC_HAMMER, -1,
867 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
868 &li.bd_hammer_walls_reappear_delay, 100
873 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
874 &li.bd_num_skeletons_needed_for_pot, 5
878 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
879 &li.bd_skeleton_worth_num_diamonds, 0
882 // (the following values are related to various game elements)
886 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
887 &li.score[SC_EMERALD], 10
892 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
893 &li.score[SC_DIAMOND], 10
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
899 &li.score[SC_BUG], 10
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
905 &li.score[SC_SPACESHIP], 10
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
911 &li.score[SC_PACMAN], 10
916 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
917 &li.score[SC_NUT], 10
922 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
923 &li.score[SC_DYNAMITE], 10
928 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
929 &li.score[SC_KEY], 10
934 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
935 &li.score[SC_PEARL], 10
940 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
941 &li.score[SC_CRYSTAL], 10
944 // (amoeba values used by R'n'D game engine only)
947 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
948 &li.amoeba_content, EL_DIAMOND
952 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
957 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
958 &li.grow_into_diggable, TRUE
960 // (amoeba values used by BD game engine only)
963 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
964 &li.bd_amoeba_wait_for_hatching, FALSE
968 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
969 &li.bd_amoeba_start_immediately, TRUE
973 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
974 &li.bd_amoeba_2_explode_by_amoeba, TRUE
978 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
979 &li.bd_amoeba_threshold_too_big, 200
983 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
984 &li.bd_amoeba_slow_growth_time, 200
988 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
989 &li.bd_amoeba_slow_growth_rate, 3
993 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
994 &li.bd_amoeba_fast_growth_rate, 25
998 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
999 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1003 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1004 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1009 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1010 &li.bd_amoeba_2_threshold_too_big, 200
1014 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1015 &li.bd_amoeba_2_slow_growth_time, 200
1019 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1020 &li.bd_amoeba_2_slow_growth_rate, 3
1024 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1025 &li.bd_amoeba_2_fast_growth_rate, 25
1029 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1030 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1034 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1035 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1039 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1040 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1044 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1045 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1050 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1051 &li.yamyam_content, EL_ROCK, NULL,
1052 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1057 &li.score[SC_YAMYAM], 10
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 &li.score[SC_ROBOT], 10
1067 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1073 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1079 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1080 &li.time_magic_wall, 10
1084 EL_GAME_OF_LIFE, -1,
1085 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1086 &li.game_of_life[0], 2
1089 EL_GAME_OF_LIFE, -1,
1090 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1091 &li.game_of_life[1], 3
1094 EL_GAME_OF_LIFE, -1,
1095 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1096 &li.game_of_life[2], 3
1099 EL_GAME_OF_LIFE, -1,
1100 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1101 &li.game_of_life[3], 3
1104 EL_GAME_OF_LIFE, -1,
1105 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1106 &li.use_life_bugs, FALSE
1111 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1116 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1121 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1126 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1131 EL_TIMEGATE_SWITCH, -1,
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1133 &li.time_timegate, 10
1137 EL_LIGHT_SWITCH_ACTIVE, -1,
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1143 EL_SHIELD_NORMAL, -1,
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1145 &li.shield_normal_time, 10
1148 EL_SHIELD_NORMAL, -1,
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1150 &li.score[SC_SHIELD], 10
1154 EL_SHIELD_DEADLY, -1,
1155 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1156 &li.shield_deadly_time, 10
1159 EL_SHIELD_DEADLY, -1,
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1161 &li.score[SC_SHIELD], 10
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1172 &li.extra_time_score, 10
1176 EL_TIME_ORB_FULL, -1,
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1178 &li.time_orb_time, 10
1181 EL_TIME_ORB_FULL, -1,
1182 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1183 &li.use_time_orb_bug, FALSE
1188 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1189 &li.use_spring_bug, FALSE
1194 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1195 &li.android_move_time, 10
1199 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1200 &li.android_clone_time, 10
1203 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1204 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1205 &li.android_clone_element[0], EL_EMPTY, NULL,
1206 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1210 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1211 &li.android_clone_element[0], EL_EMPTY, NULL,
1212 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1217 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1218 &li.lenses_score, 10
1222 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1227 EL_EMC_MAGNIFIER, -1,
1228 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1229 &li.magnify_score, 10
1232 EL_EMC_MAGNIFIER, -1,
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1234 &li.magnify_time, 10
1238 EL_EMC_MAGIC_BALL, -1,
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1243 EL_EMC_MAGIC_BALL, -1,
1244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1245 &li.ball_random, FALSE
1248 EL_EMC_MAGIC_BALL, -1,
1249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1250 &li.ball_active_initial, FALSE
1253 EL_EMC_MAGIC_BALL, -1,
1254 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1255 &li.ball_content, EL_EMPTY, NULL,
1256 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1260 EL_SOKOBAN_FIELD_EMPTY, -1,
1261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1262 &li.sb_fields_needed, TRUE
1266 EL_SOKOBAN_OBJECT, -1,
1267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1268 &li.sb_objects_needed, TRUE
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1274 &li.mm_laser_red, FALSE
1278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1279 &li.mm_laser_green, FALSE
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1284 &li.mm_laser_blue, TRUE
1289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1290 &li.df_laser_red, TRUE
1294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1295 &li.df_laser_green, TRUE
1299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1300 &li.df_laser_blue, FALSE
1304 EL_MM_FUSE_ACTIVE, -1,
1305 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1306 &li.mm_time_fuse, 25
1310 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1311 &li.mm_time_bomb, 75
1315 EL_MM_GRAY_BALL, -1,
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1317 &li.mm_time_ball, 75
1320 EL_MM_GRAY_BALL, -1,
1321 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1322 &li.mm_ball_choice_mode, ANIM_RANDOM
1325 EL_MM_GRAY_BALL, -1,
1326 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1327 &li.mm_ball_content, EL_EMPTY, NULL,
1328 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1331 EL_MM_GRAY_BALL, -1,
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1333 &li.rotate_mm_ball_content, TRUE
1336 EL_MM_GRAY_BALL, -1,
1337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1338 &li.explode_mm_ball, FALSE
1342 EL_MM_STEEL_BLOCK, -1,
1343 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1344 &li.mm_time_block, 75
1347 EL_MM_LIGHTBALL, -1,
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1349 &li.score[SC_ELEM_BONUS], 10
1359 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1363 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1364 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1368 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1369 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1375 &xx_envelope.autowrap, FALSE
1379 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1380 &xx_envelope.centered, FALSE
1385 TYPE_STRING, CONF_VALUE_BYTES(1),
1386 &xx_envelope.text, -1, NULL,
1387 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1388 &xx_default_string_empty[0]
1398 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1402 TYPE_STRING, CONF_VALUE_BYTES(1),
1403 &xx_ei.description[0], -1,
1404 &yy_ei.description[0],
1405 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1406 &xx_default_description[0]
1411 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1412 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1413 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1415 #if ENABLE_RESERVED_CODE
1416 // (reserved for later use)
1419 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1420 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1421 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1428 &xx_ei.use_gfx_element, FALSE,
1429 &yy_ei.use_gfx_element
1433 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1434 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1435 &yy_ei.gfx_element_initial
1440 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1441 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1442 &yy_ei.access_direction
1447 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1448 &xx_ei.collect_score_initial, 10,
1449 &yy_ei.collect_score_initial
1453 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1454 &xx_ei.collect_count_initial, 1,
1455 &yy_ei.collect_count_initial
1460 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1461 &xx_ei.ce_value_fixed_initial, 0,
1462 &yy_ei.ce_value_fixed_initial
1466 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1467 &xx_ei.ce_value_random_initial, 0,
1468 &yy_ei.ce_value_random_initial
1472 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1473 &xx_ei.use_last_ce_value, FALSE,
1474 &yy_ei.use_last_ce_value
1479 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1480 &xx_ei.push_delay_fixed, 8,
1481 &yy_ei.push_delay_fixed
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1486 &xx_ei.push_delay_random, 8,
1487 &yy_ei.push_delay_random
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1492 &xx_ei.drop_delay_fixed, 0,
1493 &yy_ei.drop_delay_fixed
1497 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1498 &xx_ei.drop_delay_random, 0,
1499 &yy_ei.drop_delay_random
1503 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1504 &xx_ei.move_delay_fixed, 0,
1505 &yy_ei.move_delay_fixed
1509 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1510 &xx_ei.move_delay_random, 0,
1511 &yy_ei.move_delay_random
1515 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1516 &xx_ei.step_delay_fixed, 0,
1517 &yy_ei.step_delay_fixed
1521 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1522 &xx_ei.step_delay_random, 0,
1523 &yy_ei.step_delay_random
1528 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1529 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1534 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1535 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1536 &yy_ei.move_direction_initial
1540 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1541 &xx_ei.move_stepsize, TILEX / 8,
1542 &yy_ei.move_stepsize
1547 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1548 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1549 &yy_ei.move_enter_element
1553 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1554 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1555 &yy_ei.move_leave_element
1559 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1560 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1561 &yy_ei.move_leave_type
1566 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1567 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1568 &yy_ei.slippery_type
1573 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1574 &xx_ei.explosion_type, EXPLODES_3X3,
1575 &yy_ei.explosion_type
1579 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1580 &xx_ei.explosion_delay, 16,
1581 &yy_ei.explosion_delay
1585 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1586 &xx_ei.ignition_delay, 8,
1587 &yy_ei.ignition_delay
1592 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1593 &xx_ei.content, EL_EMPTY_SPACE,
1595 &xx_num_contents, 1, 1
1598 // ---------- "num_change_pages" must be the last entry ---------------------
1601 -1, SAVE_CONF_ALWAYS,
1602 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1603 &xx_ei.num_change_pages, 1,
1604 &yy_ei.num_change_pages
1615 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1617 // ---------- "current_change_page" must be the first entry -----------------
1620 -1, SAVE_CONF_ALWAYS,
1621 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1622 &xx_current_change_page, -1
1625 // ---------- (the remaining entries can be in any order) -------------------
1629 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1630 &xx_change.can_change, FALSE
1635 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1636 &xx_event_bits[0], 0
1640 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1641 &xx_event_bits[1], 0
1646 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1647 &xx_change.trigger_player, CH_PLAYER_ANY
1651 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1652 &xx_change.trigger_side, CH_SIDE_ANY
1656 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1657 &xx_change.trigger_page, CH_PAGE_ANY
1662 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1663 &xx_change.target_element, EL_EMPTY_SPACE
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1669 &xx_change.delay_fixed, 0
1673 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1674 &xx_change.delay_random, 0
1678 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1679 &xx_change.delay_frames, FRAMES_PER_SECOND
1684 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1685 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1690 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1691 &xx_change.explode, FALSE
1695 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1696 &xx_change.use_target_content, FALSE
1700 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1701 &xx_change.only_if_complete, FALSE
1705 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1706 &xx_change.use_random_replace, FALSE
1710 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1711 &xx_change.random_percentage, 100
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1716 &xx_change.replace_when, CP_WHEN_EMPTY
1721 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1722 &xx_change.has_action, FALSE
1726 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1727 &xx_change.action_type, CA_NO_ACTION
1731 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1732 &xx_change.action_mode, CA_MODE_UNDEFINED
1736 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1737 &xx_change.action_arg, CA_ARG_UNDEFINED
1742 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1743 &xx_change.action_element, EL_EMPTY_SPACE
1748 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1749 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1750 &xx_num_contents, 1, 1
1760 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1764 TYPE_STRING, CONF_VALUE_BYTES(1),
1765 &xx_ei.description[0], -1, NULL,
1766 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1767 &xx_default_description[0]
1772 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1773 &xx_ei.use_gfx_element, FALSE
1777 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1778 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1783 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1784 &xx_group.choice_mode, ANIM_RANDOM
1789 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1790 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1791 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1801 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1805 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1806 &xx_ei.use_gfx_element, FALSE
1810 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1811 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1821 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1825 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1826 &li.block_snap_field, TRUE
1830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1831 &li.continuous_snapping, TRUE
1835 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1836 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1841 &li.use_start_element[0], FALSE
1845 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1846 &li.start_element[0], EL_PLAYER_1
1850 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1851 &li.use_artwork_element[0], FALSE
1855 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1856 &li.artwork_element[0], EL_PLAYER_1
1860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1861 &li.use_explosion_element[0], FALSE
1865 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1866 &li.explosion_element[0], EL_PLAYER_1
1881 filetype_id_list[] =
1883 { LEVEL_FILE_TYPE_RND, "RND" },
1884 { LEVEL_FILE_TYPE_BD, "BD" },
1885 { LEVEL_FILE_TYPE_EM, "EM" },
1886 { LEVEL_FILE_TYPE_SP, "SP" },
1887 { LEVEL_FILE_TYPE_DX, "DX" },
1888 { LEVEL_FILE_TYPE_SB, "SB" },
1889 { LEVEL_FILE_TYPE_DC, "DC" },
1890 { LEVEL_FILE_TYPE_MM, "MM" },
1891 { LEVEL_FILE_TYPE_MM, "DF" },
1896 // ============================================================================
1897 // level file functions
1898 // ============================================================================
1900 static boolean check_special_flags(char *flag)
1902 if (strEqual(options.special_flags, flag) ||
1903 strEqual(leveldir_current->special_flags, flag))
1909 static struct DateInfo getCurrentDate(void)
1911 time_t epoch_seconds = time(NULL);
1912 struct tm *now = localtime(&epoch_seconds);
1913 struct DateInfo date;
1915 date.year = now->tm_year + 1900;
1916 date.month = now->tm_mon + 1;
1917 date.day = now->tm_mday;
1919 date.src = DATE_SRC_CLOCK;
1924 static void resetEventFlags(struct ElementChangeInfo *change)
1928 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1929 change->has_event[i] = FALSE;
1932 static void resetEventBits(void)
1936 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1937 xx_event_bits[i] = 0;
1940 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1944 /* important: only change event flag if corresponding event bit is set
1945 (this is because all xx_event_bits[] values are loaded separately,
1946 and all xx_event_bits[] values are set back to zero before loading
1947 another value xx_event_bits[x] (each value representing 32 flags)) */
1949 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1950 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1951 change->has_event[i] = TRUE;
1954 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1958 /* in contrast to the above function setEventFlagsFromEventBits(), it
1959 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1960 depending on the corresponding change->has_event[i] values here, as
1961 all xx_event_bits[] values are reset in resetEventBits() before */
1963 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1964 if (change->has_event[i])
1965 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1968 static char *getDefaultElementDescription(struct ElementInfo *ei)
1970 static char description[MAX_ELEMENT_NAME_LEN + 1];
1971 char *default_description = (ei->custom_description != NULL ?
1972 ei->custom_description :
1973 ei->editor_description);
1976 // always start with reliable default values
1977 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1978 description[i] = '\0';
1980 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1981 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1983 return &description[0];
1986 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1988 char *default_description = getDefaultElementDescription(ei);
1991 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1992 ei->description[i] = default_description[i];
1995 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1999 for (i = 0; conf[i].data_type != -1; i++)
2001 int default_value = conf[i].default_value;
2002 int data_type = conf[i].data_type;
2003 int conf_type = conf[i].conf_type;
2004 int byte_mask = conf_type & CONF_MASK_BYTES;
2006 if (byte_mask == CONF_MASK_MULTI_BYTES)
2008 int default_num_entities = conf[i].default_num_entities;
2009 int max_num_entities = conf[i].max_num_entities;
2011 *(int *)(conf[i].num_entities) = default_num_entities;
2013 if (data_type == TYPE_STRING)
2015 char *default_string = conf[i].default_string;
2016 char *string = (char *)(conf[i].value);
2018 strncpy(string, default_string, max_num_entities);
2020 else if (data_type == TYPE_ELEMENT_LIST)
2022 int *element_array = (int *)(conf[i].value);
2025 for (j = 0; j < max_num_entities; j++)
2026 element_array[j] = default_value;
2028 else if (data_type == TYPE_CONTENT_LIST)
2030 struct Content *content = (struct Content *)(conf[i].value);
2033 for (c = 0; c < max_num_entities; c++)
2034 for (y = 0; y < 3; y++)
2035 for (x = 0; x < 3; x++)
2036 content[c].e[x][y] = default_value;
2039 else // constant size configuration data (1, 2 or 4 bytes)
2041 if (data_type == TYPE_BOOLEAN)
2042 *(boolean *)(conf[i].value) = default_value;
2044 *(int *) (conf[i].value) = default_value;
2049 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2053 for (i = 0; conf[i].data_type != -1; i++)
2055 int data_type = conf[i].data_type;
2056 int conf_type = conf[i].conf_type;
2057 int byte_mask = conf_type & CONF_MASK_BYTES;
2059 if (byte_mask == CONF_MASK_MULTI_BYTES)
2061 int max_num_entities = conf[i].max_num_entities;
2063 if (data_type == TYPE_STRING)
2065 char *string = (char *)(conf[i].value);
2066 char *string_copy = (char *)(conf[i].value_copy);
2068 strncpy(string_copy, string, max_num_entities);
2070 else if (data_type == TYPE_ELEMENT_LIST)
2072 int *element_array = (int *)(conf[i].value);
2073 int *element_array_copy = (int *)(conf[i].value_copy);
2076 for (j = 0; j < max_num_entities; j++)
2077 element_array_copy[j] = element_array[j];
2079 else if (data_type == TYPE_CONTENT_LIST)
2081 struct Content *content = (struct Content *)(conf[i].value);
2082 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2085 for (c = 0; c < max_num_entities; c++)
2086 for (y = 0; y < 3; y++)
2087 for (x = 0; x < 3; x++)
2088 content_copy[c].e[x][y] = content[c].e[x][y];
2091 else // constant size configuration data (1, 2 or 4 bytes)
2093 if (data_type == TYPE_BOOLEAN)
2094 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2096 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2101 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2105 xx_ei = *ei_from; // copy element data into temporary buffer
2106 yy_ei = *ei_to; // copy element data into temporary buffer
2108 copyConfigFromConfigList(chunk_config_CUSX_base);
2113 // ---------- reinitialize and copy change pages ----------
2115 ei_to->num_change_pages = ei_from->num_change_pages;
2116 ei_to->current_change_page = ei_from->current_change_page;
2118 setElementChangePages(ei_to, ei_to->num_change_pages);
2120 for (i = 0; i < ei_to->num_change_pages; i++)
2121 ei_to->change_page[i] = ei_from->change_page[i];
2123 // ---------- copy group element info ----------
2124 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2125 *ei_to->group = *ei_from->group;
2127 // mark this custom element as modified
2128 ei_to->modified_settings = TRUE;
2131 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2133 int change_page_size = sizeof(struct ElementChangeInfo);
2135 ei->num_change_pages = MAX(1, change_pages);
2138 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2140 if (ei->current_change_page >= ei->num_change_pages)
2141 ei->current_change_page = ei->num_change_pages - 1;
2143 ei->change = &ei->change_page[ei->current_change_page];
2146 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2148 xx_change = *change; // copy change data into temporary buffer
2150 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2152 *change = xx_change;
2154 resetEventFlags(change);
2156 change->direct_action = 0;
2157 change->other_action = 0;
2159 change->pre_change_function = NULL;
2160 change->change_function = NULL;
2161 change->post_change_function = NULL;
2164 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2168 li = *level; // copy level data into temporary buffer
2169 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2170 *level = li; // copy temporary buffer back to level data
2172 setLevelInfoToDefaults_BD();
2173 setLevelInfoToDefaults_EM();
2174 setLevelInfoToDefaults_SP();
2175 setLevelInfoToDefaults_MM();
2177 level->native_bd_level = &native_bd_level;
2178 level->native_em_level = &native_em_level;
2179 level->native_sp_level = &native_sp_level;
2180 level->native_mm_level = &native_mm_level;
2182 level->file_version = FILE_VERSION_ACTUAL;
2183 level->game_version = GAME_VERSION_ACTUAL;
2185 level->creation_date = getCurrentDate();
2187 level->encoding_16bit_field = TRUE;
2188 level->encoding_16bit_yamyam = TRUE;
2189 level->encoding_16bit_amoeba = TRUE;
2191 // clear level name and level author string buffers
2192 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2193 level->name[i] = '\0';
2194 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2195 level->author[i] = '\0';
2197 // set level name and level author to default values
2198 strcpy(level->name, NAMELESS_LEVEL_NAME);
2199 strcpy(level->author, ANONYMOUS_NAME);
2201 // set level playfield to playable default level with player and exit
2202 for (x = 0; x < MAX_LEV_FIELDX; x++)
2203 for (y = 0; y < MAX_LEV_FIELDY; y++)
2204 level->field[x][y] = EL_SAND;
2206 level->field[0][0] = EL_PLAYER_1;
2207 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2209 BorderElement = EL_STEELWALL;
2211 // detect custom elements when loading them
2212 level->file_has_custom_elements = FALSE;
2214 // set all bug compatibility flags to "false" => do not emulate this bug
2215 level->use_action_after_change_bug = FALSE;
2217 if (leveldir_current)
2219 // try to determine better author name than 'anonymous'
2220 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2222 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2223 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2227 switch (LEVELCLASS(leveldir_current))
2229 case LEVELCLASS_TUTORIAL:
2230 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2233 case LEVELCLASS_CONTRIB:
2234 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2235 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2238 case LEVELCLASS_PRIVATE:
2239 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2240 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2244 // keep default value
2251 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2253 static boolean clipboard_elements_initialized = FALSE;
2256 InitElementPropertiesStatic();
2258 li = *level; // copy level data into temporary buffer
2259 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2260 *level = li; // copy temporary buffer back to level data
2262 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2265 struct ElementInfo *ei = &element_info[element];
2267 if (element == EL_MM_GRAY_BALL)
2269 struct LevelInfo_MM *level_mm = level->native_mm_level;
2272 for (j = 0; j < level->num_mm_ball_contents; j++)
2273 level->mm_ball_content[j] =
2274 map_element_MM_to_RND(level_mm->ball_content[j]);
2277 // never initialize clipboard elements after the very first time
2278 // (to be able to use clipboard elements between several levels)
2279 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2282 if (IS_ENVELOPE(element))
2284 int envelope_nr = element - EL_ENVELOPE_1;
2286 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2288 level->envelope[envelope_nr] = xx_envelope;
2291 if (IS_CUSTOM_ELEMENT(element) ||
2292 IS_GROUP_ELEMENT(element) ||
2293 IS_INTERNAL_ELEMENT(element))
2295 xx_ei = *ei; // copy element data into temporary buffer
2297 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2302 setElementChangePages(ei, 1);
2303 setElementChangeInfoToDefaults(ei->change);
2305 if (IS_CUSTOM_ELEMENT(element) ||
2306 IS_GROUP_ELEMENT(element))
2308 setElementDescriptionToDefault(ei);
2310 ei->modified_settings = FALSE;
2313 if (IS_CUSTOM_ELEMENT(element) ||
2314 IS_INTERNAL_ELEMENT(element))
2316 // internal values used in level editor
2318 ei->access_type = 0;
2319 ei->access_layer = 0;
2320 ei->access_protected = 0;
2321 ei->walk_to_action = 0;
2322 ei->smash_targets = 0;
2325 ei->can_explode_by_fire = FALSE;
2326 ei->can_explode_smashed = FALSE;
2327 ei->can_explode_impact = FALSE;
2329 ei->current_change_page = 0;
2332 if (IS_GROUP_ELEMENT(element) ||
2333 IS_INTERNAL_ELEMENT(element))
2335 struct ElementGroupInfo *group;
2337 // initialize memory for list of elements in group
2338 if (ei->group == NULL)
2339 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2343 xx_group = *group; // copy group data into temporary buffer
2345 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2350 if (IS_EMPTY_ELEMENT(element) ||
2351 IS_INTERNAL_ELEMENT(element))
2353 xx_ei = *ei; // copy element data into temporary buffer
2355 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2361 clipboard_elements_initialized = TRUE;
2364 static void setLevelInfoToDefaults(struct LevelInfo *level,
2365 boolean level_info_only,
2366 boolean reset_file_status)
2368 setLevelInfoToDefaults_Level(level);
2370 if (!level_info_only)
2371 setLevelInfoToDefaults_Elements(level);
2373 if (reset_file_status)
2375 level->no_valid_file = FALSE;
2376 level->no_level_file = FALSE;
2379 level->changed = FALSE;
2382 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2384 level_file_info->nr = 0;
2385 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2386 level_file_info->packed = FALSE;
2388 setString(&level_file_info->basename, NULL);
2389 setString(&level_file_info->filename, NULL);
2392 int getMappedElement_SB(int, boolean);
2394 static void ActivateLevelTemplate(void)
2398 if (check_special_flags("load_xsb_to_ces"))
2400 // fill smaller playfields with padding "beyond border wall" elements
2401 if (level.fieldx < level_template.fieldx ||
2402 level.fieldy < level_template.fieldy)
2404 short field[level.fieldx][level.fieldy];
2405 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2406 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2407 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2408 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2410 // copy old playfield (which is smaller than the visible area)
2411 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2412 field[x][y] = level.field[x][y];
2414 // fill new, larger playfield with "beyond border wall" elements
2415 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2416 level.field[x][y] = getMappedElement_SB('_', TRUE);
2418 // copy the old playfield to the middle of the new playfield
2419 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2420 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2422 level.fieldx = new_fieldx;
2423 level.fieldy = new_fieldy;
2427 // Currently there is no special action needed to activate the template
2428 // data, because 'element_info' property settings overwrite the original
2429 // level data, while all other variables do not change.
2431 // Exception: 'from_level_template' elements in the original level playfield
2432 // are overwritten with the corresponding elements at the same position in
2433 // playfield from the level template.
2435 for (x = 0; x < level.fieldx; x++)
2436 for (y = 0; y < level.fieldy; y++)
2437 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2438 level.field[x][y] = level_template.field[x][y];
2440 if (check_special_flags("load_xsb_to_ces"))
2442 struct LevelInfo level_backup = level;
2444 // overwrite all individual level settings from template level settings
2445 level = level_template;
2447 // restore level file info
2448 level.file_info = level_backup.file_info;
2450 // restore playfield size
2451 level.fieldx = level_backup.fieldx;
2452 level.fieldy = level_backup.fieldy;
2454 // restore playfield content
2455 for (x = 0; x < level.fieldx; x++)
2456 for (y = 0; y < level.fieldy; y++)
2457 level.field[x][y] = level_backup.field[x][y];
2459 // restore name and author from individual level
2460 strcpy(level.name, level_backup.name);
2461 strcpy(level.author, level_backup.author);
2463 // restore flag "use_custom_template"
2464 level.use_custom_template = level_backup.use_custom_template;
2468 static boolean checkForPackageFromBasename_BD(char *basename)
2470 // check for native BD level file extensions
2471 if (!strSuffixLower(basename, ".bd") &&
2472 !strSuffixLower(basename, ".bdr") &&
2473 !strSuffixLower(basename, ".brc") &&
2474 !strSuffixLower(basename, ".gds"))
2477 // check for standard single-level BD files (like "001.bd")
2478 if (strSuffixLower(basename, ".bd") &&
2479 strlen(basename) == 6 &&
2480 basename[0] >= '0' && basename[0] <= '9' &&
2481 basename[1] >= '0' && basename[1] <= '9' &&
2482 basename[2] >= '0' && basename[2] <= '9')
2485 // this is a level package in native BD file format
2489 static char *getLevelFilenameFromBasename(char *basename)
2491 static char *filename = NULL;
2493 checked_free(filename);
2495 filename = getPath2(getCurrentLevelDir(), basename);
2500 static int getFileTypeFromBasename(char *basename)
2502 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2504 static char *filename = NULL;
2505 struct stat file_status;
2507 // ---------- try to determine file type from filename ----------
2509 // check for typical filename of a Supaplex level package file
2510 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2511 return LEVEL_FILE_TYPE_SP;
2513 // check for typical filename of a Diamond Caves II level package file
2514 if (strSuffixLower(basename, ".dc") ||
2515 strSuffixLower(basename, ".dc2"))
2516 return LEVEL_FILE_TYPE_DC;
2518 // check for typical filename of a Sokoban level package file
2519 if (strSuffixLower(basename, ".xsb") &&
2520 strchr(basename, '%') == NULL)
2521 return LEVEL_FILE_TYPE_SB;
2523 // check for typical filename of a Boulder Dash (GDash) level package file
2524 if (checkForPackageFromBasename_BD(basename))
2525 return LEVEL_FILE_TYPE_BD;
2527 // ---------- try to determine file type from filesize ----------
2529 checked_free(filename);
2530 filename = getPath2(getCurrentLevelDir(), basename);
2532 if (stat(filename, &file_status) == 0)
2534 // check for typical filesize of a Supaplex level package file
2535 if (file_status.st_size == 170496)
2536 return LEVEL_FILE_TYPE_SP;
2539 return LEVEL_FILE_TYPE_UNKNOWN;
2542 static int getFileTypeFromMagicBytes(char *filename, int type)
2546 if ((file = openFile(filename, MODE_READ)))
2548 char chunk_name[CHUNK_ID_LEN + 1];
2550 getFileChunkBE(file, chunk_name, NULL);
2552 if (strEqual(chunk_name, "MMII") ||
2553 strEqual(chunk_name, "MIRR"))
2554 type = LEVEL_FILE_TYPE_MM;
2562 static boolean checkForPackageFromBasename(char *basename)
2564 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2565 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2567 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2570 static char *getSingleLevelBasenameExt(int nr, char *extension)
2572 static char basename[MAX_FILENAME_LEN];
2575 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2577 sprintf(basename, "%03d.%s", nr, extension);
2582 static char *getSingleLevelBasename(int nr)
2584 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2587 static char *getPackedLevelBasename(int type)
2589 static char basename[MAX_FILENAME_LEN];
2590 char *directory = getCurrentLevelDir();
2592 DirectoryEntry *dir_entry;
2594 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2596 if ((dir = openDirectory(directory)) == NULL)
2598 Warn("cannot read current level directory '%s'", directory);
2603 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2605 char *entry_basename = dir_entry->basename;
2606 int entry_type = getFileTypeFromBasename(entry_basename);
2608 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2610 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2613 strcpy(basename, entry_basename);
2620 closeDirectory(dir);
2625 static char *getSingleLevelFilename(int nr)
2627 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2630 #if ENABLE_UNUSED_CODE
2631 static char *getPackedLevelFilename(int type)
2633 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2637 char *getDefaultLevelFilename(int nr)
2639 return getSingleLevelFilename(nr);
2642 #if ENABLE_UNUSED_CODE
2643 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2647 lfi->packed = FALSE;
2649 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2650 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2654 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2655 int type, char *format, ...)
2657 static char basename[MAX_FILENAME_LEN];
2660 va_start(ap, format);
2661 vsprintf(basename, format, ap);
2665 lfi->packed = FALSE;
2667 setString(&lfi->basename, basename);
2668 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2671 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2677 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2678 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2681 static int getFiletypeFromID(char *filetype_id)
2683 char *filetype_id_lower;
2684 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2687 if (filetype_id == NULL)
2688 return LEVEL_FILE_TYPE_UNKNOWN;
2690 filetype_id_lower = getStringToLower(filetype_id);
2692 for (i = 0; filetype_id_list[i].id != NULL; i++)
2694 char *id_lower = getStringToLower(filetype_id_list[i].id);
2696 if (strEqual(filetype_id_lower, id_lower))
2697 filetype = filetype_id_list[i].filetype;
2701 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2705 free(filetype_id_lower);
2710 char *getLocalLevelTemplateFilename(void)
2712 return getDefaultLevelFilename(-1);
2715 char *getGlobalLevelTemplateFilename(void)
2717 // global variable "leveldir_current" must be modified in the loop below
2718 LevelDirTree *leveldir_current_last = leveldir_current;
2719 char *filename = NULL;
2721 // check for template level in path from current to topmost tree node
2723 while (leveldir_current != NULL)
2725 filename = getDefaultLevelFilename(-1);
2727 if (fileExists(filename))
2730 leveldir_current = leveldir_current->node_parent;
2733 // restore global variable "leveldir_current" modified in above loop
2734 leveldir_current = leveldir_current_last;
2739 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2743 // special case: level number is negative => check for level template file
2746 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2747 getSingleLevelBasename(-1));
2749 // replace local level template filename with global template filename
2750 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2752 // no fallback if template file not existing
2756 // special case: check for file name/pattern specified in "levelinfo.conf"
2757 if (leveldir_current->level_filename != NULL)
2759 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2761 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2762 leveldir_current->level_filename, nr);
2764 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2766 if (fileExists(lfi->filename))
2769 else if (leveldir_current->level_filetype != NULL)
2771 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2773 // check for specified native level file with standard file name
2774 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2775 "%03d.%s", nr, LEVELFILE_EXTENSION);
2776 if (fileExists(lfi->filename))
2780 // check for native Rocks'n'Diamonds level file
2781 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2782 "%03d.%s", nr, LEVELFILE_EXTENSION);
2783 if (fileExists(lfi->filename))
2786 // check for native Boulder Dash level file
2787 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2788 if (fileExists(lfi->filename))
2791 // check for Emerald Mine level file (V1)
2792 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2793 'a' + (nr / 10) % 26, '0' + nr % 10);
2794 if (fileExists(lfi->filename))
2796 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2797 'A' + (nr / 10) % 26, '0' + nr % 10);
2798 if (fileExists(lfi->filename))
2801 // check for Emerald Mine level file (V2 to V5)
2802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2803 if (fileExists(lfi->filename))
2806 // check for Emerald Mine level file (V6 / single mode)
2807 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2808 if (fileExists(lfi->filename))
2810 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2811 if (fileExists(lfi->filename))
2814 // check for Emerald Mine level file (V6 / teamwork mode)
2815 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2816 if (fileExists(lfi->filename))
2818 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2819 if (fileExists(lfi->filename))
2822 // check for various packed level file formats
2823 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2824 if (fileExists(lfi->filename))
2827 // no known level file found -- use default values (and fail later)
2828 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2829 "%03d.%s", nr, LEVELFILE_EXTENSION);
2832 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2834 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2835 lfi->type = getFileTypeFromBasename(lfi->basename);
2837 if (lfi->type == LEVEL_FILE_TYPE_RND)
2838 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2841 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2843 // always start with reliable default values
2844 setFileInfoToDefaults(level_file_info);
2846 level_file_info->nr = nr; // set requested level number
2848 determineLevelFileInfo_Filename(level_file_info);
2849 determineLevelFileInfo_Filetype(level_file_info);
2852 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2853 struct LevelFileInfo *lfi_to)
2855 lfi_to->nr = lfi_from->nr;
2856 lfi_to->type = lfi_from->type;
2857 lfi_to->packed = lfi_from->packed;
2859 setString(&lfi_to->basename, lfi_from->basename);
2860 setString(&lfi_to->filename, lfi_from->filename);
2863 // ----------------------------------------------------------------------------
2864 // functions for loading R'n'D level
2865 // ----------------------------------------------------------------------------
2867 int getMappedElement(int element)
2869 // remap some (historic, now obsolete) elements
2873 case EL_PLAYER_OBSOLETE:
2874 element = EL_PLAYER_1;
2877 case EL_KEY_OBSOLETE:
2881 case EL_EM_KEY_1_FILE_OBSOLETE:
2882 element = EL_EM_KEY_1;
2885 case EL_EM_KEY_2_FILE_OBSOLETE:
2886 element = EL_EM_KEY_2;
2889 case EL_EM_KEY_3_FILE_OBSOLETE:
2890 element = EL_EM_KEY_3;
2893 case EL_EM_KEY_4_FILE_OBSOLETE:
2894 element = EL_EM_KEY_4;
2897 case EL_ENVELOPE_OBSOLETE:
2898 element = EL_ENVELOPE_1;
2906 if (element >= NUM_FILE_ELEMENTS)
2908 Warn("invalid level element %d", element);
2910 element = EL_UNKNOWN;
2918 static int getMappedElementByVersion(int element, int game_version)
2920 // remap some elements due to certain game version
2922 if (game_version <= VERSION_IDENT(2,2,0,0))
2924 // map game font elements
2925 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2926 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2927 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2928 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2931 if (game_version < VERSION_IDENT(3,0,0,0))
2933 // map Supaplex gravity tube elements
2934 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2935 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2936 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2937 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2944 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2946 level->file_version = getFileVersion(file);
2947 level->game_version = getFileVersion(file);
2952 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2954 level->creation_date.year = getFile16BitBE(file);
2955 level->creation_date.month = getFile8Bit(file);
2956 level->creation_date.day = getFile8Bit(file);
2958 level->creation_date.src = DATE_SRC_LEVELFILE;
2963 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2965 int initial_player_stepsize;
2966 int initial_player_gravity;
2969 level->fieldx = getFile8Bit(file);
2970 level->fieldy = getFile8Bit(file);
2972 level->time = getFile16BitBE(file);
2973 level->gems_needed = getFile16BitBE(file);
2975 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2976 level->name[i] = getFile8Bit(file);
2977 level->name[MAX_LEVEL_NAME_LEN] = 0;
2979 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2980 level->score[i] = getFile8Bit(file);
2982 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2983 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2984 for (y = 0; y < 3; y++)
2985 for (x = 0; x < 3; x++)
2986 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2988 level->amoeba_speed = getFile8Bit(file);
2989 level->time_magic_wall = getFile8Bit(file);
2990 level->time_wheel = getFile8Bit(file);
2991 level->amoeba_content = getMappedElement(getFile8Bit(file));
2993 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2996 for (i = 0; i < MAX_PLAYERS; i++)
2997 level->initial_player_stepsize[i] = initial_player_stepsize;
2999 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3001 for (i = 0; i < MAX_PLAYERS; i++)
3002 level->initial_player_gravity[i] = initial_player_gravity;
3004 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3005 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3007 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3009 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3010 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3011 level->can_move_into_acid_bits = getFile32BitBE(file);
3012 level->dont_collide_with_bits = getFile8Bit(file);
3014 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3015 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3017 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3018 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3019 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3021 level->game_engine_type = getFile8Bit(file);
3023 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3028 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3032 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3033 level->name[i] = getFile8Bit(file);
3034 level->name[MAX_LEVEL_NAME_LEN] = 0;
3039 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3043 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3044 level->author[i] = getFile8Bit(file);
3045 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3050 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3053 int chunk_size_expected = level->fieldx * level->fieldy;
3055 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3056 stored with 16-bit encoding (and should be twice as big then).
3057 Even worse, playfield data was stored 16-bit when only yamyam content
3058 contained 16-bit elements and vice versa. */
3060 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3061 chunk_size_expected *= 2;
3063 if (chunk_size_expected != chunk_size)
3065 ReadUnusedBytesFromFile(file, chunk_size);
3066 return chunk_size_expected;
3069 for (y = 0; y < level->fieldy; y++)
3070 for (x = 0; x < level->fieldx; x++)
3071 level->field[x][y] =
3072 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3077 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3080 int header_size = 4;
3081 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3082 int chunk_size_expected = header_size + content_size;
3084 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3085 stored with 16-bit encoding (and should be twice as big then).
3086 Even worse, playfield data was stored 16-bit when only yamyam content
3087 contained 16-bit elements and vice versa. */
3089 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3090 chunk_size_expected += content_size;
3092 if (chunk_size_expected != chunk_size)
3094 ReadUnusedBytesFromFile(file, chunk_size);
3095 return chunk_size_expected;
3099 level->num_yamyam_contents = getFile8Bit(file);
3103 // correct invalid number of content fields -- should never happen
3104 if (level->num_yamyam_contents < 1 ||
3105 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3106 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3108 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3109 for (y = 0; y < 3; y++)
3110 for (x = 0; x < 3; x++)
3111 level->yamyam_content[i].e[x][y] =
3112 getMappedElement(level->encoding_16bit_field ?
3113 getFile16BitBE(file) : getFile8Bit(file));
3117 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3122 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3124 element = getMappedElement(getFile16BitBE(file));
3125 num_contents = getFile8Bit(file);
3127 getFile8Bit(file); // content x size (unused)
3128 getFile8Bit(file); // content y size (unused)
3130 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3132 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3133 for (y = 0; y < 3; y++)
3134 for (x = 0; x < 3; x++)
3135 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3137 // correct invalid number of content fields -- should never happen
3138 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3139 num_contents = STD_ELEMENT_CONTENTS;
3141 if (element == EL_YAMYAM)
3143 level->num_yamyam_contents = num_contents;
3145 for (i = 0; i < num_contents; i++)
3146 for (y = 0; y < 3; y++)
3147 for (x = 0; x < 3; x++)
3148 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3150 else if (element == EL_BD_AMOEBA)
3152 level->amoeba_content = content_array[0][0][0];
3156 Warn("cannot load content for element '%d'", element);
3162 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3168 int chunk_size_expected;
3170 element = getMappedElement(getFile16BitBE(file));
3171 if (!IS_ENVELOPE(element))
3172 element = EL_ENVELOPE_1;
3174 envelope_nr = element - EL_ENVELOPE_1;
3176 envelope_len = getFile16BitBE(file);
3178 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3179 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3181 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3183 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3184 if (chunk_size_expected != chunk_size)
3186 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3187 return chunk_size_expected;
3190 for (i = 0; i < envelope_len; i++)
3191 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3196 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3198 int num_changed_custom_elements = getFile16BitBE(file);
3199 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3202 if (chunk_size_expected != chunk_size)
3204 ReadUnusedBytesFromFile(file, chunk_size - 2);
3205 return chunk_size_expected;
3208 for (i = 0; i < num_changed_custom_elements; i++)
3210 int element = getMappedElement(getFile16BitBE(file));
3211 int properties = getFile32BitBE(file);
3213 if (IS_CUSTOM_ELEMENT(element))
3214 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3216 Warn("invalid custom element number %d", element);
3218 // older game versions that wrote level files with CUS1 chunks used
3219 // different default push delay values (not yet stored in level file)
3220 element_info[element].push_delay_fixed = 2;
3221 element_info[element].push_delay_random = 8;
3224 level->file_has_custom_elements = TRUE;
3229 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3231 int num_changed_custom_elements = getFile16BitBE(file);
3232 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3235 if (chunk_size_expected != chunk_size)
3237 ReadUnusedBytesFromFile(file, chunk_size - 2);
3238 return chunk_size_expected;
3241 for (i = 0; i < num_changed_custom_elements; i++)
3243 int element = getMappedElement(getFile16BitBE(file));
3244 int custom_target_element = getMappedElement(getFile16BitBE(file));
3246 if (IS_CUSTOM_ELEMENT(element))
3247 element_info[element].change->target_element = custom_target_element;
3249 Warn("invalid custom element number %d", element);
3252 level->file_has_custom_elements = TRUE;
3257 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3259 int num_changed_custom_elements = getFile16BitBE(file);
3260 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3263 if (chunk_size_expected != chunk_size)
3265 ReadUnusedBytesFromFile(file, chunk_size - 2);
3266 return chunk_size_expected;
3269 for (i = 0; i < num_changed_custom_elements; i++)
3271 int element = getMappedElement(getFile16BitBE(file));
3272 struct ElementInfo *ei = &element_info[element];
3273 unsigned int event_bits;
3275 if (!IS_CUSTOM_ELEMENT(element))
3277 Warn("invalid custom element number %d", element);
3279 element = EL_INTERNAL_DUMMY;
3282 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3283 ei->description[j] = getFile8Bit(file);
3284 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3286 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3288 // some free bytes for future properties and padding
3289 ReadUnusedBytesFromFile(file, 7);
3291 ei->use_gfx_element = getFile8Bit(file);
3292 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3294 ei->collect_score_initial = getFile8Bit(file);
3295 ei->collect_count_initial = getFile8Bit(file);
3297 ei->push_delay_fixed = getFile16BitBE(file);
3298 ei->push_delay_random = getFile16BitBE(file);
3299 ei->move_delay_fixed = getFile16BitBE(file);
3300 ei->move_delay_random = getFile16BitBE(file);
3302 ei->move_pattern = getFile16BitBE(file);
3303 ei->move_direction_initial = getFile8Bit(file);
3304 ei->move_stepsize = getFile8Bit(file);
3306 for (y = 0; y < 3; y++)
3307 for (x = 0; x < 3; x++)
3308 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3310 // bits 0 - 31 of "has_event[]"
3311 event_bits = getFile32BitBE(file);
3312 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3313 if (event_bits & (1u << j))
3314 ei->change->has_event[j] = TRUE;
3316 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3318 ei->change->delay_fixed = getFile16BitBE(file);
3319 ei->change->delay_random = getFile16BitBE(file);
3320 ei->change->delay_frames = getFile16BitBE(file);
3322 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3324 ei->change->explode = getFile8Bit(file);
3325 ei->change->use_target_content = getFile8Bit(file);
3326 ei->change->only_if_complete = getFile8Bit(file);
3327 ei->change->use_random_replace = getFile8Bit(file);
3329 ei->change->random_percentage = getFile8Bit(file);
3330 ei->change->replace_when = getFile8Bit(file);
3332 for (y = 0; y < 3; y++)
3333 for (x = 0; x < 3; x++)
3334 ei->change->target_content.e[x][y] =
3335 getMappedElement(getFile16BitBE(file));
3337 ei->slippery_type = getFile8Bit(file);
3339 // some free bytes for future properties and padding
3340 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3342 // mark that this custom element has been modified
3343 ei->modified_settings = TRUE;
3346 level->file_has_custom_elements = TRUE;
3351 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3353 struct ElementInfo *ei;
3354 int chunk_size_expected;
3358 // ---------- custom element base property values (96 bytes) ----------------
3360 element = getMappedElement(getFile16BitBE(file));
3362 if (!IS_CUSTOM_ELEMENT(element))
3364 Warn("invalid custom element number %d", element);
3366 ReadUnusedBytesFromFile(file, chunk_size - 2);
3371 ei = &element_info[element];
3373 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3374 ei->description[i] = getFile8Bit(file);
3375 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3377 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3379 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3381 ei->num_change_pages = getFile8Bit(file);
3383 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3384 if (chunk_size_expected != chunk_size)
3386 ReadUnusedBytesFromFile(file, chunk_size - 43);
3387 return chunk_size_expected;
3390 ei->ce_value_fixed_initial = getFile16BitBE(file);
3391 ei->ce_value_random_initial = getFile16BitBE(file);
3392 ei->use_last_ce_value = getFile8Bit(file);
3394 ei->use_gfx_element = getFile8Bit(file);
3395 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3397 ei->collect_score_initial = getFile8Bit(file);
3398 ei->collect_count_initial = getFile8Bit(file);
3400 ei->drop_delay_fixed = getFile8Bit(file);
3401 ei->push_delay_fixed = getFile8Bit(file);
3402 ei->drop_delay_random = getFile8Bit(file);
3403 ei->push_delay_random = getFile8Bit(file);
3404 ei->move_delay_fixed = getFile16BitBE(file);
3405 ei->move_delay_random = getFile16BitBE(file);
3407 // bits 0 - 15 of "move_pattern" ...
3408 ei->move_pattern = getFile16BitBE(file);
3409 ei->move_direction_initial = getFile8Bit(file);
3410 ei->move_stepsize = getFile8Bit(file);
3412 ei->slippery_type = getFile8Bit(file);
3414 for (y = 0; y < 3; y++)
3415 for (x = 0; x < 3; x++)
3416 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3418 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3419 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3420 ei->move_leave_type = getFile8Bit(file);
3422 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3423 ei->move_pattern |= (getFile16BitBE(file) << 16);
3425 ei->access_direction = getFile8Bit(file);
3427 ei->explosion_delay = getFile8Bit(file);
3428 ei->ignition_delay = getFile8Bit(file);
3429 ei->explosion_type = getFile8Bit(file);
3431 // some free bytes for future custom property values and padding
3432 ReadUnusedBytesFromFile(file, 1);
3434 // ---------- change page property values (48 bytes) ------------------------
3436 setElementChangePages(ei, ei->num_change_pages);
3438 for (i = 0; i < ei->num_change_pages; i++)
3440 struct ElementChangeInfo *change = &ei->change_page[i];
3441 unsigned int event_bits;
3443 // always start with reliable default values
3444 setElementChangeInfoToDefaults(change);
3446 // bits 0 - 31 of "has_event[]" ...
3447 event_bits = getFile32BitBE(file);
3448 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3449 if (event_bits & (1u << j))
3450 change->has_event[j] = TRUE;
3452 change->target_element = getMappedElement(getFile16BitBE(file));
3454 change->delay_fixed = getFile16BitBE(file);
3455 change->delay_random = getFile16BitBE(file);
3456 change->delay_frames = getFile16BitBE(file);
3458 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3460 change->explode = getFile8Bit(file);
3461 change->use_target_content = getFile8Bit(file);
3462 change->only_if_complete = getFile8Bit(file);
3463 change->use_random_replace = getFile8Bit(file);
3465 change->random_percentage = getFile8Bit(file);
3466 change->replace_when = getFile8Bit(file);
3468 for (y = 0; y < 3; y++)
3469 for (x = 0; x < 3; x++)
3470 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3472 change->can_change = getFile8Bit(file);
3474 change->trigger_side = getFile8Bit(file);
3476 change->trigger_player = getFile8Bit(file);
3477 change->trigger_page = getFile8Bit(file);
3479 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3480 CH_PAGE_ANY : (1 << change->trigger_page));
3482 change->has_action = getFile8Bit(file);
3483 change->action_type = getFile8Bit(file);
3484 change->action_mode = getFile8Bit(file);
3485 change->action_arg = getFile16BitBE(file);
3487 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3488 event_bits = getFile8Bit(file);
3489 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3490 if (event_bits & (1u << (j - 32)))
3491 change->has_event[j] = TRUE;
3494 // mark this custom element as modified
3495 ei->modified_settings = TRUE;
3497 level->file_has_custom_elements = TRUE;
3502 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3504 struct ElementInfo *ei;
3505 struct ElementGroupInfo *group;
3509 element = getMappedElement(getFile16BitBE(file));
3511 if (!IS_GROUP_ELEMENT(element))
3513 Warn("invalid group element number %d", element);
3515 ReadUnusedBytesFromFile(file, chunk_size - 2);
3520 ei = &element_info[element];
3522 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3523 ei->description[i] = getFile8Bit(file);
3524 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3526 group = element_info[element].group;
3528 group->num_elements = getFile8Bit(file);
3530 ei->use_gfx_element = getFile8Bit(file);
3531 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3533 group->choice_mode = getFile8Bit(file);
3535 // some free bytes for future values and padding
3536 ReadUnusedBytesFromFile(file, 3);
3538 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3539 group->element[i] = getMappedElement(getFile16BitBE(file));
3541 // mark this group element as modified
3542 element_info[element].modified_settings = TRUE;
3544 level->file_has_custom_elements = TRUE;
3549 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3550 int element, int real_element)
3552 int micro_chunk_size = 0;
3553 int conf_type = getFile8Bit(file);
3554 int byte_mask = conf_type & CONF_MASK_BYTES;
3555 boolean element_found = FALSE;
3558 micro_chunk_size += 1;
3560 if (byte_mask == CONF_MASK_MULTI_BYTES)
3562 int num_bytes = getFile16BitBE(file);
3563 byte *buffer = checked_malloc(num_bytes);
3565 ReadBytesFromFile(file, buffer, num_bytes);
3567 for (i = 0; conf[i].data_type != -1; i++)
3569 if (conf[i].element == element &&
3570 conf[i].conf_type == conf_type)
3572 int data_type = conf[i].data_type;
3573 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3574 int max_num_entities = conf[i].max_num_entities;
3576 if (num_entities > max_num_entities)
3578 Warn("truncating number of entities for element %d from %d to %d",
3579 element, num_entities, max_num_entities);
3581 num_entities = max_num_entities;
3584 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3585 data_type == TYPE_CONTENT_LIST))
3587 // for element and content lists, zero entities are not allowed
3588 Warn("found empty list of entities for element %d", element);
3590 // do not set "num_entities" here to prevent reading behind buffer
3592 *(int *)(conf[i].num_entities) = 1; // at least one is required
3596 *(int *)(conf[i].num_entities) = num_entities;
3599 element_found = TRUE;
3601 if (data_type == TYPE_STRING)
3603 char *string = (char *)(conf[i].value);
3606 for (j = 0; j < max_num_entities; j++)
3607 string[j] = (j < num_entities ? buffer[j] : '\0');
3609 else if (data_type == TYPE_ELEMENT_LIST)
3611 int *element_array = (int *)(conf[i].value);
3614 for (j = 0; j < num_entities; j++)
3616 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3618 else if (data_type == TYPE_CONTENT_LIST)
3620 struct Content *content= (struct Content *)(conf[i].value);
3623 for (c = 0; c < num_entities; c++)
3624 for (y = 0; y < 3; y++)
3625 for (x = 0; x < 3; x++)
3626 content[c].e[x][y] =
3627 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3630 element_found = FALSE;
3636 checked_free(buffer);
3638 micro_chunk_size += 2 + num_bytes;
3640 else // constant size configuration data (1, 2 or 4 bytes)
3642 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3643 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3644 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3646 for (i = 0; conf[i].data_type != -1; i++)
3648 if (conf[i].element == element &&
3649 conf[i].conf_type == conf_type)
3651 int data_type = conf[i].data_type;
3653 if (data_type == TYPE_ELEMENT)
3654 value = getMappedElement(value);
3656 if (data_type == TYPE_BOOLEAN)
3657 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3659 *(int *) (conf[i].value) = value;
3661 element_found = TRUE;
3667 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3672 char *error_conf_chunk_bytes =
3673 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3674 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3675 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3676 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3677 int error_element = real_element;
3679 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3680 error_conf_chunk_bytes, error_conf_chunk_token,
3681 error_element, EL_NAME(error_element));
3684 return micro_chunk_size;
3687 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3689 int real_chunk_size = 0;
3691 li = *level; // copy level data into temporary buffer
3693 while (!checkEndOfFile(file))
3695 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3697 if (real_chunk_size >= chunk_size)
3701 *level = li; // copy temporary buffer back to level data
3703 return real_chunk_size;
3706 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3708 int real_chunk_size = 0;
3710 li = *level; // copy level data into temporary buffer
3712 while (!checkEndOfFile(file))
3714 int element = getMappedElement(getFile16BitBE(file));
3716 real_chunk_size += 2;
3717 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3719 if (real_chunk_size >= chunk_size)
3723 *level = li; // copy temporary buffer back to level data
3725 return real_chunk_size;
3728 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3730 int real_chunk_size = 0;
3732 li = *level; // copy level data into temporary buffer
3734 while (!checkEndOfFile(file))
3736 int element = getMappedElement(getFile16BitBE(file));
3738 real_chunk_size += 2;
3739 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3741 if (real_chunk_size >= chunk_size)
3745 *level = li; // copy temporary buffer back to level data
3747 return real_chunk_size;
3750 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3752 int element = getMappedElement(getFile16BitBE(file));
3753 int envelope_nr = element - EL_ENVELOPE_1;
3754 int real_chunk_size = 2;
3756 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3758 while (!checkEndOfFile(file))
3760 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3763 if (real_chunk_size >= chunk_size)
3767 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3769 return real_chunk_size;
3772 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3774 int element = getMappedElement(getFile16BitBE(file));
3775 int real_chunk_size = 2;
3776 struct ElementInfo *ei = &element_info[element];
3779 xx_ei = *ei; // copy element data into temporary buffer
3781 xx_ei.num_change_pages = -1;
3783 while (!checkEndOfFile(file))
3785 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3787 if (xx_ei.num_change_pages != -1)
3790 if (real_chunk_size >= chunk_size)
3796 if (ei->num_change_pages == -1)
3798 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3801 ei->num_change_pages = 1;
3803 setElementChangePages(ei, 1);
3804 setElementChangeInfoToDefaults(ei->change);
3806 return real_chunk_size;
3809 // initialize number of change pages stored for this custom element
3810 setElementChangePages(ei, ei->num_change_pages);
3811 for (i = 0; i < ei->num_change_pages; i++)
3812 setElementChangeInfoToDefaults(&ei->change_page[i]);
3814 // start with reading properties for the first change page
3815 xx_current_change_page = 0;
3817 while (!checkEndOfFile(file))
3819 // level file might contain invalid change page number
3820 if (xx_current_change_page >= ei->num_change_pages)
3823 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3825 xx_change = *change; // copy change data into temporary buffer
3827 resetEventBits(); // reset bits; change page might have changed
3829 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3832 *change = xx_change;
3834 setEventFlagsFromEventBits(change);
3836 if (real_chunk_size >= chunk_size)
3840 level->file_has_custom_elements = TRUE;
3842 return real_chunk_size;
3845 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3847 int element = getMappedElement(getFile16BitBE(file));
3848 int real_chunk_size = 2;
3849 struct ElementInfo *ei = &element_info[element];
3850 struct ElementGroupInfo *group = ei->group;
3855 xx_ei = *ei; // copy element data into temporary buffer
3856 xx_group = *group; // copy group data into temporary buffer
3858 while (!checkEndOfFile(file))
3860 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3863 if (real_chunk_size >= chunk_size)
3870 level->file_has_custom_elements = TRUE;
3872 return real_chunk_size;
3875 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3877 int element = getMappedElement(getFile16BitBE(file));
3878 int real_chunk_size = 2;
3879 struct ElementInfo *ei = &element_info[element];
3881 xx_ei = *ei; // copy element data into temporary buffer
3883 while (!checkEndOfFile(file))
3885 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3888 if (real_chunk_size >= chunk_size)
3894 level->file_has_custom_elements = TRUE;
3896 return real_chunk_size;
3899 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3900 struct LevelFileInfo *level_file_info,
3901 boolean level_info_only)
3903 char *filename = level_file_info->filename;
3904 char cookie[MAX_LINE_LEN];
3905 char chunk_name[CHUNK_ID_LEN + 1];
3909 if (!(file = openFile(filename, MODE_READ)))
3911 level->no_valid_file = TRUE;
3912 level->no_level_file = TRUE;
3914 if (level_info_only)
3917 Warn("cannot read level '%s' -- using empty level", filename);
3919 if (!setup.editor.use_template_for_new_levels)
3922 // if level file not found, try to initialize level data from template
3923 filename = getGlobalLevelTemplateFilename();
3925 if (!(file = openFile(filename, MODE_READ)))
3928 // default: for empty levels, use level template for custom elements
3929 level->use_custom_template = TRUE;
3931 level->no_valid_file = FALSE;
3934 getFileChunkBE(file, chunk_name, NULL);
3935 if (strEqual(chunk_name, "RND1"))
3937 getFile32BitBE(file); // not used
3939 getFileChunkBE(file, chunk_name, NULL);
3940 if (!strEqual(chunk_name, "CAVE"))
3942 level->no_valid_file = TRUE;
3944 Warn("unknown format of level file '%s'", filename);
3951 else // check for pre-2.0 file format with cookie string
3953 strcpy(cookie, chunk_name);
3954 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3956 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3957 cookie[strlen(cookie) - 1] = '\0';
3959 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3961 level->no_valid_file = TRUE;
3963 Warn("unknown format of level file '%s'", filename);
3970 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3972 level->no_valid_file = TRUE;
3974 Warn("unsupported version of level file '%s'", filename);
3981 // pre-2.0 level files have no game version, so use file version here
3982 level->game_version = level->file_version;
3985 if (level->file_version < FILE_VERSION_1_2)
3987 // level files from versions before 1.2.0 without chunk structure
3988 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3989 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3997 int (*loader)(File *, int, struct LevelInfo *);
4001 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4002 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4003 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4004 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4005 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4006 { "INFO", -1, LoadLevel_INFO },
4007 { "BODY", -1, LoadLevel_BODY },
4008 { "CONT", -1, LoadLevel_CONT },
4009 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4010 { "CNT3", -1, LoadLevel_CNT3 },
4011 { "CUS1", -1, LoadLevel_CUS1 },
4012 { "CUS2", -1, LoadLevel_CUS2 },
4013 { "CUS3", -1, LoadLevel_CUS3 },
4014 { "CUS4", -1, LoadLevel_CUS4 },
4015 { "GRP1", -1, LoadLevel_GRP1 },
4016 { "CONF", -1, LoadLevel_CONF },
4017 { "ELEM", -1, LoadLevel_ELEM },
4018 { "NOTE", -1, LoadLevel_NOTE },
4019 { "CUSX", -1, LoadLevel_CUSX },
4020 { "GRPX", -1, LoadLevel_GRPX },
4021 { "EMPX", -1, LoadLevel_EMPX },
4026 while (getFileChunkBE(file, chunk_name, &chunk_size))
4030 while (chunk_info[i].name != NULL &&
4031 !strEqual(chunk_name, chunk_info[i].name))
4034 if (chunk_info[i].name == NULL)
4036 Warn("unknown chunk '%s' in level file '%s'",
4037 chunk_name, filename);
4039 ReadUnusedBytesFromFile(file, chunk_size);
4041 else if (chunk_info[i].size != -1 &&
4042 chunk_info[i].size != chunk_size)
4044 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4045 chunk_size, chunk_name, filename);
4047 ReadUnusedBytesFromFile(file, chunk_size);
4051 // call function to load this level chunk
4052 int chunk_size_expected =
4053 (chunk_info[i].loader)(file, chunk_size, level);
4055 if (chunk_size_expected < 0)
4057 Warn("error reading chunk '%s' in level file '%s'",
4058 chunk_name, filename);
4063 // the size of some chunks cannot be checked before reading other
4064 // chunks first (like "HEAD" and "BODY") that contain some header
4065 // information, so check them here
4066 if (chunk_size_expected != chunk_size)
4068 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4069 chunk_size, chunk_name, filename);
4081 // ----------------------------------------------------------------------------
4082 // functions for loading BD level
4083 // ----------------------------------------------------------------------------
4085 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4087 struct LevelInfo_BD *level_bd = level->native_bd_level;
4088 GdCave *cave = NULL; // will be changed below
4089 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4090 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4093 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4095 // cave and map newly allocated when set to defaults above
4096 cave = level_bd->cave;
4099 cave->intermission = level->bd_intermission;
4102 cave->level_time[0] = level->time;
4103 cave->level_diamonds[0] = level->gems_needed;
4106 cave->scheduling = level->bd_scheduling_type;
4107 cave->pal_timing = level->bd_pal_timing;
4108 cave->level_speed[0] = level->bd_cycle_delay_ms;
4109 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4110 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4111 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4114 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4115 cave->diamond_value = level->score[SC_EMERALD];
4116 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4118 // compatibility settings
4119 cave->lineshift = level->bd_line_shifting_borders;
4120 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4121 cave->short_explosions = level->bd_short_explosions;
4122 cave->gravity_affects_all = level->bd_gravity_affects_all;
4124 // player properties
4125 cave->diagonal_movements = level->bd_diagonal_movements;
4126 cave->active_is_first_found = level->bd_topmost_player_active;
4127 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4128 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4129 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4130 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4132 // element properties
4133 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4134 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4135 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4136 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4137 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4138 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4139 cave->level_magic_wall_time[0] = level->time_magic_wall;
4140 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4141 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4143 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4144 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4145 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4146 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4147 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4148 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4149 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4151 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4152 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4153 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4154 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4155 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4156 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4157 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4158 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4159 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4160 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4161 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4163 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4164 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4165 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4166 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4167 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4168 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4170 cave->slime_predictable = level->bd_slime_is_predictable;
4171 cave->slime_correct_random = level->bd_slime_correct_random;
4172 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4173 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4174 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4175 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4176 cave->slime_eats_1 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4177 cave->slime_converts_1 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4178 cave->slime_eats_2 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4179 cave->slime_converts_2 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4180 cave->slime_eats_3 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4181 cave->slime_converts_3 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4183 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4184 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4185 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4187 cave->biter_delay_frame = level->bd_biter_move_delay;
4188 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4190 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4192 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4194 cave->replicators_active = level->bd_replicators_active;
4195 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4197 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4198 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4200 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4202 cave->nut_turns_to_when_crushed = map_element_RND_to_BD_cave(level->bd_nut_content);
4204 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4205 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4206 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4208 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4209 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4211 cave->expanding_wall_looks_like = map_element_RND_to_BD_cave(level->bd_expanding_wall_looks_like);
4214 strncpy(cave->name, level->name, sizeof(GdString));
4215 cave->name[sizeof(GdString) - 1] = '\0';
4217 // playfield elements
4218 for (x = 0; x < cave->w; x++)
4219 for (y = 0; y < cave->h; y++)
4220 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4223 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4225 struct LevelInfo_BD *level_bd = level->native_bd_level;
4226 GdCave *cave = level_bd->cave;
4227 int bd_level_nr = level_bd->level_nr;
4230 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4231 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4234 level->bd_intermission = cave->intermission;
4237 level->time = cave->level_time[bd_level_nr];
4238 level->gems_needed = cave->level_diamonds[bd_level_nr];
4241 level->bd_scheduling_type = cave->scheduling;
4242 level->bd_pal_timing = cave->pal_timing;
4243 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4244 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4245 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4246 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4249 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4250 level->score[SC_EMERALD] = cave->diamond_value;
4251 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4253 // compatibility settings
4254 level->bd_line_shifting_borders = cave->lineshift;
4255 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4256 level->bd_short_explosions = cave->short_explosions;
4257 level->bd_gravity_affects_all = cave->gravity_affects_all;
4259 // player properties
4260 level->bd_diagonal_movements = cave->diagonal_movements;
4261 level->bd_topmost_player_active = cave->active_is_first_found;
4262 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4263 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4264 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4265 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4267 // element properties
4268 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4269 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4270 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4271 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4272 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4273 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4274 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4275 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4276 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4278 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4279 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4280 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4281 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4282 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4283 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4284 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4286 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4287 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4288 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4289 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4290 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4291 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4292 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4293 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4294 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4295 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4296 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4298 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4299 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4300 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4301 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4302 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4303 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4305 level->bd_slime_is_predictable = cave->slime_predictable;
4306 level->bd_slime_correct_random = cave->slime_correct_random;
4307 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4308 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4309 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4310 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4311 level->bd_slime_eats_element_1 = map_element_BD_to_RND_cave(cave->slime_eats_1);
4312 level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4313 level->bd_slime_eats_element_2 = map_element_BD_to_RND_cave(cave->slime_eats_2);
4314 level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4315 level->bd_slime_eats_element_3 = map_element_BD_to_RND_cave(cave->slime_eats_3);
4316 level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4318 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4319 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4320 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4322 level->bd_biter_move_delay = cave->biter_delay_frame;
4323 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4325 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4327 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4329 level->bd_replicators_active = cave->replicators_active;
4330 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4332 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4333 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4335 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4337 level->bd_nut_content = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4339 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4340 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4341 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4343 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4344 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4346 level->bd_expanding_wall_looks_like = map_element_BD_to_RND_cave(cave->expanding_wall_looks_like);
4349 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4351 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4352 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4354 // playfield elements
4355 for (x = 0; x < level->fieldx; x++)
4356 for (y = 0; y < level->fieldy; y++)
4357 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4359 checked_free(cave_name);
4362 static void setTapeInfoToDefaults(void);
4364 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4366 struct LevelInfo_BD *level_bd = level->native_bd_level;
4367 GdCave *cave = level_bd->cave;
4368 GdReplay *replay = level_bd->replay;
4374 // always start with reliable default values
4375 setTapeInfoToDefaults();
4377 tape.level_nr = level_nr; // (currently not used)
4378 tape.random_seed = replay->seed;
4380 TapeSetDateFromIsoDateString(replay->date);
4383 tape.pos[tape.counter].delay = 0;
4385 tape.bd_replay = TRUE;
4387 // all time calculations only used to display approximate tape time
4388 int cave_speed = cave->speed;
4389 int milliseconds_game = 0;
4390 int milliseconds_elapsed = 20;
4392 for (i = 0; i < replay->movements->len; i++)
4394 int replay_action = replay->movements->data[i];
4395 int tape_action = map_action_BD_to_RND(replay_action);
4396 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4397 boolean success = 0;
4401 success = TapeAddAction(action);
4403 milliseconds_game += milliseconds_elapsed;
4405 if (milliseconds_game >= cave_speed)
4407 milliseconds_game -= cave_speed;
4414 tape.pos[tape.counter].delay = 0;
4415 tape.pos[tape.counter].action[0] = 0;
4419 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4425 TapeHaltRecording();
4429 // ----------------------------------------------------------------------------
4430 // functions for loading EM level
4431 // ----------------------------------------------------------------------------
4433 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4435 static int ball_xy[8][2] =
4446 struct LevelInfo_EM *level_em = level->native_em_level;
4447 struct CAVE *cav = level_em->cav;
4450 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4451 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4453 cav->time_seconds = level->time;
4454 cav->gems_needed = level->gems_needed;
4456 cav->emerald_score = level->score[SC_EMERALD];
4457 cav->diamond_score = level->score[SC_DIAMOND];
4458 cav->alien_score = level->score[SC_ROBOT];
4459 cav->tank_score = level->score[SC_SPACESHIP];
4460 cav->bug_score = level->score[SC_BUG];
4461 cav->eater_score = level->score[SC_YAMYAM];
4462 cav->nut_score = level->score[SC_NUT];
4463 cav->dynamite_score = level->score[SC_DYNAMITE];
4464 cav->key_score = level->score[SC_KEY];
4465 cav->exit_score = level->score[SC_TIME_BONUS];
4467 cav->num_eater_arrays = level->num_yamyam_contents;
4469 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4470 for (y = 0; y < 3; y++)
4471 for (x = 0; x < 3; x++)
4472 cav->eater_array[i][y * 3 + x] =
4473 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4475 cav->amoeba_time = level->amoeba_speed;
4476 cav->wonderwall_time = level->time_magic_wall;
4477 cav->wheel_time = level->time_wheel;
4479 cav->android_move_time = level->android_move_time;
4480 cav->android_clone_time = level->android_clone_time;
4481 cav->ball_random = level->ball_random;
4482 cav->ball_active = level->ball_active_initial;
4483 cav->ball_time = level->ball_time;
4484 cav->num_ball_arrays = level->num_ball_contents;
4486 cav->lenses_score = level->lenses_score;
4487 cav->magnify_score = level->magnify_score;
4488 cav->slurp_score = level->slurp_score;
4490 cav->lenses_time = level->lenses_time;
4491 cav->magnify_time = level->magnify_time;
4493 cav->wind_time = 9999;
4494 cav->wind_direction =
4495 map_direction_RND_to_EM(level->wind_direction_initial);
4497 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4498 for (j = 0; j < 8; j++)
4499 cav->ball_array[i][j] =
4500 map_element_RND_to_EM_cave(level->ball_content[i].
4501 e[ball_xy[j][0]][ball_xy[j][1]]);
4503 map_android_clone_elements_RND_to_EM(level);
4505 // first fill the complete playfield with the empty space element
4506 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4507 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4508 cav->cave[x][y] = Cblank;
4510 // then copy the real level contents from level file into the playfield
4511 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4513 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4515 if (level->field[x][y] == EL_AMOEBA_DEAD)
4516 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4518 cav->cave[x][y] = new_element;
4521 for (i = 0; i < MAX_PLAYERS; i++)
4523 cav->player_x[i] = -1;
4524 cav->player_y[i] = -1;
4527 // initialize player positions and delete players from the playfield
4528 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4530 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4532 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4534 cav->player_x[player_nr] = x;
4535 cav->player_y[player_nr] = y;
4537 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4542 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4544 static int ball_xy[8][2] =
4555 struct LevelInfo_EM *level_em = level->native_em_level;
4556 struct CAVE *cav = level_em->cav;
4559 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4560 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4562 level->time = cav->time_seconds;
4563 level->gems_needed = cav->gems_needed;
4565 sprintf(level->name, "Level %d", level->file_info.nr);
4567 level->score[SC_EMERALD] = cav->emerald_score;
4568 level->score[SC_DIAMOND] = cav->diamond_score;
4569 level->score[SC_ROBOT] = cav->alien_score;
4570 level->score[SC_SPACESHIP] = cav->tank_score;
4571 level->score[SC_BUG] = cav->bug_score;
4572 level->score[SC_YAMYAM] = cav->eater_score;
4573 level->score[SC_NUT] = cav->nut_score;
4574 level->score[SC_DYNAMITE] = cav->dynamite_score;
4575 level->score[SC_KEY] = cav->key_score;
4576 level->score[SC_TIME_BONUS] = cav->exit_score;
4578 level->num_yamyam_contents = cav->num_eater_arrays;
4580 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4581 for (y = 0; y < 3; y++)
4582 for (x = 0; x < 3; x++)
4583 level->yamyam_content[i].e[x][y] =
4584 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4586 level->amoeba_speed = cav->amoeba_time;
4587 level->time_magic_wall = cav->wonderwall_time;
4588 level->time_wheel = cav->wheel_time;
4590 level->android_move_time = cav->android_move_time;
4591 level->android_clone_time = cav->android_clone_time;
4592 level->ball_random = cav->ball_random;
4593 level->ball_active_initial = cav->ball_active;
4594 level->ball_time = cav->ball_time;
4595 level->num_ball_contents = cav->num_ball_arrays;
4597 level->lenses_score = cav->lenses_score;
4598 level->magnify_score = cav->magnify_score;
4599 level->slurp_score = cav->slurp_score;
4601 level->lenses_time = cav->lenses_time;
4602 level->magnify_time = cav->magnify_time;
4604 level->wind_direction_initial =
4605 map_direction_EM_to_RND(cav->wind_direction);
4607 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4608 for (j = 0; j < 8; j++)
4609 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4610 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4612 map_android_clone_elements_EM_to_RND(level);
4614 // convert the playfield (some elements need special treatment)
4615 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4617 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4619 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4620 new_element = EL_AMOEBA_DEAD;
4622 level->field[x][y] = new_element;
4625 for (i = 0; i < MAX_PLAYERS; i++)
4627 // in case of all players set to the same field, use the first player
4628 int nr = MAX_PLAYERS - i - 1;
4629 int jx = cav->player_x[nr];
4630 int jy = cav->player_y[nr];
4632 if (jx != -1 && jy != -1)
4633 level->field[jx][jy] = EL_PLAYER_1 + nr;
4636 // time score is counted for each 10 seconds left in Emerald Mine levels
4637 level->time_score_base = 10;
4641 // ----------------------------------------------------------------------------
4642 // functions for loading SP level
4643 // ----------------------------------------------------------------------------
4645 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4647 struct LevelInfo_SP *level_sp = level->native_sp_level;
4648 LevelInfoType *header = &level_sp->header;
4651 level_sp->width = level->fieldx;
4652 level_sp->height = level->fieldy;
4654 for (x = 0; x < level->fieldx; x++)
4655 for (y = 0; y < level->fieldy; y++)
4656 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4658 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4660 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4661 header->LevelTitle[i] = level->name[i];
4662 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4664 header->InfotronsNeeded = level->gems_needed;
4666 header->SpecialPortCount = 0;
4668 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4670 boolean gravity_port_found = FALSE;
4671 boolean gravity_port_valid = FALSE;
4672 int gravity_port_flag;
4673 int gravity_port_base_element;
4674 int element = level->field[x][y];
4676 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4677 element <= EL_SP_GRAVITY_ON_PORT_UP)
4679 gravity_port_found = TRUE;
4680 gravity_port_valid = TRUE;
4681 gravity_port_flag = 1;
4682 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4684 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4685 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4687 gravity_port_found = TRUE;
4688 gravity_port_valid = TRUE;
4689 gravity_port_flag = 0;
4690 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4692 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4693 element <= EL_SP_GRAVITY_PORT_UP)
4695 // change R'n'D style gravity inverting special port to normal port
4696 // (there are no gravity inverting ports in native Supaplex engine)
4698 gravity_port_found = TRUE;
4699 gravity_port_valid = FALSE;
4700 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4703 if (gravity_port_found)
4705 if (gravity_port_valid &&
4706 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4708 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4710 port->PortLocation = (y * level->fieldx + x) * 2;
4711 port->Gravity = gravity_port_flag;
4713 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4715 header->SpecialPortCount++;
4719 // change special gravity port to normal port
4721 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4724 level_sp->playfield[x][y] = element - EL_SP_START;
4729 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4731 struct LevelInfo_SP *level_sp = level->native_sp_level;
4732 LevelInfoType *header = &level_sp->header;
4733 boolean num_invalid_elements = 0;
4736 level->fieldx = level_sp->width;
4737 level->fieldy = level_sp->height;
4739 for (x = 0; x < level->fieldx; x++)
4741 for (y = 0; y < level->fieldy; y++)
4743 int element_old = level_sp->playfield[x][y];
4744 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4746 if (element_new == EL_UNKNOWN)
4748 num_invalid_elements++;
4750 Debug("level:native:SP", "invalid element %d at position %d, %d",
4754 level->field[x][y] = element_new;
4758 if (num_invalid_elements > 0)
4759 Warn("found %d invalid elements%s", num_invalid_elements,
4760 (!options.debug ? " (use '--debug' for more details)" : ""));
4762 for (i = 0; i < MAX_PLAYERS; i++)
4763 level->initial_player_gravity[i] =
4764 (header->InitialGravity == 1 ? TRUE : FALSE);
4766 // skip leading spaces
4767 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4768 if (header->LevelTitle[i] != ' ')
4772 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4773 level->name[j] = header->LevelTitle[i];
4774 level->name[j] = '\0';
4776 // cut trailing spaces
4778 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4779 level->name[j - 1] = '\0';
4781 level->gems_needed = header->InfotronsNeeded;
4783 for (i = 0; i < header->SpecialPortCount; i++)
4785 SpecialPortType *port = &header->SpecialPort[i];
4786 int port_location = port->PortLocation;
4787 int gravity = port->Gravity;
4788 int port_x, port_y, port_element;
4790 port_x = (port_location / 2) % level->fieldx;
4791 port_y = (port_location / 2) / level->fieldx;
4793 if (port_x < 0 || port_x >= level->fieldx ||
4794 port_y < 0 || port_y >= level->fieldy)
4796 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4801 port_element = level->field[port_x][port_y];
4803 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4804 port_element > EL_SP_GRAVITY_PORT_UP)
4806 Warn("no special port at position (%d, %d)", port_x, port_y);
4811 // change previous (wrong) gravity inverting special port to either
4812 // gravity enabling special port or gravity disabling special port
4813 level->field[port_x][port_y] +=
4814 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4815 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4818 // change special gravity ports without database entries to normal ports
4819 for (x = 0; x < level->fieldx; x++)
4820 for (y = 0; y < level->fieldy; y++)
4821 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4822 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4823 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4825 level->time = 0; // no time limit
4826 level->amoeba_speed = 0;
4827 level->time_magic_wall = 0;
4828 level->time_wheel = 0;
4829 level->amoeba_content = EL_EMPTY;
4831 // original Supaplex does not use score values -- rate by playing time
4832 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4833 level->score[i] = 0;
4835 level->rate_time_over_score = TRUE;
4837 // there are no yamyams in supaplex levels
4838 for (i = 0; i < level->num_yamyam_contents; i++)
4839 for (x = 0; x < 3; x++)
4840 for (y = 0; y < 3; y++)
4841 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4844 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4846 struct LevelInfo_SP *level_sp = level->native_sp_level;
4847 struct DemoInfo_SP *demo = &level_sp->demo;
4850 // always start with reliable default values
4851 demo->is_available = FALSE;
4854 if (TAPE_IS_EMPTY(tape))
4857 demo->level_nr = tape.level_nr; // (currently not used)
4859 level_sp->header.DemoRandomSeed = tape.random_seed;
4863 for (i = 0; i < tape.length; i++)
4865 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4866 int demo_repeat = tape.pos[i].delay;
4867 int demo_entries = (demo_repeat + 15) / 16;
4869 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4871 Warn("tape truncated: size exceeds maximum SP demo size %d",
4877 for (j = 0; j < demo_repeat / 16; j++)
4878 demo->data[demo->length++] = 0xf0 | demo_action;
4880 if (demo_repeat % 16)
4881 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4884 demo->is_available = TRUE;
4887 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4889 struct LevelInfo_SP *level_sp = level->native_sp_level;
4890 struct DemoInfo_SP *demo = &level_sp->demo;
4891 char *filename = level->file_info.filename;
4894 // always start with reliable default values
4895 setTapeInfoToDefaults();
4897 if (!demo->is_available)
4900 tape.level_nr = demo->level_nr; // (currently not used)
4901 tape.random_seed = level_sp->header.DemoRandomSeed;
4903 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4906 tape.pos[tape.counter].delay = 0;
4908 for (i = 0; i < demo->length; i++)
4910 int demo_action = demo->data[i] & 0x0f;
4911 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4912 int tape_action = map_key_SP_to_RND(demo_action);
4913 int tape_repeat = demo_repeat + 1;
4914 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4915 boolean success = 0;
4918 for (j = 0; j < tape_repeat; j++)
4919 success = TapeAddAction(action);
4923 Warn("SP demo truncated: size exceeds maximum tape size %d",
4930 TapeHaltRecording();
4934 // ----------------------------------------------------------------------------
4935 // functions for loading MM level
4936 // ----------------------------------------------------------------------------
4938 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4940 struct LevelInfo_MM *level_mm = level->native_mm_level;
4943 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4944 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4946 level_mm->time = level->time;
4947 level_mm->kettles_needed = level->gems_needed;
4948 level_mm->auto_count_kettles = level->auto_count_gems;
4950 level_mm->mm_laser_red = level->mm_laser_red;
4951 level_mm->mm_laser_green = level->mm_laser_green;
4952 level_mm->mm_laser_blue = level->mm_laser_blue;
4954 level_mm->df_laser_red = level->df_laser_red;
4955 level_mm->df_laser_green = level->df_laser_green;
4956 level_mm->df_laser_blue = level->df_laser_blue;
4958 strcpy(level_mm->name, level->name);
4959 strcpy(level_mm->author, level->author);
4961 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4962 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4963 level_mm->score[SC_KEY] = level->score[SC_KEY];
4964 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4965 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4967 level_mm->amoeba_speed = level->amoeba_speed;
4968 level_mm->time_fuse = level->mm_time_fuse;
4969 level_mm->time_bomb = level->mm_time_bomb;
4970 level_mm->time_ball = level->mm_time_ball;
4971 level_mm->time_block = level->mm_time_block;
4973 level_mm->num_ball_contents = level->num_mm_ball_contents;
4974 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4975 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4976 level_mm->explode_ball = level->explode_mm_ball;
4978 for (i = 0; i < level->num_mm_ball_contents; i++)
4979 level_mm->ball_content[i] =
4980 map_element_RND_to_MM(level->mm_ball_content[i]);
4982 for (x = 0; x < level->fieldx; x++)
4983 for (y = 0; y < level->fieldy; y++)
4985 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4988 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4990 struct LevelInfo_MM *level_mm = level->native_mm_level;
4993 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4994 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4996 level->time = level_mm->time;
4997 level->gems_needed = level_mm->kettles_needed;
4998 level->auto_count_gems = level_mm->auto_count_kettles;
5000 level->mm_laser_red = level_mm->mm_laser_red;
5001 level->mm_laser_green = level_mm->mm_laser_green;
5002 level->mm_laser_blue = level_mm->mm_laser_blue;
5004 level->df_laser_red = level_mm->df_laser_red;
5005 level->df_laser_green = level_mm->df_laser_green;
5006 level->df_laser_blue = level_mm->df_laser_blue;
5008 strcpy(level->name, level_mm->name);
5010 // only overwrite author from 'levelinfo.conf' if author defined in level
5011 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5012 strcpy(level->author, level_mm->author);
5014 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5015 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5016 level->score[SC_KEY] = level_mm->score[SC_KEY];
5017 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5018 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5020 level->amoeba_speed = level_mm->amoeba_speed;
5021 level->mm_time_fuse = level_mm->time_fuse;
5022 level->mm_time_bomb = level_mm->time_bomb;
5023 level->mm_time_ball = level_mm->time_ball;
5024 level->mm_time_block = level_mm->time_block;
5026 level->num_mm_ball_contents = level_mm->num_ball_contents;
5027 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5028 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5029 level->explode_mm_ball = level_mm->explode_ball;
5031 for (i = 0; i < level->num_mm_ball_contents; i++)
5032 level->mm_ball_content[i] =
5033 map_element_MM_to_RND(level_mm->ball_content[i]);
5035 for (x = 0; x < level->fieldx; x++)
5036 for (y = 0; y < level->fieldy; y++)
5037 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5041 // ----------------------------------------------------------------------------
5042 // functions for loading DC level
5043 // ----------------------------------------------------------------------------
5045 #define DC_LEVEL_HEADER_SIZE 344
5047 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5050 static int last_data_encoded;
5054 int diff_hi, diff_lo;
5055 int data_hi, data_lo;
5056 unsigned short data_decoded;
5060 last_data_encoded = 0;
5067 diff = data_encoded - last_data_encoded;
5068 diff_hi = diff & ~0xff;
5069 diff_lo = diff & 0xff;
5073 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5074 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5075 data_hi = data_hi & 0xff00;
5077 data_decoded = data_hi | data_lo;
5079 last_data_encoded = data_encoded;
5081 offset1 = (offset1 + 1) % 31;
5082 offset2 = offset2 & 0xff;
5084 return data_decoded;
5087 static int getMappedElement_DC(int element)
5095 // 0x0117 - 0x036e: (?)
5098 // 0x042d - 0x0684: (?)
5114 element = EL_CRYSTAL;
5117 case 0x0e77: // quicksand (boulder)
5118 element = EL_QUICKSAND_FAST_FULL;
5121 case 0x0e99: // slow quicksand (boulder)
5122 element = EL_QUICKSAND_FULL;
5126 element = EL_EM_EXIT_OPEN;
5130 element = EL_EM_EXIT_CLOSED;
5134 element = EL_EM_STEEL_EXIT_OPEN;
5138 element = EL_EM_STEEL_EXIT_CLOSED;
5141 case 0x0f4f: // dynamite (lit 1)
5142 element = EL_EM_DYNAMITE_ACTIVE;
5145 case 0x0f57: // dynamite (lit 2)
5146 element = EL_EM_DYNAMITE_ACTIVE;
5149 case 0x0f5f: // dynamite (lit 3)
5150 element = EL_EM_DYNAMITE_ACTIVE;
5153 case 0x0f67: // dynamite (lit 4)
5154 element = EL_EM_DYNAMITE_ACTIVE;
5161 element = EL_AMOEBA_WET;
5165 element = EL_AMOEBA_DROP;
5169 element = EL_DC_MAGIC_WALL;
5173 element = EL_SPACESHIP_UP;
5177 element = EL_SPACESHIP_DOWN;
5181 element = EL_SPACESHIP_LEFT;
5185 element = EL_SPACESHIP_RIGHT;
5189 element = EL_BUG_UP;
5193 element = EL_BUG_DOWN;
5197 element = EL_BUG_LEFT;
5201 element = EL_BUG_RIGHT;
5205 element = EL_MOLE_UP;
5209 element = EL_MOLE_DOWN;
5213 element = EL_MOLE_LEFT;
5217 element = EL_MOLE_RIGHT;
5225 element = EL_YAMYAM_UP;
5229 element = EL_SWITCHGATE_OPEN;
5233 element = EL_SWITCHGATE_CLOSED;
5237 element = EL_DC_SWITCHGATE_SWITCH_UP;
5241 element = EL_TIMEGATE_CLOSED;
5244 case 0x144c: // conveyor belt switch (green)
5245 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5248 case 0x144f: // conveyor belt switch (red)
5249 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5252 case 0x1452: // conveyor belt switch (blue)
5253 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5257 element = EL_CONVEYOR_BELT_3_MIDDLE;
5261 element = EL_CONVEYOR_BELT_3_LEFT;
5265 element = EL_CONVEYOR_BELT_3_RIGHT;
5269 element = EL_CONVEYOR_BELT_1_MIDDLE;
5273 element = EL_CONVEYOR_BELT_1_LEFT;
5277 element = EL_CONVEYOR_BELT_1_RIGHT;
5281 element = EL_CONVEYOR_BELT_4_MIDDLE;
5285 element = EL_CONVEYOR_BELT_4_LEFT;
5289 element = EL_CONVEYOR_BELT_4_RIGHT;
5293 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5297 element = EL_EXPANDABLE_WALL_VERTICAL;
5301 element = EL_EXPANDABLE_WALL_ANY;
5304 case 0x14ce: // growing steel wall (left/right)
5305 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5308 case 0x14df: // growing steel wall (up/down)
5309 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5312 case 0x14e8: // growing steel wall (up/down/left/right)
5313 element = EL_EXPANDABLE_STEELWALL_ANY;
5317 element = EL_SHIELD_DEADLY;
5321 element = EL_EXTRA_TIME;
5329 element = EL_EMPTY_SPACE;
5332 case 0x1578: // quicksand (empty)
5333 element = EL_QUICKSAND_FAST_EMPTY;
5336 case 0x1579: // slow quicksand (empty)
5337 element = EL_QUICKSAND_EMPTY;
5347 element = EL_EM_DYNAMITE;
5350 case 0x15a1: // key (red)
5351 element = EL_EM_KEY_1;
5354 case 0x15a2: // key (yellow)
5355 element = EL_EM_KEY_2;
5358 case 0x15a3: // key (blue)
5359 element = EL_EM_KEY_4;
5362 case 0x15a4: // key (green)
5363 element = EL_EM_KEY_3;
5366 case 0x15a5: // key (white)
5367 element = EL_DC_KEY_WHITE;
5371 element = EL_WALL_SLIPPERY;
5378 case 0x15a8: // wall (not round)
5382 case 0x15a9: // (blue)
5383 element = EL_CHAR_A;
5386 case 0x15aa: // (blue)
5387 element = EL_CHAR_B;
5390 case 0x15ab: // (blue)
5391 element = EL_CHAR_C;
5394 case 0x15ac: // (blue)
5395 element = EL_CHAR_D;
5398 case 0x15ad: // (blue)
5399 element = EL_CHAR_E;
5402 case 0x15ae: // (blue)
5403 element = EL_CHAR_F;
5406 case 0x15af: // (blue)
5407 element = EL_CHAR_G;
5410 case 0x15b0: // (blue)
5411 element = EL_CHAR_H;
5414 case 0x15b1: // (blue)
5415 element = EL_CHAR_I;
5418 case 0x15b2: // (blue)
5419 element = EL_CHAR_J;
5422 case 0x15b3: // (blue)
5423 element = EL_CHAR_K;
5426 case 0x15b4: // (blue)
5427 element = EL_CHAR_L;
5430 case 0x15b5: // (blue)
5431 element = EL_CHAR_M;
5434 case 0x15b6: // (blue)
5435 element = EL_CHAR_N;
5438 case 0x15b7: // (blue)
5439 element = EL_CHAR_O;
5442 case 0x15b8: // (blue)
5443 element = EL_CHAR_P;
5446 case 0x15b9: // (blue)
5447 element = EL_CHAR_Q;
5450 case 0x15ba: // (blue)
5451 element = EL_CHAR_R;
5454 case 0x15bb: // (blue)
5455 element = EL_CHAR_S;
5458 case 0x15bc: // (blue)
5459 element = EL_CHAR_T;
5462 case 0x15bd: // (blue)
5463 element = EL_CHAR_U;
5466 case 0x15be: // (blue)
5467 element = EL_CHAR_V;
5470 case 0x15bf: // (blue)
5471 element = EL_CHAR_W;
5474 case 0x15c0: // (blue)
5475 element = EL_CHAR_X;
5478 case 0x15c1: // (blue)
5479 element = EL_CHAR_Y;
5482 case 0x15c2: // (blue)
5483 element = EL_CHAR_Z;
5486 case 0x15c3: // (blue)
5487 element = EL_CHAR_AUMLAUT;
5490 case 0x15c4: // (blue)
5491 element = EL_CHAR_OUMLAUT;
5494 case 0x15c5: // (blue)
5495 element = EL_CHAR_UUMLAUT;
5498 case 0x15c6: // (blue)
5499 element = EL_CHAR_0;
5502 case 0x15c7: // (blue)
5503 element = EL_CHAR_1;
5506 case 0x15c8: // (blue)
5507 element = EL_CHAR_2;
5510 case 0x15c9: // (blue)
5511 element = EL_CHAR_3;
5514 case 0x15ca: // (blue)
5515 element = EL_CHAR_4;
5518 case 0x15cb: // (blue)
5519 element = EL_CHAR_5;
5522 case 0x15cc: // (blue)
5523 element = EL_CHAR_6;
5526 case 0x15cd: // (blue)
5527 element = EL_CHAR_7;
5530 case 0x15ce: // (blue)
5531 element = EL_CHAR_8;
5534 case 0x15cf: // (blue)
5535 element = EL_CHAR_9;
5538 case 0x15d0: // (blue)
5539 element = EL_CHAR_PERIOD;
5542 case 0x15d1: // (blue)
5543 element = EL_CHAR_EXCLAM;
5546 case 0x15d2: // (blue)
5547 element = EL_CHAR_COLON;
5550 case 0x15d3: // (blue)
5551 element = EL_CHAR_LESS;
5554 case 0x15d4: // (blue)
5555 element = EL_CHAR_GREATER;
5558 case 0x15d5: // (blue)
5559 element = EL_CHAR_QUESTION;
5562 case 0x15d6: // (blue)
5563 element = EL_CHAR_COPYRIGHT;
5566 case 0x15d7: // (blue)
5567 element = EL_CHAR_UP;
5570 case 0x15d8: // (blue)
5571 element = EL_CHAR_DOWN;
5574 case 0x15d9: // (blue)
5575 element = EL_CHAR_BUTTON;
5578 case 0x15da: // (blue)
5579 element = EL_CHAR_PLUS;
5582 case 0x15db: // (blue)
5583 element = EL_CHAR_MINUS;
5586 case 0x15dc: // (blue)
5587 element = EL_CHAR_APOSTROPHE;
5590 case 0x15dd: // (blue)
5591 element = EL_CHAR_PARENLEFT;
5594 case 0x15de: // (blue)
5595 element = EL_CHAR_PARENRIGHT;
5598 case 0x15df: // (green)
5599 element = EL_CHAR_A;
5602 case 0x15e0: // (green)
5603 element = EL_CHAR_B;
5606 case 0x15e1: // (green)
5607 element = EL_CHAR_C;
5610 case 0x15e2: // (green)
5611 element = EL_CHAR_D;
5614 case 0x15e3: // (green)
5615 element = EL_CHAR_E;
5618 case 0x15e4: // (green)
5619 element = EL_CHAR_F;
5622 case 0x15e5: // (green)
5623 element = EL_CHAR_G;
5626 case 0x15e6: // (green)
5627 element = EL_CHAR_H;
5630 case 0x15e7: // (green)
5631 element = EL_CHAR_I;
5634 case 0x15e8: // (green)
5635 element = EL_CHAR_J;
5638 case 0x15e9: // (green)
5639 element = EL_CHAR_K;
5642 case 0x15ea: // (green)
5643 element = EL_CHAR_L;
5646 case 0x15eb: // (green)
5647 element = EL_CHAR_M;
5650 case 0x15ec: // (green)
5651 element = EL_CHAR_N;
5654 case 0x15ed: // (green)
5655 element = EL_CHAR_O;
5658 case 0x15ee: // (green)
5659 element = EL_CHAR_P;
5662 case 0x15ef: // (green)
5663 element = EL_CHAR_Q;
5666 case 0x15f0: // (green)
5667 element = EL_CHAR_R;
5670 case 0x15f1: // (green)
5671 element = EL_CHAR_S;
5674 case 0x15f2: // (green)
5675 element = EL_CHAR_T;
5678 case 0x15f3: // (green)
5679 element = EL_CHAR_U;
5682 case 0x15f4: // (green)
5683 element = EL_CHAR_V;
5686 case 0x15f5: // (green)
5687 element = EL_CHAR_W;
5690 case 0x15f6: // (green)
5691 element = EL_CHAR_X;
5694 case 0x15f7: // (green)
5695 element = EL_CHAR_Y;
5698 case 0x15f8: // (green)
5699 element = EL_CHAR_Z;
5702 case 0x15f9: // (green)
5703 element = EL_CHAR_AUMLAUT;
5706 case 0x15fa: // (green)
5707 element = EL_CHAR_OUMLAUT;
5710 case 0x15fb: // (green)
5711 element = EL_CHAR_UUMLAUT;
5714 case 0x15fc: // (green)
5715 element = EL_CHAR_0;
5718 case 0x15fd: // (green)
5719 element = EL_CHAR_1;
5722 case 0x15fe: // (green)
5723 element = EL_CHAR_2;
5726 case 0x15ff: // (green)
5727 element = EL_CHAR_3;
5730 case 0x1600: // (green)
5731 element = EL_CHAR_4;
5734 case 0x1601: // (green)
5735 element = EL_CHAR_5;
5738 case 0x1602: // (green)
5739 element = EL_CHAR_6;
5742 case 0x1603: // (green)
5743 element = EL_CHAR_7;
5746 case 0x1604: // (green)
5747 element = EL_CHAR_8;
5750 case 0x1605: // (green)
5751 element = EL_CHAR_9;
5754 case 0x1606: // (green)
5755 element = EL_CHAR_PERIOD;
5758 case 0x1607: // (green)
5759 element = EL_CHAR_EXCLAM;
5762 case 0x1608: // (green)
5763 element = EL_CHAR_COLON;
5766 case 0x1609: // (green)
5767 element = EL_CHAR_LESS;
5770 case 0x160a: // (green)
5771 element = EL_CHAR_GREATER;
5774 case 0x160b: // (green)
5775 element = EL_CHAR_QUESTION;
5778 case 0x160c: // (green)
5779 element = EL_CHAR_COPYRIGHT;
5782 case 0x160d: // (green)
5783 element = EL_CHAR_UP;
5786 case 0x160e: // (green)
5787 element = EL_CHAR_DOWN;
5790 case 0x160f: // (green)
5791 element = EL_CHAR_BUTTON;
5794 case 0x1610: // (green)
5795 element = EL_CHAR_PLUS;
5798 case 0x1611: // (green)
5799 element = EL_CHAR_MINUS;
5802 case 0x1612: // (green)
5803 element = EL_CHAR_APOSTROPHE;
5806 case 0x1613: // (green)
5807 element = EL_CHAR_PARENLEFT;
5810 case 0x1614: // (green)
5811 element = EL_CHAR_PARENRIGHT;
5814 case 0x1615: // (blue steel)
5815 element = EL_STEEL_CHAR_A;
5818 case 0x1616: // (blue steel)
5819 element = EL_STEEL_CHAR_B;
5822 case 0x1617: // (blue steel)
5823 element = EL_STEEL_CHAR_C;
5826 case 0x1618: // (blue steel)
5827 element = EL_STEEL_CHAR_D;
5830 case 0x1619: // (blue steel)
5831 element = EL_STEEL_CHAR_E;
5834 case 0x161a: // (blue steel)
5835 element = EL_STEEL_CHAR_F;
5838 case 0x161b: // (blue steel)
5839 element = EL_STEEL_CHAR_G;
5842 case 0x161c: // (blue steel)
5843 element = EL_STEEL_CHAR_H;
5846 case 0x161d: // (blue steel)
5847 element = EL_STEEL_CHAR_I;
5850 case 0x161e: // (blue steel)
5851 element = EL_STEEL_CHAR_J;
5854 case 0x161f: // (blue steel)
5855 element = EL_STEEL_CHAR_K;
5858 case 0x1620: // (blue steel)
5859 element = EL_STEEL_CHAR_L;
5862 case 0x1621: // (blue steel)
5863 element = EL_STEEL_CHAR_M;
5866 case 0x1622: // (blue steel)
5867 element = EL_STEEL_CHAR_N;
5870 case 0x1623: // (blue steel)
5871 element = EL_STEEL_CHAR_O;
5874 case 0x1624: // (blue steel)
5875 element = EL_STEEL_CHAR_P;
5878 case 0x1625: // (blue steel)
5879 element = EL_STEEL_CHAR_Q;
5882 case 0x1626: // (blue steel)
5883 element = EL_STEEL_CHAR_R;
5886 case 0x1627: // (blue steel)
5887 element = EL_STEEL_CHAR_S;
5890 case 0x1628: // (blue steel)
5891 element = EL_STEEL_CHAR_T;
5894 case 0x1629: // (blue steel)
5895 element = EL_STEEL_CHAR_U;
5898 case 0x162a: // (blue steel)
5899 element = EL_STEEL_CHAR_V;
5902 case 0x162b: // (blue steel)
5903 element = EL_STEEL_CHAR_W;
5906 case 0x162c: // (blue steel)
5907 element = EL_STEEL_CHAR_X;
5910 case 0x162d: // (blue steel)
5911 element = EL_STEEL_CHAR_Y;
5914 case 0x162e: // (blue steel)
5915 element = EL_STEEL_CHAR_Z;
5918 case 0x162f: // (blue steel)
5919 element = EL_STEEL_CHAR_AUMLAUT;
5922 case 0x1630: // (blue steel)
5923 element = EL_STEEL_CHAR_OUMLAUT;
5926 case 0x1631: // (blue steel)
5927 element = EL_STEEL_CHAR_UUMLAUT;
5930 case 0x1632: // (blue steel)
5931 element = EL_STEEL_CHAR_0;
5934 case 0x1633: // (blue steel)
5935 element = EL_STEEL_CHAR_1;
5938 case 0x1634: // (blue steel)
5939 element = EL_STEEL_CHAR_2;
5942 case 0x1635: // (blue steel)
5943 element = EL_STEEL_CHAR_3;
5946 case 0x1636: // (blue steel)
5947 element = EL_STEEL_CHAR_4;
5950 case 0x1637: // (blue steel)
5951 element = EL_STEEL_CHAR_5;
5954 case 0x1638: // (blue steel)
5955 element = EL_STEEL_CHAR_6;
5958 case 0x1639: // (blue steel)
5959 element = EL_STEEL_CHAR_7;
5962 case 0x163a: // (blue steel)
5963 element = EL_STEEL_CHAR_8;
5966 case 0x163b: // (blue steel)
5967 element = EL_STEEL_CHAR_9;
5970 case 0x163c: // (blue steel)
5971 element = EL_STEEL_CHAR_PERIOD;
5974 case 0x163d: // (blue steel)
5975 element = EL_STEEL_CHAR_EXCLAM;
5978 case 0x163e: // (blue steel)
5979 element = EL_STEEL_CHAR_COLON;
5982 case 0x163f: // (blue steel)
5983 element = EL_STEEL_CHAR_LESS;
5986 case 0x1640: // (blue steel)
5987 element = EL_STEEL_CHAR_GREATER;
5990 case 0x1641: // (blue steel)
5991 element = EL_STEEL_CHAR_QUESTION;
5994 case 0x1642: // (blue steel)
5995 element = EL_STEEL_CHAR_COPYRIGHT;
5998 case 0x1643: // (blue steel)
5999 element = EL_STEEL_CHAR_UP;
6002 case 0x1644: // (blue steel)
6003 element = EL_STEEL_CHAR_DOWN;
6006 case 0x1645: // (blue steel)
6007 element = EL_STEEL_CHAR_BUTTON;
6010 case 0x1646: // (blue steel)
6011 element = EL_STEEL_CHAR_PLUS;
6014 case 0x1647: // (blue steel)
6015 element = EL_STEEL_CHAR_MINUS;
6018 case 0x1648: // (blue steel)
6019 element = EL_STEEL_CHAR_APOSTROPHE;
6022 case 0x1649: // (blue steel)
6023 element = EL_STEEL_CHAR_PARENLEFT;
6026 case 0x164a: // (blue steel)
6027 element = EL_STEEL_CHAR_PARENRIGHT;
6030 case 0x164b: // (green steel)
6031 element = EL_STEEL_CHAR_A;
6034 case 0x164c: // (green steel)
6035 element = EL_STEEL_CHAR_B;
6038 case 0x164d: // (green steel)
6039 element = EL_STEEL_CHAR_C;
6042 case 0x164e: // (green steel)
6043 element = EL_STEEL_CHAR_D;
6046 case 0x164f: // (green steel)
6047 element = EL_STEEL_CHAR_E;
6050 case 0x1650: // (green steel)
6051 element = EL_STEEL_CHAR_F;
6054 case 0x1651: // (green steel)
6055 element = EL_STEEL_CHAR_G;
6058 case 0x1652: // (green steel)
6059 element = EL_STEEL_CHAR_H;
6062 case 0x1653: // (green steel)
6063 element = EL_STEEL_CHAR_I;
6066 case 0x1654: // (green steel)
6067 element = EL_STEEL_CHAR_J;
6070 case 0x1655: // (green steel)
6071 element = EL_STEEL_CHAR_K;
6074 case 0x1656: // (green steel)
6075 element = EL_STEEL_CHAR_L;
6078 case 0x1657: // (green steel)
6079 element = EL_STEEL_CHAR_M;
6082 case 0x1658: // (green steel)
6083 element = EL_STEEL_CHAR_N;
6086 case 0x1659: // (green steel)
6087 element = EL_STEEL_CHAR_O;
6090 case 0x165a: // (green steel)
6091 element = EL_STEEL_CHAR_P;
6094 case 0x165b: // (green steel)
6095 element = EL_STEEL_CHAR_Q;
6098 case 0x165c: // (green steel)
6099 element = EL_STEEL_CHAR_R;
6102 case 0x165d: // (green steel)
6103 element = EL_STEEL_CHAR_S;
6106 case 0x165e: // (green steel)
6107 element = EL_STEEL_CHAR_T;
6110 case 0x165f: // (green steel)
6111 element = EL_STEEL_CHAR_U;
6114 case 0x1660: // (green steel)
6115 element = EL_STEEL_CHAR_V;
6118 case 0x1661: // (green steel)
6119 element = EL_STEEL_CHAR_W;
6122 case 0x1662: // (green steel)
6123 element = EL_STEEL_CHAR_X;
6126 case 0x1663: // (green steel)
6127 element = EL_STEEL_CHAR_Y;
6130 case 0x1664: // (green steel)
6131 element = EL_STEEL_CHAR_Z;
6134 case 0x1665: // (green steel)
6135 element = EL_STEEL_CHAR_AUMLAUT;
6138 case 0x1666: // (green steel)
6139 element = EL_STEEL_CHAR_OUMLAUT;
6142 case 0x1667: // (green steel)
6143 element = EL_STEEL_CHAR_UUMLAUT;
6146 case 0x1668: // (green steel)
6147 element = EL_STEEL_CHAR_0;
6150 case 0x1669: // (green steel)
6151 element = EL_STEEL_CHAR_1;
6154 case 0x166a: // (green steel)
6155 element = EL_STEEL_CHAR_2;
6158 case 0x166b: // (green steel)
6159 element = EL_STEEL_CHAR_3;
6162 case 0x166c: // (green steel)
6163 element = EL_STEEL_CHAR_4;
6166 case 0x166d: // (green steel)
6167 element = EL_STEEL_CHAR_5;
6170 case 0x166e: // (green steel)
6171 element = EL_STEEL_CHAR_6;
6174 case 0x166f: // (green steel)
6175 element = EL_STEEL_CHAR_7;
6178 case 0x1670: // (green steel)
6179 element = EL_STEEL_CHAR_8;
6182 case 0x1671: // (green steel)
6183 element = EL_STEEL_CHAR_9;
6186 case 0x1672: // (green steel)
6187 element = EL_STEEL_CHAR_PERIOD;
6190 case 0x1673: // (green steel)
6191 element = EL_STEEL_CHAR_EXCLAM;
6194 case 0x1674: // (green steel)
6195 element = EL_STEEL_CHAR_COLON;
6198 case 0x1675: // (green steel)
6199 element = EL_STEEL_CHAR_LESS;
6202 case 0x1676: // (green steel)
6203 element = EL_STEEL_CHAR_GREATER;
6206 case 0x1677: // (green steel)
6207 element = EL_STEEL_CHAR_QUESTION;
6210 case 0x1678: // (green steel)
6211 element = EL_STEEL_CHAR_COPYRIGHT;
6214 case 0x1679: // (green steel)
6215 element = EL_STEEL_CHAR_UP;
6218 case 0x167a: // (green steel)
6219 element = EL_STEEL_CHAR_DOWN;
6222 case 0x167b: // (green steel)
6223 element = EL_STEEL_CHAR_BUTTON;
6226 case 0x167c: // (green steel)
6227 element = EL_STEEL_CHAR_PLUS;
6230 case 0x167d: // (green steel)
6231 element = EL_STEEL_CHAR_MINUS;
6234 case 0x167e: // (green steel)
6235 element = EL_STEEL_CHAR_APOSTROPHE;
6238 case 0x167f: // (green steel)
6239 element = EL_STEEL_CHAR_PARENLEFT;
6242 case 0x1680: // (green steel)
6243 element = EL_STEEL_CHAR_PARENRIGHT;
6246 case 0x1681: // gate (red)
6247 element = EL_EM_GATE_1;
6250 case 0x1682: // secret gate (red)
6251 element = EL_EM_GATE_1_GRAY;
6254 case 0x1683: // gate (yellow)
6255 element = EL_EM_GATE_2;
6258 case 0x1684: // secret gate (yellow)
6259 element = EL_EM_GATE_2_GRAY;
6262 case 0x1685: // gate (blue)
6263 element = EL_EM_GATE_4;
6266 case 0x1686: // secret gate (blue)
6267 element = EL_EM_GATE_4_GRAY;
6270 case 0x1687: // gate (green)
6271 element = EL_EM_GATE_3;
6274 case 0x1688: // secret gate (green)
6275 element = EL_EM_GATE_3_GRAY;
6278 case 0x1689: // gate (white)
6279 element = EL_DC_GATE_WHITE;
6282 case 0x168a: // secret gate (white)
6283 element = EL_DC_GATE_WHITE_GRAY;
6286 case 0x168b: // secret gate (no key)
6287 element = EL_DC_GATE_FAKE_GRAY;
6291 element = EL_ROBOT_WHEEL;
6295 element = EL_DC_TIMEGATE_SWITCH;
6299 element = EL_ACID_POOL_BOTTOM;
6303 element = EL_ACID_POOL_TOPLEFT;
6307 element = EL_ACID_POOL_TOPRIGHT;
6311 element = EL_ACID_POOL_BOTTOMLEFT;
6315 element = EL_ACID_POOL_BOTTOMRIGHT;
6319 element = EL_STEELWALL;
6323 element = EL_STEELWALL_SLIPPERY;
6326 case 0x1695: // steel wall (not round)
6327 element = EL_STEELWALL;
6330 case 0x1696: // steel wall (left)
6331 element = EL_DC_STEELWALL_1_LEFT;
6334 case 0x1697: // steel wall (bottom)
6335 element = EL_DC_STEELWALL_1_BOTTOM;
6338 case 0x1698: // steel wall (right)
6339 element = EL_DC_STEELWALL_1_RIGHT;
6342 case 0x1699: // steel wall (top)
6343 element = EL_DC_STEELWALL_1_TOP;
6346 case 0x169a: // steel wall (left/bottom)
6347 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6350 case 0x169b: // steel wall (right/bottom)
6351 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6354 case 0x169c: // steel wall (right/top)
6355 element = EL_DC_STEELWALL_1_TOPRIGHT;
6358 case 0x169d: // steel wall (left/top)
6359 element = EL_DC_STEELWALL_1_TOPLEFT;
6362 case 0x169e: // steel wall (right/bottom small)
6363 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6366 case 0x169f: // steel wall (left/bottom small)
6367 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6370 case 0x16a0: // steel wall (right/top small)
6371 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6374 case 0x16a1: // steel wall (left/top small)
6375 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6378 case 0x16a2: // steel wall (left/right)
6379 element = EL_DC_STEELWALL_1_VERTICAL;
6382 case 0x16a3: // steel wall (top/bottom)
6383 element = EL_DC_STEELWALL_1_HORIZONTAL;
6386 case 0x16a4: // steel wall 2 (left end)
6387 element = EL_DC_STEELWALL_2_LEFT;
6390 case 0x16a5: // steel wall 2 (right end)
6391 element = EL_DC_STEELWALL_2_RIGHT;
6394 case 0x16a6: // steel wall 2 (top end)
6395 element = EL_DC_STEELWALL_2_TOP;
6398 case 0x16a7: // steel wall 2 (bottom end)
6399 element = EL_DC_STEELWALL_2_BOTTOM;
6402 case 0x16a8: // steel wall 2 (left/right)
6403 element = EL_DC_STEELWALL_2_HORIZONTAL;
6406 case 0x16a9: // steel wall 2 (up/down)
6407 element = EL_DC_STEELWALL_2_VERTICAL;
6410 case 0x16aa: // steel wall 2 (mid)
6411 element = EL_DC_STEELWALL_2_MIDDLE;
6415 element = EL_SIGN_EXCLAMATION;
6419 element = EL_SIGN_RADIOACTIVITY;
6423 element = EL_SIGN_STOP;
6427 element = EL_SIGN_WHEELCHAIR;
6431 element = EL_SIGN_PARKING;
6435 element = EL_SIGN_NO_ENTRY;
6439 element = EL_SIGN_HEART;
6443 element = EL_SIGN_GIVE_WAY;
6447 element = EL_SIGN_ENTRY_FORBIDDEN;
6451 element = EL_SIGN_EMERGENCY_EXIT;
6455 element = EL_SIGN_YIN_YANG;
6459 element = EL_WALL_EMERALD;
6463 element = EL_WALL_DIAMOND;
6467 element = EL_WALL_PEARL;
6471 element = EL_WALL_CRYSTAL;
6475 element = EL_INVISIBLE_WALL;
6479 element = EL_INVISIBLE_STEELWALL;
6483 // EL_INVISIBLE_SAND
6486 element = EL_LIGHT_SWITCH;
6490 element = EL_ENVELOPE_1;
6494 if (element >= 0x0117 && element <= 0x036e) // (?)
6495 element = EL_DIAMOND;
6496 else if (element >= 0x042d && element <= 0x0684) // (?)
6497 element = EL_EMERALD;
6498 else if (element >= 0x157c && element <= 0x158b)
6500 else if (element >= 0x1590 && element <= 0x159f)
6501 element = EL_DC_LANDMINE;
6502 else if (element >= 0x16bc && element <= 0x16cb)
6503 element = EL_INVISIBLE_SAND;
6506 Warn("unknown Diamond Caves element 0x%04x", element);
6508 element = EL_UNKNOWN;
6513 return getMappedElement(element);
6516 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6518 byte header[DC_LEVEL_HEADER_SIZE];
6520 int envelope_header_pos = 62;
6521 int envelope_content_pos = 94;
6522 int level_name_pos = 251;
6523 int level_author_pos = 292;
6524 int envelope_header_len;
6525 int envelope_content_len;
6527 int level_author_len;
6529 int num_yamyam_contents;
6532 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6534 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6536 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6538 header[i * 2 + 0] = header_word >> 8;
6539 header[i * 2 + 1] = header_word & 0xff;
6542 // read some values from level header to check level decoding integrity
6543 fieldx = header[6] | (header[7] << 8);
6544 fieldy = header[8] | (header[9] << 8);
6545 num_yamyam_contents = header[60] | (header[61] << 8);
6547 // do some simple sanity checks to ensure that level was correctly decoded
6548 if (fieldx < 1 || fieldx > 256 ||
6549 fieldy < 1 || fieldy > 256 ||
6550 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6552 level->no_valid_file = TRUE;
6554 Warn("cannot decode level from stream -- using empty level");
6559 // maximum envelope header size is 31 bytes
6560 envelope_header_len = header[envelope_header_pos];
6561 // maximum envelope content size is 110 (156?) bytes
6562 envelope_content_len = header[envelope_content_pos];
6564 // maximum level title size is 40 bytes
6565 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6566 // maximum level author size is 30 (51?) bytes
6567 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6571 for (i = 0; i < envelope_header_len; i++)
6572 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6573 level->envelope[0].text[envelope_size++] =
6574 header[envelope_header_pos + 1 + i];
6576 if (envelope_header_len > 0 && envelope_content_len > 0)
6578 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6579 level->envelope[0].text[envelope_size++] = '\n';
6580 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6581 level->envelope[0].text[envelope_size++] = '\n';
6584 for (i = 0; i < envelope_content_len; i++)
6585 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6586 level->envelope[0].text[envelope_size++] =
6587 header[envelope_content_pos + 1 + i];
6589 level->envelope[0].text[envelope_size] = '\0';
6591 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6592 level->envelope[0].ysize = 10;
6593 level->envelope[0].autowrap = TRUE;
6594 level->envelope[0].centered = TRUE;
6596 for (i = 0; i < level_name_len; i++)
6597 level->name[i] = header[level_name_pos + 1 + i];
6598 level->name[level_name_len] = '\0';
6600 for (i = 0; i < level_author_len; i++)
6601 level->author[i] = header[level_author_pos + 1 + i];
6602 level->author[level_author_len] = '\0';
6604 num_yamyam_contents = header[60] | (header[61] << 8);
6605 level->num_yamyam_contents =
6606 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6608 for (i = 0; i < num_yamyam_contents; i++)
6610 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6612 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6613 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6615 if (i < MAX_ELEMENT_CONTENTS)
6616 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6620 fieldx = header[6] | (header[7] << 8);
6621 fieldy = header[8] | (header[9] << 8);
6622 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6623 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6625 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6627 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6628 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6630 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6631 level->field[x][y] = getMappedElement_DC(element_dc);
6634 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6635 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6636 level->field[x][y] = EL_PLAYER_1;
6638 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6639 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6640 level->field[x][y] = EL_PLAYER_2;
6642 level->gems_needed = header[18] | (header[19] << 8);
6644 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6645 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6646 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6647 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6648 level->score[SC_NUT] = header[28] | (header[29] << 8);
6649 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6650 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6651 level->score[SC_BUG] = header[34] | (header[35] << 8);
6652 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6653 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6654 level->score[SC_KEY] = header[40] | (header[41] << 8);
6655 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6657 level->time = header[44] | (header[45] << 8);
6659 level->amoeba_speed = header[46] | (header[47] << 8);
6660 level->time_light = header[48] | (header[49] << 8);
6661 level->time_timegate = header[50] | (header[51] << 8);
6662 level->time_wheel = header[52] | (header[53] << 8);
6663 level->time_magic_wall = header[54] | (header[55] << 8);
6664 level->extra_time = header[56] | (header[57] << 8);
6665 level->shield_normal_time = header[58] | (header[59] << 8);
6667 // shield and extra time elements do not have a score
6668 level->score[SC_SHIELD] = 0;
6669 level->extra_time_score = 0;
6671 // set time for normal and deadly shields to the same value
6672 level->shield_deadly_time = level->shield_normal_time;
6674 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6675 // can slip down from flat walls, like normal walls and steel walls
6676 level->em_slippery_gems = TRUE;
6678 // time score is counted for each 10 seconds left in Diamond Caves levels
6679 level->time_score_base = 10;
6682 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6683 struct LevelFileInfo *level_file_info,
6684 boolean level_info_only)
6686 char *filename = level_file_info->filename;
6688 int num_magic_bytes = 8;
6689 char magic_bytes[num_magic_bytes + 1];
6690 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6692 if (!(file = openFile(filename, MODE_READ)))
6694 level->no_valid_file = TRUE;
6696 if (!level_info_only)
6697 Warn("cannot read level '%s' -- using empty level", filename);
6702 // fseek(file, 0x0000, SEEK_SET);
6704 if (level_file_info->packed)
6706 // read "magic bytes" from start of file
6707 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6708 magic_bytes[0] = '\0';
6710 // check "magic bytes" for correct file format
6711 if (!strPrefix(magic_bytes, "DC2"))
6713 level->no_valid_file = TRUE;
6715 Warn("unknown DC level file '%s' -- using empty level", filename);
6720 if (strPrefix(magic_bytes, "DC2Win95") ||
6721 strPrefix(magic_bytes, "DC2Win98"))
6723 int position_first_level = 0x00fa;
6724 int extra_bytes = 4;
6727 // advance file stream to first level inside the level package
6728 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6730 // each block of level data is followed by block of non-level data
6731 num_levels_to_skip *= 2;
6733 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6734 while (num_levels_to_skip >= 0)
6736 // advance file stream to next level inside the level package
6737 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6739 level->no_valid_file = TRUE;
6741 Warn("cannot fseek in file '%s' -- using empty level", filename);
6746 // skip apparently unused extra bytes following each level
6747 ReadUnusedBytesFromFile(file, extra_bytes);
6749 // read size of next level in level package
6750 skip_bytes = getFile32BitLE(file);
6752 num_levels_to_skip--;
6757 level->no_valid_file = TRUE;
6759 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6765 LoadLevelFromFileStream_DC(file, level);
6771 // ----------------------------------------------------------------------------
6772 // functions for loading SB level
6773 // ----------------------------------------------------------------------------
6775 int getMappedElement_SB(int element_ascii, boolean use_ces)
6783 sb_element_mapping[] =
6785 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6786 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6787 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6788 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6789 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6790 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6791 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6792 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6799 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6800 if (element_ascii == sb_element_mapping[i].ascii)
6801 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6803 return EL_UNDEFINED;
6806 static void SetLevelSettings_SB(struct LevelInfo *level)
6810 level->use_step_counter = TRUE;
6813 level->score[SC_TIME_BONUS] = 0;
6814 level->time_score_base = 1;
6815 level->rate_time_over_score = TRUE;
6818 level->auto_exit_sokoban = TRUE;
6821 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6822 struct LevelFileInfo *level_file_info,
6823 boolean level_info_only)
6825 char *filename = level_file_info->filename;
6826 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6827 char last_comment[MAX_LINE_LEN];
6828 char level_name[MAX_LINE_LEN];
6831 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6832 boolean read_continued_line = FALSE;
6833 boolean reading_playfield = FALSE;
6834 boolean got_valid_playfield_line = FALSE;
6835 boolean invalid_playfield_char = FALSE;
6836 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6837 int file_level_nr = 0;
6838 int x = 0, y = 0; // initialized to make compilers happy
6840 last_comment[0] = '\0';
6841 level_name[0] = '\0';
6843 if (!(file = openFile(filename, MODE_READ)))
6845 level->no_valid_file = TRUE;
6847 if (!level_info_only)
6848 Warn("cannot read level '%s' -- using empty level", filename);
6853 while (!checkEndOfFile(file))
6855 // level successfully read, but next level may follow here
6856 if (!got_valid_playfield_line && reading_playfield)
6858 // read playfield from single level file -- skip remaining file
6859 if (!level_file_info->packed)
6862 if (file_level_nr >= num_levels_to_skip)
6867 last_comment[0] = '\0';
6868 level_name[0] = '\0';
6870 reading_playfield = FALSE;
6873 got_valid_playfield_line = FALSE;
6875 // read next line of input file
6876 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6879 // cut trailing line break (this can be newline and/or carriage return)
6880 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6881 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6884 // copy raw input line for later use (mainly debugging output)
6885 strcpy(line_raw, line);
6887 if (read_continued_line)
6889 // append new line to existing line, if there is enough space
6890 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6891 strcat(previous_line, line_ptr);
6893 strcpy(line, previous_line); // copy storage buffer to line
6895 read_continued_line = FALSE;
6898 // if the last character is '\', continue at next line
6899 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6901 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6902 strcpy(previous_line, line); // copy line to storage buffer
6904 read_continued_line = TRUE;
6910 if (line[0] == '\0')
6913 // extract comment text from comment line
6916 for (line_ptr = line; *line_ptr; line_ptr++)
6917 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6920 strcpy(last_comment, line_ptr);
6925 // extract level title text from line containing level title
6926 if (line[0] == '\'')
6928 strcpy(level_name, &line[1]);
6930 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6931 level_name[strlen(level_name) - 1] = '\0';
6936 // skip lines containing only spaces (or empty lines)
6937 for (line_ptr = line; *line_ptr; line_ptr++)
6938 if (*line_ptr != ' ')
6940 if (*line_ptr == '\0')
6943 // at this point, we have found a line containing part of a playfield
6945 got_valid_playfield_line = TRUE;
6947 if (!reading_playfield)
6949 reading_playfield = TRUE;
6950 invalid_playfield_char = FALSE;
6952 for (x = 0; x < MAX_LEV_FIELDX; x++)
6953 for (y = 0; y < MAX_LEV_FIELDY; y++)
6954 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6959 // start with topmost tile row
6963 // skip playfield line if larger row than allowed
6964 if (y >= MAX_LEV_FIELDY)
6967 // start with leftmost tile column
6970 // read playfield elements from line
6971 for (line_ptr = line; *line_ptr; line_ptr++)
6973 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6975 // stop parsing playfield line if larger column than allowed
6976 if (x >= MAX_LEV_FIELDX)
6979 if (mapped_sb_element == EL_UNDEFINED)
6981 invalid_playfield_char = TRUE;
6986 level->field[x][y] = mapped_sb_element;
6988 // continue with next tile column
6991 level->fieldx = MAX(x, level->fieldx);
6994 if (invalid_playfield_char)
6996 // if first playfield line, treat invalid lines as comment lines
6998 reading_playfield = FALSE;
7003 // continue with next tile row
7011 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7012 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7014 if (!reading_playfield)
7016 level->no_valid_file = TRUE;
7018 Warn("cannot read level '%s' -- using empty level", filename);
7023 if (*level_name != '\0')
7025 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7026 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7028 else if (*last_comment != '\0')
7030 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7031 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7035 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7038 // set all empty fields beyond the border walls to invisible steel wall
7039 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7041 if ((x == 0 || x == level->fieldx - 1 ||
7042 y == 0 || y == level->fieldy - 1) &&
7043 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7044 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7045 level->field, level->fieldx, level->fieldy);
7048 // set special level settings for Sokoban levels
7049 SetLevelSettings_SB(level);
7051 if (load_xsb_to_ces)
7053 // special global settings can now be set in level template
7054 level->use_custom_template = TRUE;
7059 // -------------------------------------------------------------------------
7060 // functions for handling native levels
7061 // -------------------------------------------------------------------------
7063 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7064 struct LevelFileInfo *level_file_info,
7065 boolean level_info_only)
7069 // determine position of requested level inside level package
7070 if (level_file_info->packed)
7071 pos = level_file_info->nr - leveldir_current->first_level;
7073 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7074 level->no_valid_file = TRUE;
7077 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7078 struct LevelFileInfo *level_file_info,
7079 boolean level_info_only)
7081 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7082 level->no_valid_file = TRUE;
7085 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7086 struct LevelFileInfo *level_file_info,
7087 boolean level_info_only)
7091 // determine position of requested level inside level package
7092 if (level_file_info->packed)
7093 pos = level_file_info->nr - leveldir_current->first_level;
7095 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7096 level->no_valid_file = TRUE;
7099 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7100 struct LevelFileInfo *level_file_info,
7101 boolean level_info_only)
7103 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7104 level->no_valid_file = TRUE;
7107 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7109 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7110 CopyNativeLevel_RND_to_BD(level);
7111 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7112 CopyNativeLevel_RND_to_EM(level);
7113 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7114 CopyNativeLevel_RND_to_SP(level);
7115 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7116 CopyNativeLevel_RND_to_MM(level);
7119 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7121 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7122 CopyNativeLevel_BD_to_RND(level);
7123 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7124 CopyNativeLevel_EM_to_RND(level);
7125 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7126 CopyNativeLevel_SP_to_RND(level);
7127 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7128 CopyNativeLevel_MM_to_RND(level);
7131 void SaveNativeLevel(struct LevelInfo *level)
7133 // saving native level files only supported for some game engines
7134 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7135 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7138 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7139 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7140 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7141 char *filename = getLevelFilenameFromBasename(basename);
7143 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7146 boolean success = FALSE;
7148 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7150 CopyNativeLevel_RND_to_BD(level);
7151 // CopyNativeTape_RND_to_BD(level);
7153 success = SaveNativeLevel_BD(filename);
7155 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7157 CopyNativeLevel_RND_to_SP(level);
7158 CopyNativeTape_RND_to_SP(level);
7160 success = SaveNativeLevel_SP(filename);
7164 Request("Native level file saved!", REQ_CONFIRM);
7166 Request("Failed to save native level file!", REQ_CONFIRM);
7170 // ----------------------------------------------------------------------------
7171 // functions for loading generic level
7172 // ----------------------------------------------------------------------------
7174 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7175 struct LevelFileInfo *level_file_info,
7176 boolean level_info_only)
7178 // always start with reliable default values
7179 setLevelInfoToDefaults(level, level_info_only, TRUE);
7181 switch (level_file_info->type)
7183 case LEVEL_FILE_TYPE_RND:
7184 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7187 case LEVEL_FILE_TYPE_BD:
7188 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7189 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7192 case LEVEL_FILE_TYPE_EM:
7193 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7194 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7197 case LEVEL_FILE_TYPE_SP:
7198 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7199 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7202 case LEVEL_FILE_TYPE_MM:
7203 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7204 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7207 case LEVEL_FILE_TYPE_DC:
7208 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7211 case LEVEL_FILE_TYPE_SB:
7212 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7216 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7220 // if level file is invalid, restore level structure to default values
7221 if (level->no_valid_file)
7222 setLevelInfoToDefaults(level, level_info_only, FALSE);
7224 if (check_special_flags("use_native_bd_game_engine"))
7225 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7227 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7228 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7230 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7231 CopyNativeLevel_Native_to_RND(level);
7234 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7236 static struct LevelFileInfo level_file_info;
7238 // always start with reliable default values
7239 setFileInfoToDefaults(&level_file_info);
7241 level_file_info.nr = 0; // unknown level number
7242 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7244 setString(&level_file_info.filename, filename);
7246 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7249 static void LoadLevel_InitVersion(struct LevelInfo *level)
7253 if (leveldir_current == NULL) // only when dumping level
7256 // all engine modifications also valid for levels which use latest engine
7257 if (level->game_version < VERSION_IDENT(3,2,0,5))
7259 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7260 level->time_score_base = 10;
7263 if (leveldir_current->latest_engine)
7265 // ---------- use latest game engine --------------------------------------
7267 /* For all levels which are forced to use the latest game engine version
7268 (normally all but user contributed, private and undefined levels), set
7269 the game engine version to the actual version; this allows for actual
7270 corrections in the game engine to take effect for existing, converted
7271 levels (from "classic" or other existing games) to make the emulation
7272 of the corresponding game more accurate, while (hopefully) not breaking
7273 existing levels created from other players. */
7275 level->game_version = GAME_VERSION_ACTUAL;
7277 /* Set special EM style gems behaviour: EM style gems slip down from
7278 normal, steel and growing wall. As this is a more fundamental change,
7279 it seems better to set the default behaviour to "off" (as it is more
7280 natural) and make it configurable in the level editor (as a property
7281 of gem style elements). Already existing converted levels (neither
7282 private nor contributed levels) are changed to the new behaviour. */
7284 if (level->file_version < FILE_VERSION_2_0)
7285 level->em_slippery_gems = TRUE;
7290 // ---------- use game engine the level was created with --------------------
7292 /* For all levels which are not forced to use the latest game engine
7293 version (normally user contributed, private and undefined levels),
7294 use the version of the game engine the levels were created for.
7296 Since 2.0.1, the game engine version is now directly stored
7297 in the level file (chunk "VERS"), so there is no need anymore
7298 to set the game version from the file version (except for old,
7299 pre-2.0 levels, where the game version is still taken from the
7300 file format version used to store the level -- see above). */
7302 // player was faster than enemies in 1.0.0 and before
7303 if (level->file_version == FILE_VERSION_1_0)
7304 for (i = 0; i < MAX_PLAYERS; i++)
7305 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7307 // default behaviour for EM style gems was "slippery" only in 2.0.1
7308 if (level->game_version == VERSION_IDENT(2,0,1,0))
7309 level->em_slippery_gems = TRUE;
7311 // springs could be pushed over pits before (pre-release version) 2.2.0
7312 if (level->game_version < VERSION_IDENT(2,2,0,0))
7313 level->use_spring_bug = TRUE;
7315 if (level->game_version < VERSION_IDENT(3,2,0,5))
7317 // time orb caused limited time in endless time levels before 3.2.0-5
7318 level->use_time_orb_bug = TRUE;
7320 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7321 level->block_snap_field = FALSE;
7323 // extra time score was same value as time left score before 3.2.0-5
7324 level->extra_time_score = level->score[SC_TIME_BONUS];
7327 if (level->game_version < VERSION_IDENT(3,2,0,7))
7329 // default behaviour for snapping was "not continuous" before 3.2.0-7
7330 level->continuous_snapping = FALSE;
7333 // only few elements were able to actively move into acid before 3.1.0
7334 // trigger settings did not exist before 3.1.0; set to default "any"
7335 if (level->game_version < VERSION_IDENT(3,1,0,0))
7337 // correct "can move into acid" settings (all zero in old levels)
7339 level->can_move_into_acid_bits = 0; // nothing can move into acid
7340 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7342 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7343 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7344 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7345 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7347 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7348 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7350 // correct trigger settings (stored as zero == "none" in old levels)
7352 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7354 int element = EL_CUSTOM_START + i;
7355 struct ElementInfo *ei = &element_info[element];
7357 for (j = 0; j < ei->num_change_pages; j++)
7359 struct ElementChangeInfo *change = &ei->change_page[j];
7361 change->trigger_player = CH_PLAYER_ANY;
7362 change->trigger_page = CH_PAGE_ANY;
7367 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7369 int element = EL_CUSTOM_256;
7370 struct ElementInfo *ei = &element_info[element];
7371 struct ElementChangeInfo *change = &ei->change_page[0];
7373 /* This is needed to fix a problem that was caused by a bugfix in function
7374 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7375 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7376 not replace walkable elements, but instead just placed the player on it,
7377 without placing the Sokoban field under the player). Unfortunately, this
7378 breaks "Snake Bite" style levels when the snake is halfway through a door
7379 that just closes (the snake head is still alive and can be moved in this
7380 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7381 player (without Sokoban element) which then gets killed as designed). */
7383 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7384 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7385 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7386 change->target_element = EL_PLAYER_1;
7389 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7390 if (level->game_version < VERSION_IDENT(3,2,5,0))
7392 /* This is needed to fix a problem that was caused by a bugfix in function
7393 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7394 corrects the behaviour when a custom element changes to another custom
7395 element with a higher element number that has change actions defined.
7396 Normally, only one change per frame is allowed for custom elements.
7397 Therefore, it is checked if a custom element already changed in the
7398 current frame; if it did, subsequent changes are suppressed.
7399 Unfortunately, this is only checked for element changes, but not for
7400 change actions, which are still executed. As the function above loops
7401 through all custom elements from lower to higher, an element change
7402 resulting in a lower CE number won't be checked again, while a target
7403 element with a higher number will also be checked, and potential change
7404 actions will get executed for this CE, too (which is wrong), while
7405 further changes are ignored (which is correct). As this bugfix breaks
7406 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7407 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7408 behaviour for existing levels and tapes that make use of this bug */
7410 level->use_action_after_change_bug = TRUE;
7413 // not centering level after relocating player was default only in 3.2.3
7414 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7415 level->shifted_relocation = TRUE;
7417 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7418 if (level->game_version < VERSION_IDENT(3,2,6,0))
7419 level->em_explodes_by_fire = TRUE;
7421 // levels were solved by the first player entering an exit up to 4.1.0.0
7422 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7423 level->solved_by_one_player = TRUE;
7425 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7426 if (level->game_version < VERSION_IDENT(4,1,1,1))
7427 level->use_life_bugs = TRUE;
7429 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7430 if (level->game_version < VERSION_IDENT(4,1,1,1))
7431 level->sb_objects_needed = FALSE;
7433 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7434 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7435 level->finish_dig_collect = FALSE;
7437 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7438 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7439 level->keep_walkable_ce = TRUE;
7442 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7444 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7447 // check if this level is (not) a Sokoban level
7448 for (y = 0; y < level->fieldy; y++)
7449 for (x = 0; x < level->fieldx; x++)
7450 if (!IS_SB_ELEMENT(Tile[x][y]))
7451 is_sokoban_level = FALSE;
7453 if (is_sokoban_level)
7455 // set special level settings for Sokoban levels
7456 SetLevelSettings_SB(level);
7460 static void LoadLevel_InitSettings(struct LevelInfo *level)
7462 // adjust level settings for (non-native) Sokoban-style levels
7463 LoadLevel_InitSettings_SB(level);
7465 // rename levels with title "nameless level" or if renaming is forced
7466 if (leveldir_current->empty_level_name != NULL &&
7467 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7468 leveldir_current->force_level_name))
7469 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7470 leveldir_current->empty_level_name, level_nr);
7473 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7477 // map elements that have changed in newer versions
7478 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7479 level->game_version);
7480 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7481 for (x = 0; x < 3; x++)
7482 for (y = 0; y < 3; y++)
7483 level->yamyam_content[i].e[x][y] =
7484 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7485 level->game_version);
7489 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7493 // map custom element change events that have changed in newer versions
7494 // (these following values were accidentally changed in version 3.0.1)
7495 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7496 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7498 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7500 int element = EL_CUSTOM_START + i;
7502 // order of checking and copying events to be mapped is important
7503 // (do not change the start and end value -- they are constant)
7504 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7506 if (HAS_CHANGE_EVENT(element, j - 2))
7508 SET_CHANGE_EVENT(element, j - 2, FALSE);
7509 SET_CHANGE_EVENT(element, j, TRUE);
7513 // order of checking and copying events to be mapped is important
7514 // (do not change the start and end value -- they are constant)
7515 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7517 if (HAS_CHANGE_EVENT(element, j - 1))
7519 SET_CHANGE_EVENT(element, j - 1, FALSE);
7520 SET_CHANGE_EVENT(element, j, TRUE);
7526 // initialize "can_change" field for old levels with only one change page
7527 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7529 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7531 int element = EL_CUSTOM_START + i;
7533 if (CAN_CHANGE(element))
7534 element_info[element].change->can_change = TRUE;
7538 // correct custom element values (for old levels without these options)
7539 if (level->game_version < VERSION_IDENT(3,1,1,0))
7541 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7543 int element = EL_CUSTOM_START + i;
7544 struct ElementInfo *ei = &element_info[element];
7546 if (ei->access_direction == MV_NO_DIRECTION)
7547 ei->access_direction = MV_ALL_DIRECTIONS;
7551 // correct custom element values (fix invalid values for all versions)
7554 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7556 int element = EL_CUSTOM_START + i;
7557 struct ElementInfo *ei = &element_info[element];
7559 for (j = 0; j < ei->num_change_pages; j++)
7561 struct ElementChangeInfo *change = &ei->change_page[j];
7563 if (change->trigger_player == CH_PLAYER_NONE)
7564 change->trigger_player = CH_PLAYER_ANY;
7566 if (change->trigger_side == CH_SIDE_NONE)
7567 change->trigger_side = CH_SIDE_ANY;
7572 // initialize "can_explode" field for old levels which did not store this
7573 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7574 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7576 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7578 int element = EL_CUSTOM_START + i;
7580 if (EXPLODES_1X1_OLD(element))
7581 element_info[element].explosion_type = EXPLODES_1X1;
7583 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7584 EXPLODES_SMASHED(element) ||
7585 EXPLODES_IMPACT(element)));
7589 // correct previously hard-coded move delay values for maze runner style
7590 if (level->game_version < VERSION_IDENT(3,1,1,0))
7592 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7594 int element = EL_CUSTOM_START + i;
7596 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7598 // previously hard-coded and therefore ignored
7599 element_info[element].move_delay_fixed = 9;
7600 element_info[element].move_delay_random = 0;
7605 // set some other uninitialized values of custom elements in older levels
7606 if (level->game_version < VERSION_IDENT(3,1,0,0))
7608 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7610 int element = EL_CUSTOM_START + i;
7612 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7614 element_info[element].explosion_delay = 17;
7615 element_info[element].ignition_delay = 8;
7619 // set mouse click change events to work for left/middle/right mouse button
7620 if (level->game_version < VERSION_IDENT(4,2,3,0))
7622 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7624 int element = EL_CUSTOM_START + i;
7625 struct ElementInfo *ei = &element_info[element];
7627 for (j = 0; j < ei->num_change_pages; j++)
7629 struct ElementChangeInfo *change = &ei->change_page[j];
7631 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7632 change->has_event[CE_PRESSED_BY_MOUSE] ||
7633 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7634 change->has_event[CE_MOUSE_PRESSED_ON_X])
7635 change->trigger_side = CH_SIDE_ANY;
7641 static void LoadLevel_InitElements(struct LevelInfo *level)
7643 LoadLevel_InitStandardElements(level);
7645 if (level->file_has_custom_elements)
7646 LoadLevel_InitCustomElements(level);
7648 // initialize element properties for level editor etc.
7649 InitElementPropertiesEngine(level->game_version);
7650 InitElementPropertiesGfxElement();
7653 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7657 // map elements that have changed in newer versions
7658 for (y = 0; y < level->fieldy; y++)
7659 for (x = 0; x < level->fieldx; x++)
7660 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7661 level->game_version);
7663 // clear unused playfield data (nicer if level gets resized in editor)
7664 for (x = 0; x < MAX_LEV_FIELDX; x++)
7665 for (y = 0; y < MAX_LEV_FIELDY; y++)
7666 if (x >= level->fieldx || y >= level->fieldy)
7667 level->field[x][y] = EL_EMPTY;
7669 // copy elements to runtime playfield array
7670 for (x = 0; x < MAX_LEV_FIELDX; x++)
7671 for (y = 0; y < MAX_LEV_FIELDY; y++)
7672 Tile[x][y] = level->field[x][y];
7674 // initialize level size variables for faster access
7675 lev_fieldx = level->fieldx;
7676 lev_fieldy = level->fieldy;
7678 // determine border element for this level
7679 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7680 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7685 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7687 struct LevelFileInfo *level_file_info = &level->file_info;
7689 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7690 CopyNativeLevel_RND_to_Native(level);
7693 static void LoadLevelTemplate_LoadAndInit(void)
7695 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7697 LoadLevel_InitVersion(&level_template);
7698 LoadLevel_InitElements(&level_template);
7699 LoadLevel_InitSettings(&level_template);
7701 ActivateLevelTemplate();
7704 void LoadLevelTemplate(int nr)
7706 if (!fileExists(getGlobalLevelTemplateFilename()))
7708 Warn("no level template found for this level");
7713 setLevelFileInfo(&level_template.file_info, nr);
7715 LoadLevelTemplate_LoadAndInit();
7718 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7720 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7722 LoadLevelTemplate_LoadAndInit();
7725 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7727 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7729 if (level.use_custom_template)
7731 if (network_level != NULL)
7732 LoadNetworkLevelTemplate(network_level);
7734 LoadLevelTemplate(-1);
7737 LoadLevel_InitVersion(&level);
7738 LoadLevel_InitElements(&level);
7739 LoadLevel_InitPlayfield(&level);
7740 LoadLevel_InitSettings(&level);
7742 LoadLevel_InitNativeEngines(&level);
7745 void LoadLevel(int nr)
7747 SetLevelSetInfo(leveldir_current->identifier, nr);
7749 setLevelFileInfo(&level.file_info, nr);
7751 LoadLevel_LoadAndInit(NULL);
7754 void LoadLevelInfoOnly(int nr)
7756 setLevelFileInfo(&level.file_info, nr);
7758 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7761 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7763 SetLevelSetInfo(network_level->leveldir_identifier,
7764 network_level->file_info.nr);
7766 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7768 LoadLevel_LoadAndInit(network_level);
7771 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7775 chunk_size += putFileVersion(file, level->file_version);
7776 chunk_size += putFileVersion(file, level->game_version);
7781 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7785 chunk_size += putFile16BitBE(file, level->creation_date.year);
7786 chunk_size += putFile8Bit(file, level->creation_date.month);
7787 chunk_size += putFile8Bit(file, level->creation_date.day);
7792 #if ENABLE_HISTORIC_CHUNKS
7793 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7797 putFile8Bit(file, level->fieldx);
7798 putFile8Bit(file, level->fieldy);
7800 putFile16BitBE(file, level->time);
7801 putFile16BitBE(file, level->gems_needed);
7803 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7804 putFile8Bit(file, level->name[i]);
7806 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7807 putFile8Bit(file, level->score[i]);
7809 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7810 for (y = 0; y < 3; y++)
7811 for (x = 0; x < 3; x++)
7812 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7813 level->yamyam_content[i].e[x][y]));
7814 putFile8Bit(file, level->amoeba_speed);
7815 putFile8Bit(file, level->time_magic_wall);
7816 putFile8Bit(file, level->time_wheel);
7817 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7818 level->amoeba_content));
7819 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7820 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7821 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7822 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7824 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7826 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7827 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7828 putFile32BitBE(file, level->can_move_into_acid_bits);
7829 putFile8Bit(file, level->dont_collide_with_bits);
7831 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7832 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7834 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7835 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7836 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7838 putFile8Bit(file, level->game_engine_type);
7840 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7844 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7849 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7850 chunk_size += putFile8Bit(file, level->name[i]);
7855 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7860 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7861 chunk_size += putFile8Bit(file, level->author[i]);
7866 #if ENABLE_HISTORIC_CHUNKS
7867 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7872 for (y = 0; y < level->fieldy; y++)
7873 for (x = 0; x < level->fieldx; x++)
7874 if (level->encoding_16bit_field)
7875 chunk_size += putFile16BitBE(file, level->field[x][y]);
7877 chunk_size += putFile8Bit(file, level->field[x][y]);
7883 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7888 for (y = 0; y < level->fieldy; y++)
7889 for (x = 0; x < level->fieldx; x++)
7890 chunk_size += putFile16BitBE(file, level->field[x][y]);
7895 #if ENABLE_HISTORIC_CHUNKS
7896 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7900 putFile8Bit(file, EL_YAMYAM);
7901 putFile8Bit(file, level->num_yamyam_contents);
7902 putFile8Bit(file, 0);
7903 putFile8Bit(file, 0);
7905 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7906 for (y = 0; y < 3; y++)
7907 for (x = 0; x < 3; x++)
7908 if (level->encoding_16bit_field)
7909 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7911 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7915 #if ENABLE_HISTORIC_CHUNKS
7916 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7919 int num_contents, content_xsize, content_ysize;
7920 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7922 if (element == EL_YAMYAM)
7924 num_contents = level->num_yamyam_contents;
7928 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7929 for (y = 0; y < 3; y++)
7930 for (x = 0; x < 3; x++)
7931 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7933 else if (element == EL_BD_AMOEBA)
7939 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7940 for (y = 0; y < 3; y++)
7941 for (x = 0; x < 3; x++)
7942 content_array[i][x][y] = EL_EMPTY;
7943 content_array[0][0][0] = level->amoeba_content;
7947 // chunk header already written -- write empty chunk data
7948 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7950 Warn("cannot save content for element '%d'", element);
7955 putFile16BitBE(file, element);
7956 putFile8Bit(file, num_contents);
7957 putFile8Bit(file, content_xsize);
7958 putFile8Bit(file, content_ysize);
7960 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7962 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7963 for (y = 0; y < 3; y++)
7964 for (x = 0; x < 3; x++)
7965 putFile16BitBE(file, content_array[i][x][y]);
7969 #if ENABLE_HISTORIC_CHUNKS
7970 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7972 int envelope_nr = element - EL_ENVELOPE_1;
7973 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7977 chunk_size += putFile16BitBE(file, element);
7978 chunk_size += putFile16BitBE(file, envelope_len);
7979 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7980 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7982 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7983 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7985 for (i = 0; i < envelope_len; i++)
7986 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7992 #if ENABLE_HISTORIC_CHUNKS
7993 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7994 int num_changed_custom_elements)
7998 putFile16BitBE(file, num_changed_custom_elements);
8000 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8002 int element = EL_CUSTOM_START + i;
8004 struct ElementInfo *ei = &element_info[element];
8006 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8008 if (check < num_changed_custom_elements)
8010 putFile16BitBE(file, element);
8011 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8018 if (check != num_changed_custom_elements) // should not happen
8019 Warn("inconsistent number of custom element properties");
8023 #if ENABLE_HISTORIC_CHUNKS
8024 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8025 int num_changed_custom_elements)
8029 putFile16BitBE(file, num_changed_custom_elements);
8031 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8033 int element = EL_CUSTOM_START + i;
8035 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8037 if (check < num_changed_custom_elements)
8039 putFile16BitBE(file, element);
8040 putFile16BitBE(file, element_info[element].change->target_element);
8047 if (check != num_changed_custom_elements) // should not happen
8048 Warn("inconsistent number of custom target elements");
8052 #if ENABLE_HISTORIC_CHUNKS
8053 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8054 int num_changed_custom_elements)
8056 int i, j, x, y, check = 0;
8058 putFile16BitBE(file, num_changed_custom_elements);
8060 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8062 int element = EL_CUSTOM_START + i;
8063 struct ElementInfo *ei = &element_info[element];
8065 if (ei->modified_settings)
8067 if (check < num_changed_custom_elements)
8069 putFile16BitBE(file, element);
8071 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8072 putFile8Bit(file, ei->description[j]);
8074 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8076 // some free bytes for future properties and padding
8077 WriteUnusedBytesToFile(file, 7);
8079 putFile8Bit(file, ei->use_gfx_element);
8080 putFile16BitBE(file, ei->gfx_element_initial);
8082 putFile8Bit(file, ei->collect_score_initial);
8083 putFile8Bit(file, ei->collect_count_initial);
8085 putFile16BitBE(file, ei->push_delay_fixed);
8086 putFile16BitBE(file, ei->push_delay_random);
8087 putFile16BitBE(file, ei->move_delay_fixed);
8088 putFile16BitBE(file, ei->move_delay_random);
8090 putFile16BitBE(file, ei->move_pattern);
8091 putFile8Bit(file, ei->move_direction_initial);
8092 putFile8Bit(file, ei->move_stepsize);
8094 for (y = 0; y < 3; y++)
8095 for (x = 0; x < 3; x++)
8096 putFile16BitBE(file, ei->content.e[x][y]);
8098 putFile32BitBE(file, ei->change->events);
8100 putFile16BitBE(file, ei->change->target_element);
8102 putFile16BitBE(file, ei->change->delay_fixed);
8103 putFile16BitBE(file, ei->change->delay_random);
8104 putFile16BitBE(file, ei->change->delay_frames);
8106 putFile16BitBE(file, ei->change->initial_trigger_element);
8108 putFile8Bit(file, ei->change->explode);
8109 putFile8Bit(file, ei->change->use_target_content);
8110 putFile8Bit(file, ei->change->only_if_complete);
8111 putFile8Bit(file, ei->change->use_random_replace);
8113 putFile8Bit(file, ei->change->random_percentage);
8114 putFile8Bit(file, ei->change->replace_when);
8116 for (y = 0; y < 3; y++)
8117 for (x = 0; x < 3; x++)
8118 putFile16BitBE(file, ei->change->content.e[x][y]);
8120 putFile8Bit(file, ei->slippery_type);
8122 // some free bytes for future properties and padding
8123 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8130 if (check != num_changed_custom_elements) // should not happen
8131 Warn("inconsistent number of custom element properties");
8135 #if ENABLE_HISTORIC_CHUNKS
8136 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8138 struct ElementInfo *ei = &element_info[element];
8141 // ---------- custom element base property values (96 bytes) ----------------
8143 putFile16BitBE(file, element);
8145 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8146 putFile8Bit(file, ei->description[i]);
8148 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8150 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8152 putFile8Bit(file, ei->num_change_pages);
8154 putFile16BitBE(file, ei->ce_value_fixed_initial);
8155 putFile16BitBE(file, ei->ce_value_random_initial);
8156 putFile8Bit(file, ei->use_last_ce_value);
8158 putFile8Bit(file, ei->use_gfx_element);
8159 putFile16BitBE(file, ei->gfx_element_initial);
8161 putFile8Bit(file, ei->collect_score_initial);
8162 putFile8Bit(file, ei->collect_count_initial);
8164 putFile8Bit(file, ei->drop_delay_fixed);
8165 putFile8Bit(file, ei->push_delay_fixed);
8166 putFile8Bit(file, ei->drop_delay_random);
8167 putFile8Bit(file, ei->push_delay_random);
8168 putFile16BitBE(file, ei->move_delay_fixed);
8169 putFile16BitBE(file, ei->move_delay_random);
8171 // bits 0 - 15 of "move_pattern" ...
8172 putFile16BitBE(file, ei->move_pattern & 0xffff);
8173 putFile8Bit(file, ei->move_direction_initial);
8174 putFile8Bit(file, ei->move_stepsize);
8176 putFile8Bit(file, ei->slippery_type);
8178 for (y = 0; y < 3; y++)
8179 for (x = 0; x < 3; x++)
8180 putFile16BitBE(file, ei->content.e[x][y]);
8182 putFile16BitBE(file, ei->move_enter_element);
8183 putFile16BitBE(file, ei->move_leave_element);
8184 putFile8Bit(file, ei->move_leave_type);
8186 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8187 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8189 putFile8Bit(file, ei->access_direction);
8191 putFile8Bit(file, ei->explosion_delay);
8192 putFile8Bit(file, ei->ignition_delay);
8193 putFile8Bit(file, ei->explosion_type);
8195 // some free bytes for future custom property values and padding
8196 WriteUnusedBytesToFile(file, 1);
8198 // ---------- change page property values (48 bytes) ------------------------
8200 for (i = 0; i < ei->num_change_pages; i++)
8202 struct ElementChangeInfo *change = &ei->change_page[i];
8203 unsigned int event_bits;
8205 // bits 0 - 31 of "has_event[]" ...
8207 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8208 if (change->has_event[j])
8209 event_bits |= (1u << j);
8210 putFile32BitBE(file, event_bits);
8212 putFile16BitBE(file, change->target_element);
8214 putFile16BitBE(file, change->delay_fixed);
8215 putFile16BitBE(file, change->delay_random);
8216 putFile16BitBE(file, change->delay_frames);
8218 putFile16BitBE(file, change->initial_trigger_element);
8220 putFile8Bit(file, change->explode);
8221 putFile8Bit(file, change->use_target_content);
8222 putFile8Bit(file, change->only_if_complete);
8223 putFile8Bit(file, change->use_random_replace);
8225 putFile8Bit(file, change->random_percentage);
8226 putFile8Bit(file, change->replace_when);
8228 for (y = 0; y < 3; y++)
8229 for (x = 0; x < 3; x++)
8230 putFile16BitBE(file, change->target_content.e[x][y]);
8232 putFile8Bit(file, change->can_change);
8234 putFile8Bit(file, change->trigger_side);
8236 putFile8Bit(file, change->trigger_player);
8237 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8238 log_2(change->trigger_page)));
8240 putFile8Bit(file, change->has_action);
8241 putFile8Bit(file, change->action_type);
8242 putFile8Bit(file, change->action_mode);
8243 putFile16BitBE(file, change->action_arg);
8245 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8247 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8248 if (change->has_event[j])
8249 event_bits |= (1u << (j - 32));
8250 putFile8Bit(file, event_bits);
8255 #if ENABLE_HISTORIC_CHUNKS
8256 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8258 struct ElementInfo *ei = &element_info[element];
8259 struct ElementGroupInfo *group = ei->group;
8262 putFile16BitBE(file, element);
8264 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8265 putFile8Bit(file, ei->description[i]);
8267 putFile8Bit(file, group->num_elements);
8269 putFile8Bit(file, ei->use_gfx_element);
8270 putFile16BitBE(file, ei->gfx_element_initial);
8272 putFile8Bit(file, group->choice_mode);
8274 // some free bytes for future values and padding
8275 WriteUnusedBytesToFile(file, 3);
8277 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8278 putFile16BitBE(file, group->element[i]);
8282 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8283 boolean write_element)
8285 int save_type = entry->save_type;
8286 int data_type = entry->data_type;
8287 int conf_type = entry->conf_type;
8288 int byte_mask = conf_type & CONF_MASK_BYTES;
8289 int element = entry->element;
8290 int default_value = entry->default_value;
8292 boolean modified = FALSE;
8294 if (byte_mask != CONF_MASK_MULTI_BYTES)
8296 void *value_ptr = entry->value;
8297 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8300 // check if any settings have been modified before saving them
8301 if (value != default_value)
8304 // do not save if explicitly told or if unmodified default settings
8305 if ((save_type == SAVE_CONF_NEVER) ||
8306 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8310 num_bytes += putFile16BitBE(file, element);
8312 num_bytes += putFile8Bit(file, conf_type);
8313 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8314 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8315 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8318 else if (data_type == TYPE_STRING)
8320 char *default_string = entry->default_string;
8321 char *string = (char *)(entry->value);
8322 int string_length = strlen(string);
8325 // check if any settings have been modified before saving them
8326 if (!strEqual(string, default_string))
8329 // do not save if explicitly told or if unmodified default settings
8330 if ((save_type == SAVE_CONF_NEVER) ||
8331 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8335 num_bytes += putFile16BitBE(file, element);
8337 num_bytes += putFile8Bit(file, conf_type);
8338 num_bytes += putFile16BitBE(file, string_length);
8340 for (i = 0; i < string_length; i++)
8341 num_bytes += putFile8Bit(file, string[i]);
8343 else if (data_type == TYPE_ELEMENT_LIST)
8345 int *element_array = (int *)(entry->value);
8346 int num_elements = *(int *)(entry->num_entities);
8349 // check if any settings have been modified before saving them
8350 for (i = 0; i < num_elements; i++)
8351 if (element_array[i] != default_value)
8354 // do not save if explicitly told or if unmodified default settings
8355 if ((save_type == SAVE_CONF_NEVER) ||
8356 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8360 num_bytes += putFile16BitBE(file, element);
8362 num_bytes += putFile8Bit(file, conf_type);
8363 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8365 for (i = 0; i < num_elements; i++)
8366 num_bytes += putFile16BitBE(file, element_array[i]);
8368 else if (data_type == TYPE_CONTENT_LIST)
8370 struct Content *content = (struct Content *)(entry->value);
8371 int num_contents = *(int *)(entry->num_entities);
8374 // check if any settings have been modified before saving them
8375 for (i = 0; i < num_contents; i++)
8376 for (y = 0; y < 3; y++)
8377 for (x = 0; x < 3; x++)
8378 if (content[i].e[x][y] != default_value)
8381 // do not save if explicitly told or if unmodified default settings
8382 if ((save_type == SAVE_CONF_NEVER) ||
8383 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8387 num_bytes += putFile16BitBE(file, element);
8389 num_bytes += putFile8Bit(file, conf_type);
8390 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8392 for (i = 0; i < num_contents; i++)
8393 for (y = 0; y < 3; y++)
8394 for (x = 0; x < 3; x++)
8395 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8401 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8406 li = *level; // copy level data into temporary buffer
8408 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8409 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8414 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8419 li = *level; // copy level data into temporary buffer
8421 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8422 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8427 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8429 int envelope_nr = element - EL_ENVELOPE_1;
8433 chunk_size += putFile16BitBE(file, element);
8435 // copy envelope data into temporary buffer
8436 xx_envelope = level->envelope[envelope_nr];
8438 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8439 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8444 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8446 struct ElementInfo *ei = &element_info[element];
8450 chunk_size += putFile16BitBE(file, element);
8452 xx_ei = *ei; // copy element data into temporary buffer
8454 // set default description string for this specific element
8455 strcpy(xx_default_description, getDefaultElementDescription(ei));
8457 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8458 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8460 for (i = 0; i < ei->num_change_pages; i++)
8462 struct ElementChangeInfo *change = &ei->change_page[i];
8464 xx_current_change_page = i;
8466 xx_change = *change; // copy change data into temporary buffer
8469 setEventBitsFromEventFlags(change);
8471 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8472 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8479 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8481 struct ElementInfo *ei = &element_info[element];
8482 struct ElementGroupInfo *group = ei->group;
8486 chunk_size += putFile16BitBE(file, element);
8488 xx_ei = *ei; // copy element data into temporary buffer
8489 xx_group = *group; // copy group data into temporary buffer
8491 // set default description string for this specific element
8492 strcpy(xx_default_description, getDefaultElementDescription(ei));
8494 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8495 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8500 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8502 struct ElementInfo *ei = &element_info[element];
8506 chunk_size += putFile16BitBE(file, element);
8508 xx_ei = *ei; // copy element data into temporary buffer
8510 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8511 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8516 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8517 boolean save_as_template)
8523 if (!(file = fopen(filename, MODE_WRITE)))
8525 Warn("cannot save level file '%s'", filename);
8530 level->file_version = FILE_VERSION_ACTUAL;
8531 level->game_version = GAME_VERSION_ACTUAL;
8533 level->creation_date = getCurrentDate();
8535 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8536 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8538 chunk_size = SaveLevel_VERS(NULL, level);
8539 putFileChunkBE(file, "VERS", chunk_size);
8540 SaveLevel_VERS(file, level);
8542 chunk_size = SaveLevel_DATE(NULL, level);
8543 putFileChunkBE(file, "DATE", chunk_size);
8544 SaveLevel_DATE(file, level);
8546 chunk_size = SaveLevel_NAME(NULL, level);
8547 putFileChunkBE(file, "NAME", chunk_size);
8548 SaveLevel_NAME(file, level);
8550 chunk_size = SaveLevel_AUTH(NULL, level);
8551 putFileChunkBE(file, "AUTH", chunk_size);
8552 SaveLevel_AUTH(file, level);
8554 chunk_size = SaveLevel_INFO(NULL, level);
8555 putFileChunkBE(file, "INFO", chunk_size);
8556 SaveLevel_INFO(file, level);
8558 chunk_size = SaveLevel_BODY(NULL, level);
8559 putFileChunkBE(file, "BODY", chunk_size);
8560 SaveLevel_BODY(file, level);
8562 chunk_size = SaveLevel_ELEM(NULL, level);
8563 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8565 putFileChunkBE(file, "ELEM", chunk_size);
8566 SaveLevel_ELEM(file, level);
8569 for (i = 0; i < NUM_ENVELOPES; i++)
8571 int element = EL_ENVELOPE_1 + i;
8573 chunk_size = SaveLevel_NOTE(NULL, level, element);
8574 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8576 putFileChunkBE(file, "NOTE", chunk_size);
8577 SaveLevel_NOTE(file, level, element);
8581 // if not using template level, check for non-default custom/group elements
8582 if (!level->use_custom_template || save_as_template)
8584 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8586 int element = EL_CUSTOM_START + i;
8588 chunk_size = SaveLevel_CUSX(NULL, level, element);
8589 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8591 putFileChunkBE(file, "CUSX", chunk_size);
8592 SaveLevel_CUSX(file, level, element);
8596 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8598 int element = EL_GROUP_START + i;
8600 chunk_size = SaveLevel_GRPX(NULL, level, element);
8601 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8603 putFileChunkBE(file, "GRPX", chunk_size);
8604 SaveLevel_GRPX(file, level, element);
8608 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8610 int element = GET_EMPTY_ELEMENT(i);
8612 chunk_size = SaveLevel_EMPX(NULL, level, element);
8613 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8615 putFileChunkBE(file, "EMPX", chunk_size);
8616 SaveLevel_EMPX(file, level, element);
8623 SetFilePermissions(filename, PERMS_PRIVATE);
8626 void SaveLevel(int nr)
8628 char *filename = getDefaultLevelFilename(nr);
8630 SaveLevelFromFilename(&level, filename, FALSE);
8633 void SaveLevelTemplate(void)
8635 char *filename = getLocalLevelTemplateFilename();
8637 SaveLevelFromFilename(&level, filename, TRUE);
8640 boolean SaveLevelChecked(int nr)
8642 char *filename = getDefaultLevelFilename(nr);
8643 boolean new_level = !fileExists(filename);
8644 boolean level_saved = FALSE;
8646 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8651 Request("Level saved!", REQ_CONFIRM);
8659 void DumpLevel(struct LevelInfo *level)
8661 if (level->no_level_file || level->no_valid_file)
8663 Warn("cannot dump -- no valid level file found");
8669 Print("Level xxx (file version %08d, game version %08d)\n",
8670 level->file_version, level->game_version);
8673 Print("Level author: '%s'\n", level->author);
8674 Print("Level title: '%s'\n", level->name);
8676 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8678 Print("Level time: %d seconds\n", level->time);
8679 Print("Gems needed: %d\n", level->gems_needed);
8681 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8682 Print("Time for wheel: %d seconds\n", level->time_wheel);
8683 Print("Time for light: %d seconds\n", level->time_light);
8684 Print("Time for timegate: %d seconds\n", level->time_timegate);
8686 Print("Amoeba speed: %d\n", level->amoeba_speed);
8689 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8690 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8691 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8692 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8693 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8694 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8700 for (i = 0; i < NUM_ENVELOPES; i++)
8702 char *text = level->envelope[i].text;
8703 int text_len = strlen(text);
8704 boolean has_text = FALSE;
8706 for (j = 0; j < text_len; j++)
8707 if (text[j] != ' ' && text[j] != '\n')
8713 Print("Envelope %d:\n'%s'\n", i + 1, text);
8721 void DumpLevels(void)
8723 static LevelDirTree *dumplevel_leveldir = NULL;
8725 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8726 global.dumplevel_leveldir);
8728 if (dumplevel_leveldir == NULL)
8729 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8731 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8732 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8733 Fail("no such level number: %d", global.dumplevel_level_nr);
8735 leveldir_current = dumplevel_leveldir;
8737 LoadLevel(global.dumplevel_level_nr);
8744 // ============================================================================
8745 // tape file functions
8746 // ============================================================================
8748 static void setTapeInfoToDefaults(void)
8752 // always start with reliable default values (empty tape)
8755 // default values (also for pre-1.2 tapes) with only the first player
8756 tape.player_participates[0] = TRUE;
8757 for (i = 1; i < MAX_PLAYERS; i++)
8758 tape.player_participates[i] = FALSE;
8760 // at least one (default: the first) player participates in every tape
8761 tape.num_participating_players = 1;
8763 tape.property_bits = TAPE_PROPERTY_NONE;
8765 tape.level_nr = level_nr;
8767 tape.changed = FALSE;
8768 tape.solved = FALSE;
8770 tape.recording = FALSE;
8771 tape.playing = FALSE;
8772 tape.pausing = FALSE;
8774 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8775 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8777 tape.no_info_chunk = TRUE;
8778 tape.no_valid_file = FALSE;
8781 static int getTapePosSize(struct TapeInfo *tape)
8783 int tape_pos_size = 0;
8785 if (tape->use_key_actions)
8786 tape_pos_size += tape->num_participating_players;
8788 if (tape->use_mouse_actions)
8789 tape_pos_size += 3; // x and y position and mouse button mask
8791 tape_pos_size += 1; // tape action delay value
8793 return tape_pos_size;
8796 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8798 tape->use_key_actions = FALSE;
8799 tape->use_mouse_actions = FALSE;
8801 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8802 tape->use_key_actions = TRUE;
8804 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8805 tape->use_mouse_actions = TRUE;
8808 static int getTapeActionValue(struct TapeInfo *tape)
8810 return (tape->use_key_actions &&
8811 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8812 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8813 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8814 TAPE_ACTIONS_DEFAULT);
8817 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8819 tape->file_version = getFileVersion(file);
8820 tape->game_version = getFileVersion(file);
8825 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8829 tape->random_seed = getFile32BitBE(file);
8830 tape->date = getFile32BitBE(file);
8831 tape->length = getFile32BitBE(file);
8833 // read header fields that are new since version 1.2
8834 if (tape->file_version >= FILE_VERSION_1_2)
8836 byte store_participating_players = getFile8Bit(file);
8839 // since version 1.2, tapes store which players participate in the tape
8840 tape->num_participating_players = 0;
8841 for (i = 0; i < MAX_PLAYERS; i++)
8843 tape->player_participates[i] = FALSE;
8845 if (store_participating_players & (1 << i))
8847 tape->player_participates[i] = TRUE;
8848 tape->num_participating_players++;
8852 setTapeActionFlags(tape, getFile8Bit(file));
8854 tape->property_bits = getFile8Bit(file);
8855 tape->solved = getFile8Bit(file);
8857 engine_version = getFileVersion(file);
8858 if (engine_version > 0)
8859 tape->engine_version = engine_version;
8861 tape->engine_version = tape->game_version;
8867 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8869 tape->scr_fieldx = getFile8Bit(file);
8870 tape->scr_fieldy = getFile8Bit(file);
8875 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8877 char *level_identifier = NULL;
8878 int level_identifier_size;
8881 tape->no_info_chunk = FALSE;
8883 level_identifier_size = getFile16BitBE(file);
8885 level_identifier = checked_malloc(level_identifier_size);
8887 for (i = 0; i < level_identifier_size; i++)
8888 level_identifier[i] = getFile8Bit(file);
8890 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8891 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8893 checked_free(level_identifier);
8895 tape->level_nr = getFile16BitBE(file);
8897 chunk_size = 2 + level_identifier_size + 2;
8902 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8905 int tape_pos_size = getTapePosSize(tape);
8906 int chunk_size_expected = tape_pos_size * tape->length;
8908 if (chunk_size_expected != chunk_size)
8910 ReadUnusedBytesFromFile(file, chunk_size);
8911 return chunk_size_expected;
8914 for (i = 0; i < tape->length; i++)
8916 if (i >= MAX_TAPE_LEN)
8918 Warn("tape truncated -- size exceeds maximum tape size %d",
8921 // tape too large; read and ignore remaining tape data from this chunk
8922 for (;i < tape->length; i++)
8923 ReadUnusedBytesFromFile(file, tape_pos_size);
8928 if (tape->use_key_actions)
8930 for (j = 0; j < MAX_PLAYERS; j++)
8932 tape->pos[i].action[j] = MV_NONE;
8934 if (tape->player_participates[j])
8935 tape->pos[i].action[j] = getFile8Bit(file);
8939 if (tape->use_mouse_actions)
8941 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8942 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8943 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8946 tape->pos[i].delay = getFile8Bit(file);
8948 if (tape->file_version == FILE_VERSION_1_0)
8950 // eliminate possible diagonal moves in old tapes
8951 // this is only for backward compatibility
8953 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8954 byte action = tape->pos[i].action[0];
8955 int k, num_moves = 0;
8957 for (k = 0; k < 4; k++)
8959 if (action & joy_dir[k])
8961 tape->pos[i + num_moves].action[0] = joy_dir[k];
8963 tape->pos[i + num_moves].delay = 0;
8972 tape->length += num_moves;
8975 else if (tape->file_version < FILE_VERSION_2_0)
8977 // convert pre-2.0 tapes to new tape format
8979 if (tape->pos[i].delay > 1)
8982 tape->pos[i + 1] = tape->pos[i];
8983 tape->pos[i + 1].delay = 1;
8986 for (j = 0; j < MAX_PLAYERS; j++)
8987 tape->pos[i].action[j] = MV_NONE;
8988 tape->pos[i].delay--;
8995 if (checkEndOfFile(file))
8999 if (i != tape->length)
9000 chunk_size = tape_pos_size * i;
9005 static void LoadTape_SokobanSolution(char *filename)
9008 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9010 if (!(file = openFile(filename, MODE_READ)))
9012 tape.no_valid_file = TRUE;
9017 while (!checkEndOfFile(file))
9019 unsigned char c = getByteFromFile(file);
9021 if (checkEndOfFile(file))
9028 tape.pos[tape.length].action[0] = MV_UP;
9029 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9035 tape.pos[tape.length].action[0] = MV_DOWN;
9036 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9042 tape.pos[tape.length].action[0] = MV_LEFT;
9043 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9049 tape.pos[tape.length].action[0] = MV_RIGHT;
9050 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9058 // ignore white-space characters
9062 tape.no_valid_file = TRUE;
9064 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9072 if (tape.no_valid_file)
9075 tape.length_frames = GetTapeLengthFrames();
9076 tape.length_seconds = GetTapeLengthSeconds();
9079 void LoadTapeFromFilename(char *filename)
9081 char cookie[MAX_LINE_LEN];
9082 char chunk_name[CHUNK_ID_LEN + 1];
9086 // always start with reliable default values
9087 setTapeInfoToDefaults();
9089 if (strSuffix(filename, ".sln"))
9091 LoadTape_SokobanSolution(filename);
9096 if (!(file = openFile(filename, MODE_READ)))
9098 tape.no_valid_file = TRUE;
9103 getFileChunkBE(file, chunk_name, NULL);
9104 if (strEqual(chunk_name, "RND1"))
9106 getFile32BitBE(file); // not used
9108 getFileChunkBE(file, chunk_name, NULL);
9109 if (!strEqual(chunk_name, "TAPE"))
9111 tape.no_valid_file = TRUE;
9113 Warn("unknown format of tape file '%s'", filename);
9120 else // check for pre-2.0 file format with cookie string
9122 strcpy(cookie, chunk_name);
9123 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9125 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9126 cookie[strlen(cookie) - 1] = '\0';
9128 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9130 tape.no_valid_file = TRUE;
9132 Warn("unknown format of tape file '%s'", filename);
9139 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9141 tape.no_valid_file = TRUE;
9143 Warn("unsupported version of tape file '%s'", filename);
9150 // pre-2.0 tape files have no game version, so use file version here
9151 tape.game_version = tape.file_version;
9154 if (tape.file_version < FILE_VERSION_1_2)
9156 // tape files from versions before 1.2.0 without chunk structure
9157 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9158 LoadTape_BODY(file, 2 * tape.length, &tape);
9166 int (*loader)(File *, int, struct TapeInfo *);
9170 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9171 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9172 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9173 { "INFO", -1, LoadTape_INFO },
9174 { "BODY", -1, LoadTape_BODY },
9178 while (getFileChunkBE(file, chunk_name, &chunk_size))
9182 while (chunk_info[i].name != NULL &&
9183 !strEqual(chunk_name, chunk_info[i].name))
9186 if (chunk_info[i].name == NULL)
9188 Warn("unknown chunk '%s' in tape file '%s'",
9189 chunk_name, filename);
9191 ReadUnusedBytesFromFile(file, chunk_size);
9193 else if (chunk_info[i].size != -1 &&
9194 chunk_info[i].size != chunk_size)
9196 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9197 chunk_size, chunk_name, filename);
9199 ReadUnusedBytesFromFile(file, chunk_size);
9203 // call function to load this tape chunk
9204 int chunk_size_expected =
9205 (chunk_info[i].loader)(file, chunk_size, &tape);
9207 // the size of some chunks cannot be checked before reading other
9208 // chunks first (like "HEAD" and "BODY") that contain some header
9209 // information, so check them here
9210 if (chunk_size_expected != chunk_size)
9212 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9213 chunk_size, chunk_name, filename);
9221 tape.length_frames = GetTapeLengthFrames();
9222 tape.length_seconds = GetTapeLengthSeconds();
9225 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9227 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9229 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9230 tape.engine_version);
9234 void LoadTape(int nr)
9236 char *filename = getTapeFilename(nr);
9238 LoadTapeFromFilename(filename);
9241 void LoadSolutionTape(int nr)
9243 char *filename = getSolutionTapeFilename(nr);
9245 LoadTapeFromFilename(filename);
9247 if (TAPE_IS_EMPTY(tape))
9249 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9250 level.native_bd_level->replay != NULL)
9251 CopyNativeTape_BD_to_RND(&level);
9252 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9253 level.native_sp_level->demo.is_available)
9254 CopyNativeTape_SP_to_RND(&level);
9258 void LoadScoreTape(char *score_tape_basename, int nr)
9260 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9262 LoadTapeFromFilename(filename);
9265 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9267 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9269 LoadTapeFromFilename(filename);
9272 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9274 // chunk required for team mode tapes with non-default screen size
9275 return (tape->num_participating_players > 1 &&
9276 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9277 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9280 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9282 putFileVersion(file, tape->file_version);
9283 putFileVersion(file, tape->game_version);
9286 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9289 byte store_participating_players = 0;
9291 // set bits for participating players for compact storage
9292 for (i = 0; i < MAX_PLAYERS; i++)
9293 if (tape->player_participates[i])
9294 store_participating_players |= (1 << i);
9296 putFile32BitBE(file, tape->random_seed);
9297 putFile32BitBE(file, tape->date);
9298 putFile32BitBE(file, tape->length);
9300 putFile8Bit(file, store_participating_players);
9302 putFile8Bit(file, getTapeActionValue(tape));
9304 putFile8Bit(file, tape->property_bits);
9305 putFile8Bit(file, tape->solved);
9307 putFileVersion(file, tape->engine_version);
9310 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9312 putFile8Bit(file, tape->scr_fieldx);
9313 putFile8Bit(file, tape->scr_fieldy);
9316 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9318 int level_identifier_size = strlen(tape->level_identifier) + 1;
9321 putFile16BitBE(file, level_identifier_size);
9323 for (i = 0; i < level_identifier_size; i++)
9324 putFile8Bit(file, tape->level_identifier[i]);
9326 putFile16BitBE(file, tape->level_nr);
9329 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9333 for (i = 0; i < tape->length; i++)
9335 if (tape->use_key_actions)
9337 for (j = 0; j < MAX_PLAYERS; j++)
9338 if (tape->player_participates[j])
9339 putFile8Bit(file, tape->pos[i].action[j]);
9342 if (tape->use_mouse_actions)
9344 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9345 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9346 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9349 putFile8Bit(file, tape->pos[i].delay);
9353 void SaveTapeToFilename(char *filename)
9357 int info_chunk_size;
9358 int body_chunk_size;
9360 if (!(file = fopen(filename, MODE_WRITE)))
9362 Warn("cannot save level recording file '%s'", filename);
9367 tape_pos_size = getTapePosSize(&tape);
9369 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9370 body_chunk_size = tape_pos_size * tape.length;
9372 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9373 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9375 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9376 SaveTape_VERS(file, &tape);
9378 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9379 SaveTape_HEAD(file, &tape);
9381 if (checkSaveTape_SCRN(&tape))
9383 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9384 SaveTape_SCRN(file, &tape);
9387 putFileChunkBE(file, "INFO", info_chunk_size);
9388 SaveTape_INFO(file, &tape);
9390 putFileChunkBE(file, "BODY", body_chunk_size);
9391 SaveTape_BODY(file, &tape);
9395 SetFilePermissions(filename, PERMS_PRIVATE);
9398 static void SaveTapeExt(char *filename)
9402 tape.file_version = FILE_VERSION_ACTUAL;
9403 tape.game_version = GAME_VERSION_ACTUAL;
9405 tape.num_participating_players = 0;
9407 // count number of participating players
9408 for (i = 0; i < MAX_PLAYERS; i++)
9409 if (tape.player_participates[i])
9410 tape.num_participating_players++;
9412 SaveTapeToFilename(filename);
9414 tape.changed = FALSE;
9417 void SaveTape(int nr)
9419 char *filename = getTapeFilename(nr);
9421 InitTapeDirectory(leveldir_current->subdir);
9423 SaveTapeExt(filename);
9426 void SaveScoreTape(int nr)
9428 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9430 // used instead of "leveldir_current->subdir" (for network games)
9431 InitScoreTapeDirectory(levelset.identifier, nr);
9433 SaveTapeExt(filename);
9436 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9437 unsigned int req_state_added)
9439 char *filename = getTapeFilename(nr);
9440 boolean new_tape = !fileExists(filename);
9441 boolean tape_saved = FALSE;
9443 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9448 Request(msg_saved, REQ_CONFIRM | req_state_added);
9456 boolean SaveTapeChecked(int nr)
9458 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9461 boolean SaveTapeChecked_LevelSolved(int nr)
9463 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9464 "Level solved! Tape saved!", REQ_STAY_OPEN);
9467 void DumpTape(struct TapeInfo *tape)
9469 int tape_frame_counter;
9472 if (tape->no_valid_file)
9474 Warn("cannot dump -- no valid tape file found");
9481 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9482 tape->level_nr, tape->file_version, tape->game_version);
9483 Print(" (effective engine version %08d)\n",
9484 tape->engine_version);
9485 Print("Level series identifier: '%s'\n", tape->level_identifier);
9487 Print("Solution tape: %s\n",
9488 tape->solved ? "yes" :
9489 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9491 Print("Special tape properties: ");
9492 if (tape->property_bits == TAPE_PROPERTY_NONE)
9494 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9495 Print("[em_random_bug]");
9496 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9497 Print("[game_speed]");
9498 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9500 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9501 Print("[single_step]");
9502 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9503 Print("[snapshot]");
9504 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9505 Print("[replayed]");
9506 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9507 Print("[tas_keys]");
9508 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9509 Print("[small_graphics]");
9512 int year2 = tape->date / 10000;
9513 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9514 int month_index_raw = (tape->date / 100) % 100;
9515 int month_index = month_index_raw % 12; // prevent invalid index
9516 int month = month_index + 1;
9517 int day = tape->date % 100;
9519 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9523 tape_frame_counter = 0;
9525 for (i = 0; i < tape->length; i++)
9527 if (i >= MAX_TAPE_LEN)
9532 for (j = 0; j < MAX_PLAYERS; j++)
9534 if (tape->player_participates[j])
9536 int action = tape->pos[i].action[j];
9538 Print("%d:%02x ", j, action);
9539 Print("[%c%c%c%c|%c%c] - ",
9540 (action & JOY_LEFT ? '<' : ' '),
9541 (action & JOY_RIGHT ? '>' : ' '),
9542 (action & JOY_UP ? '^' : ' '),
9543 (action & JOY_DOWN ? 'v' : ' '),
9544 (action & JOY_BUTTON_1 ? '1' : ' '),
9545 (action & JOY_BUTTON_2 ? '2' : ' '));
9549 Print("(%03d) ", tape->pos[i].delay);
9550 Print("[%05d]\n", tape_frame_counter);
9552 tape_frame_counter += tape->pos[i].delay;
9558 void DumpTapes(void)
9560 static LevelDirTree *dumptape_leveldir = NULL;
9562 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9563 global.dumptape_leveldir);
9565 if (dumptape_leveldir == NULL)
9566 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9568 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9569 global.dumptape_level_nr > dumptape_leveldir->last_level)
9570 Fail("no such level number: %d", global.dumptape_level_nr);
9572 leveldir_current = dumptape_leveldir;
9574 if (options.mytapes)
9575 LoadTape(global.dumptape_level_nr);
9577 LoadSolutionTape(global.dumptape_level_nr);
9585 // ============================================================================
9586 // score file functions
9587 // ============================================================================
9589 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9593 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9595 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9596 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9597 scores->entry[i].score = 0;
9598 scores->entry[i].time = 0;
9600 scores->entry[i].id = -1;
9601 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9602 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9603 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9604 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9605 strcpy(scores->entry[i].country_code, "??");
9608 scores->num_entries = 0;
9609 scores->last_added = -1;
9610 scores->last_added_local = -1;
9612 scores->updated = FALSE;
9613 scores->uploaded = FALSE;
9614 scores->tape_downloaded = FALSE;
9615 scores->force_last_added = FALSE;
9617 // The following values are intentionally not reset here:
9621 // - continue_playing
9622 // - continue_on_return
9625 static void setScoreInfoToDefaults(void)
9627 setScoreInfoToDefaultsExt(&scores);
9630 static void setServerScoreInfoToDefaults(void)
9632 setScoreInfoToDefaultsExt(&server_scores);
9635 static void LoadScore_OLD(int nr)
9638 char *filename = getScoreFilename(nr);
9639 char cookie[MAX_LINE_LEN];
9640 char line[MAX_LINE_LEN];
9644 if (!(file = fopen(filename, MODE_READ)))
9647 // check file identifier
9648 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9650 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9651 cookie[strlen(cookie) - 1] = '\0';
9653 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9655 Warn("unknown format of score file '%s'", filename);
9662 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9664 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9665 Warn("fscanf() failed; %s", strerror(errno));
9667 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9670 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9671 line[strlen(line) - 1] = '\0';
9673 for (line_ptr = line; *line_ptr; line_ptr++)
9675 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9677 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9678 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9687 static void ConvertScore_OLD(void)
9689 // only convert score to time for levels that rate playing time over score
9690 if (!level.rate_time_over_score)
9693 // convert old score to playing time for score-less levels (like Supaplex)
9694 int time_final_max = 999;
9697 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9699 int score = scores.entry[i].score;
9701 if (score > 0 && score < time_final_max)
9702 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9706 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9708 scores->file_version = getFileVersion(file);
9709 scores->game_version = getFileVersion(file);
9714 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9716 char *level_identifier = NULL;
9717 int level_identifier_size;
9720 level_identifier_size = getFile16BitBE(file);
9722 level_identifier = checked_malloc(level_identifier_size);
9724 for (i = 0; i < level_identifier_size; i++)
9725 level_identifier[i] = getFile8Bit(file);
9727 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9728 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9730 checked_free(level_identifier);
9732 scores->level_nr = getFile16BitBE(file);
9733 scores->num_entries = getFile16BitBE(file);
9735 chunk_size = 2 + level_identifier_size + 2 + 2;
9740 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9744 for (i = 0; i < scores->num_entries; i++)
9746 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9747 scores->entry[i].name[j] = getFile8Bit(file);
9749 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9752 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9757 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9761 for (i = 0; i < scores->num_entries; i++)
9762 scores->entry[i].score = getFile16BitBE(file);
9764 chunk_size = scores->num_entries * 2;
9769 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9773 for (i = 0; i < scores->num_entries; i++)
9774 scores->entry[i].score = getFile32BitBE(file);
9776 chunk_size = scores->num_entries * 4;
9781 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9785 for (i = 0; i < scores->num_entries; i++)
9786 scores->entry[i].time = getFile32BitBE(file);
9788 chunk_size = scores->num_entries * 4;
9793 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9797 for (i = 0; i < scores->num_entries; i++)
9799 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9800 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9802 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9805 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9810 void LoadScore(int nr)
9812 char *filename = getScoreFilename(nr);
9813 char cookie[MAX_LINE_LEN];
9814 char chunk_name[CHUNK_ID_LEN + 1];
9816 boolean old_score_file_format = FALSE;
9819 // always start with reliable default values
9820 setScoreInfoToDefaults();
9822 if (!(file = openFile(filename, MODE_READ)))
9825 getFileChunkBE(file, chunk_name, NULL);
9826 if (strEqual(chunk_name, "RND1"))
9828 getFile32BitBE(file); // not used
9830 getFileChunkBE(file, chunk_name, NULL);
9831 if (!strEqual(chunk_name, "SCOR"))
9833 Warn("unknown format of score file '%s'", filename);
9840 else // check for old file format with cookie string
9842 strcpy(cookie, chunk_name);
9843 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9845 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9846 cookie[strlen(cookie) - 1] = '\0';
9848 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9850 Warn("unknown format of score file '%s'", filename);
9857 old_score_file_format = TRUE;
9860 if (old_score_file_format)
9862 // score files from versions before 4.2.4.0 without chunk structure
9865 // convert score to time, if possible (mainly for Supaplex levels)
9874 int (*loader)(File *, int, struct ScoreInfo *);
9878 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9879 { "INFO", -1, LoadScore_INFO },
9880 { "NAME", -1, LoadScore_NAME },
9881 { "SCOR", -1, LoadScore_SCOR },
9882 { "SC4R", -1, LoadScore_SC4R },
9883 { "TIME", -1, LoadScore_TIME },
9884 { "TAPE", -1, LoadScore_TAPE },
9889 while (getFileChunkBE(file, chunk_name, &chunk_size))
9893 while (chunk_info[i].name != NULL &&
9894 !strEqual(chunk_name, chunk_info[i].name))
9897 if (chunk_info[i].name == NULL)
9899 Warn("unknown chunk '%s' in score file '%s'",
9900 chunk_name, filename);
9902 ReadUnusedBytesFromFile(file, chunk_size);
9904 else if (chunk_info[i].size != -1 &&
9905 chunk_info[i].size != chunk_size)
9907 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9908 chunk_size, chunk_name, filename);
9910 ReadUnusedBytesFromFile(file, chunk_size);
9914 // call function to load this score chunk
9915 int chunk_size_expected =
9916 (chunk_info[i].loader)(file, chunk_size, &scores);
9918 // the size of some chunks cannot be checked before reading other
9919 // chunks first (like "HEAD" and "BODY") that contain some header
9920 // information, so check them here
9921 if (chunk_size_expected != chunk_size)
9923 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9924 chunk_size, chunk_name, filename);
9933 #if ENABLE_HISTORIC_CHUNKS
9934 void SaveScore_OLD(int nr)
9937 char *filename = getScoreFilename(nr);
9940 // used instead of "leveldir_current->subdir" (for network games)
9941 InitScoreDirectory(levelset.identifier);
9943 if (!(file = fopen(filename, MODE_WRITE)))
9945 Warn("cannot save score for level %d", nr);
9950 fprintf(file, "%s\n\n", SCORE_COOKIE);
9952 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9953 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9957 SetFilePermissions(filename, PERMS_PRIVATE);
9961 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9963 putFileVersion(file, scores->file_version);
9964 putFileVersion(file, scores->game_version);
9967 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9969 int level_identifier_size = strlen(scores->level_identifier) + 1;
9972 putFile16BitBE(file, level_identifier_size);
9974 for (i = 0; i < level_identifier_size; i++)
9975 putFile8Bit(file, scores->level_identifier[i]);
9977 putFile16BitBE(file, scores->level_nr);
9978 putFile16BitBE(file, scores->num_entries);
9981 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9985 for (i = 0; i < scores->num_entries; i++)
9987 int name_size = strlen(scores->entry[i].name);
9989 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9990 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9994 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9998 for (i = 0; i < scores->num_entries; i++)
9999 putFile16BitBE(file, scores->entry[i].score);
10002 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10006 for (i = 0; i < scores->num_entries; i++)
10007 putFile32BitBE(file, scores->entry[i].score);
10010 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10014 for (i = 0; i < scores->num_entries; i++)
10015 putFile32BitBE(file, scores->entry[i].time);
10018 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10022 for (i = 0; i < scores->num_entries; i++)
10024 int size = strlen(scores->entry[i].tape_basename);
10026 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10027 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10031 static void SaveScoreToFilename(char *filename)
10034 int info_chunk_size;
10035 int name_chunk_size;
10036 int scor_chunk_size;
10037 int sc4r_chunk_size;
10038 int time_chunk_size;
10039 int tape_chunk_size;
10040 boolean has_large_score_values;
10043 if (!(file = fopen(filename, MODE_WRITE)))
10045 Warn("cannot save score file '%s'", filename);
10050 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10051 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10052 scor_chunk_size = scores.num_entries * 2;
10053 sc4r_chunk_size = scores.num_entries * 4;
10054 time_chunk_size = scores.num_entries * 4;
10055 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10057 has_large_score_values = FALSE;
10058 for (i = 0; i < scores.num_entries; i++)
10059 if (scores.entry[i].score > 0xffff)
10060 has_large_score_values = TRUE;
10062 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10063 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10065 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10066 SaveScore_VERS(file, &scores);
10068 putFileChunkBE(file, "INFO", info_chunk_size);
10069 SaveScore_INFO(file, &scores);
10071 putFileChunkBE(file, "NAME", name_chunk_size);
10072 SaveScore_NAME(file, &scores);
10074 if (has_large_score_values)
10076 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10077 SaveScore_SC4R(file, &scores);
10081 putFileChunkBE(file, "SCOR", scor_chunk_size);
10082 SaveScore_SCOR(file, &scores);
10085 putFileChunkBE(file, "TIME", time_chunk_size);
10086 SaveScore_TIME(file, &scores);
10088 putFileChunkBE(file, "TAPE", tape_chunk_size);
10089 SaveScore_TAPE(file, &scores);
10093 SetFilePermissions(filename, PERMS_PRIVATE);
10096 void SaveScore(int nr)
10098 char *filename = getScoreFilename(nr);
10101 // used instead of "leveldir_current->subdir" (for network games)
10102 InitScoreDirectory(levelset.identifier);
10104 scores.file_version = FILE_VERSION_ACTUAL;
10105 scores.game_version = GAME_VERSION_ACTUAL;
10107 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10108 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10109 scores.level_nr = level_nr;
10111 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10112 if (scores.entry[i].score == 0 &&
10113 scores.entry[i].time == 0 &&
10114 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10117 scores.num_entries = i;
10119 if (scores.num_entries == 0)
10122 SaveScoreToFilename(filename);
10125 static void LoadServerScoreFromCache(int nr)
10127 struct ScoreEntry score_entry;
10136 { &score_entry.score, FALSE, 0 },
10137 { &score_entry.time, FALSE, 0 },
10138 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10139 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10140 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10141 { &score_entry.id, FALSE, 0 },
10142 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10143 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10144 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10145 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10149 char *filename = getScoreCacheFilename(nr);
10150 SetupFileHash *score_hash = loadSetupFileHash(filename);
10153 server_scores.num_entries = 0;
10155 if (score_hash == NULL)
10158 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10160 score_entry = server_scores.entry[i];
10162 for (j = 0; score_mapping[j].value != NULL; j++)
10166 sprintf(token, "%02d.%d", i, j);
10168 char *value = getHashEntry(score_hash, token);
10173 if (score_mapping[j].is_string)
10175 char *score_value = (char *)score_mapping[j].value;
10176 int value_size = score_mapping[j].string_size;
10178 strncpy(score_value, value, value_size);
10179 score_value[value_size] = '\0';
10183 int *score_value = (int *)score_mapping[j].value;
10185 *score_value = atoi(value);
10188 server_scores.num_entries = i + 1;
10191 server_scores.entry[i] = score_entry;
10194 freeSetupFileHash(score_hash);
10197 void LoadServerScore(int nr, boolean download_score)
10199 if (!setup.use_api_server)
10202 // always start with reliable default values
10203 setServerScoreInfoToDefaults();
10205 // 1st step: load server scores from cache file (which may not exist)
10206 // (this should prevent reading it while the thread is writing to it)
10207 LoadServerScoreFromCache(nr);
10209 if (download_score && runtime.use_api_server)
10211 // 2nd step: download server scores from score server to cache file
10212 // (as thread, as it might time out if the server is not reachable)
10213 ApiGetScoreAsThread(nr);
10217 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10219 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10221 // if score tape not uploaded, ask for uploading missing tapes later
10222 if (!setup.has_remaining_tapes)
10223 setup.ask_for_remaining_tapes = TRUE;
10225 setup.provide_uploading_tapes = TRUE;
10226 setup.has_remaining_tapes = TRUE;
10228 SaveSetup_ServerSetup();
10231 void SaveServerScore(int nr, boolean tape_saved)
10233 if (!runtime.use_api_server)
10235 PrepareScoreTapesForUpload(leveldir_current->subdir);
10240 ApiAddScoreAsThread(nr, tape_saved, NULL);
10243 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10244 char *score_tape_filename)
10246 if (!runtime.use_api_server)
10249 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10252 void LoadLocalAndServerScore(int nr, boolean download_score)
10254 int last_added_local = scores.last_added_local;
10255 boolean force_last_added = scores.force_last_added;
10257 // needed if only showing server scores
10258 setScoreInfoToDefaults();
10260 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10263 // restore last added local score entry (before merging server scores)
10264 scores.last_added = scores.last_added_local = last_added_local;
10266 if (setup.use_api_server &&
10267 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10269 // load server scores from cache file and trigger update from server
10270 LoadServerScore(nr, download_score);
10272 // merge local scores with scores from server
10273 MergeServerScore();
10276 if (force_last_added)
10277 scores.force_last_added = force_last_added;
10281 // ============================================================================
10282 // setup file functions
10283 // ============================================================================
10285 #define TOKEN_STR_PLAYER_PREFIX "player_"
10288 static struct TokenInfo global_setup_tokens[] =
10292 &setup.player_name, "player_name"
10296 &setup.multiple_users, "multiple_users"
10300 &setup.sound, "sound"
10304 &setup.sound_loops, "repeating_sound_loops"
10308 &setup.sound_music, "background_music"
10312 &setup.sound_simple, "simple_sound_effects"
10316 &setup.toons, "toons"
10320 &setup.global_animations, "global_animations"
10324 &setup.scroll_delay, "scroll_delay"
10328 &setup.forced_scroll_delay, "forced_scroll_delay"
10332 &setup.scroll_delay_value, "scroll_delay_value"
10336 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10340 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10344 &setup.fade_screens, "fade_screens"
10348 &setup.autorecord, "automatic_tape_recording"
10352 &setup.autorecord_after_replay, "autorecord_after_replay"
10356 &setup.auto_pause_on_start, "auto_pause_on_start"
10360 &setup.show_titlescreen, "show_titlescreen"
10364 &setup.quick_doors, "quick_doors"
10368 &setup.team_mode, "team_mode"
10372 &setup.handicap, "handicap"
10376 &setup.skip_levels, "skip_levels"
10380 &setup.increment_levels, "increment_levels"
10384 &setup.auto_play_next_level, "auto_play_next_level"
10388 &setup.count_score_after_game, "count_score_after_game"
10392 &setup.show_scores_after_game, "show_scores_after_game"
10396 &setup.time_limit, "time_limit"
10400 &setup.fullscreen, "fullscreen"
10404 &setup.window_scaling_percent, "window_scaling_percent"
10408 &setup.window_scaling_quality, "window_scaling_quality"
10412 &setup.screen_rendering_mode, "screen_rendering_mode"
10416 &setup.vsync_mode, "vsync_mode"
10420 &setup.ask_on_escape, "ask_on_escape"
10424 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10428 &setup.ask_on_game_over, "ask_on_game_over"
10432 &setup.ask_on_quit_game, "ask_on_quit_game"
10436 &setup.ask_on_quit_program, "ask_on_quit_program"
10440 &setup.quick_switch, "quick_player_switch"
10444 &setup.input_on_focus, "input_on_focus"
10448 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10452 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10456 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10460 &setup.game_speed_extended, "game_speed_extended"
10464 &setup.game_frame_delay, "game_frame_delay"
10468 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10472 &setup.bd_skip_hatching, "bd_skip_hatching"
10476 &setup.bd_scroll_delay, "bd_scroll_delay"
10480 &setup.bd_smooth_movements, "bd_smooth_movements"
10484 &setup.sp_show_border_elements, "sp_show_border_elements"
10488 &setup.small_game_graphics, "small_game_graphics"
10492 &setup.show_load_save_buttons, "show_load_save_buttons"
10496 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10500 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10504 &setup.graphics_set, "graphics_set"
10508 &setup.sounds_set, "sounds_set"
10512 &setup.music_set, "music_set"
10516 &setup.override_level_graphics, "override_level_graphics"
10520 &setup.override_level_sounds, "override_level_sounds"
10524 &setup.override_level_music, "override_level_music"
10528 &setup.volume_simple, "volume_simple"
10532 &setup.volume_loops, "volume_loops"
10536 &setup.volume_music, "volume_music"
10540 &setup.network_mode, "network_mode"
10544 &setup.network_player_nr, "network_player"
10548 &setup.network_server_hostname, "network_server_hostname"
10552 &setup.touch.control_type, "touch.control_type"
10556 &setup.touch.move_distance, "touch.move_distance"
10560 &setup.touch.drop_distance, "touch.drop_distance"
10564 &setup.touch.transparency, "touch.transparency"
10568 &setup.touch.draw_outlined, "touch.draw_outlined"
10572 &setup.touch.draw_pressed, "touch.draw_pressed"
10576 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10580 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10584 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10588 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10592 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10596 static struct TokenInfo auto_setup_tokens[] =
10600 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10604 static struct TokenInfo server_setup_tokens[] =
10608 &setup.player_uuid, "player_uuid"
10612 &setup.player_version, "player_version"
10616 &setup.use_api_server, TEST_PREFIX "use_api_server"
10620 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10624 &setup.api_server_password, TEST_PREFIX "api_server_password"
10628 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10632 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10636 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10640 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10644 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10648 static struct TokenInfo editor_setup_tokens[] =
10652 &setup.editor.el_classic, "editor.el_classic"
10656 &setup.editor.el_custom, "editor.el_custom"
10660 &setup.editor.el_user_defined, "editor.el_user_defined"
10664 &setup.editor.el_dynamic, "editor.el_dynamic"
10668 &setup.editor.el_headlines, "editor.el_headlines"
10672 &setup.editor.show_element_token, "editor.show_element_token"
10676 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10680 static struct TokenInfo editor_cascade_setup_tokens[] =
10684 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10688 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10692 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10696 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10700 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10704 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10708 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10712 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10716 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10720 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10724 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10728 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10732 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10736 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10740 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10744 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10748 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10752 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10756 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10760 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10764 static struct TokenInfo shortcut_setup_tokens[] =
10768 &setup.shortcut.save_game, "shortcut.save_game"
10772 &setup.shortcut.load_game, "shortcut.load_game"
10776 &setup.shortcut.restart_game, "shortcut.restart_game"
10780 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10784 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10788 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10792 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10796 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10800 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10804 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10808 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10812 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10816 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10820 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10824 &setup.shortcut.tape_record, "shortcut.tape_record"
10828 &setup.shortcut.tape_play, "shortcut.tape_play"
10832 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10836 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10840 &setup.shortcut.sound_music, "shortcut.sound_music"
10844 &setup.shortcut.snap_left, "shortcut.snap_left"
10848 &setup.shortcut.snap_right, "shortcut.snap_right"
10852 &setup.shortcut.snap_up, "shortcut.snap_up"
10856 &setup.shortcut.snap_down, "shortcut.snap_down"
10860 static struct SetupInputInfo setup_input;
10861 static struct TokenInfo player_setup_tokens[] =
10865 &setup_input.use_joystick, ".use_joystick"
10869 &setup_input.joy.device_name, ".joy.device_name"
10873 &setup_input.joy.xleft, ".joy.xleft"
10877 &setup_input.joy.xmiddle, ".joy.xmiddle"
10881 &setup_input.joy.xright, ".joy.xright"
10885 &setup_input.joy.yupper, ".joy.yupper"
10889 &setup_input.joy.ymiddle, ".joy.ymiddle"
10893 &setup_input.joy.ylower, ".joy.ylower"
10897 &setup_input.joy.snap, ".joy.snap_field"
10901 &setup_input.joy.drop, ".joy.place_bomb"
10905 &setup_input.key.left, ".key.move_left"
10909 &setup_input.key.right, ".key.move_right"
10913 &setup_input.key.up, ".key.move_up"
10917 &setup_input.key.down, ".key.move_down"
10921 &setup_input.key.snap, ".key.snap_field"
10925 &setup_input.key.drop, ".key.place_bomb"
10929 static struct TokenInfo system_setup_tokens[] =
10933 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10937 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10941 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10945 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10949 static struct TokenInfo internal_setup_tokens[] =
10953 &setup.internal.program_title, "program_title"
10957 &setup.internal.program_version, "program_version"
10961 &setup.internal.program_author, "program_author"
10965 &setup.internal.program_email, "program_email"
10969 &setup.internal.program_website, "program_website"
10973 &setup.internal.program_copyright, "program_copyright"
10977 &setup.internal.program_company, "program_company"
10981 &setup.internal.program_icon_file, "program_icon_file"
10985 &setup.internal.default_graphics_set, "default_graphics_set"
10989 &setup.internal.default_sounds_set, "default_sounds_set"
10993 &setup.internal.default_music_set, "default_music_set"
10997 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11001 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11005 &setup.internal.fallback_music_file, "fallback_music_file"
11009 &setup.internal.default_level_series, "default_level_series"
11013 &setup.internal.default_window_width, "default_window_width"
11017 &setup.internal.default_window_height, "default_window_height"
11021 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11025 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11029 &setup.internal.create_user_levelset, "create_user_levelset"
11033 &setup.internal.info_screens_from_main, "info_screens_from_main"
11037 &setup.internal.menu_game, "menu_game"
11041 &setup.internal.menu_engines, "menu_engines"
11045 &setup.internal.menu_editor, "menu_editor"
11049 &setup.internal.menu_graphics, "menu_graphics"
11053 &setup.internal.menu_sound, "menu_sound"
11057 &setup.internal.menu_artwork, "menu_artwork"
11061 &setup.internal.menu_input, "menu_input"
11065 &setup.internal.menu_touch, "menu_touch"
11069 &setup.internal.menu_shortcuts, "menu_shortcuts"
11073 &setup.internal.menu_exit, "menu_exit"
11077 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11081 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11085 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11089 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11093 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11097 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11101 &setup.internal.info_title, "info_title"
11105 &setup.internal.info_elements, "info_elements"
11109 &setup.internal.info_music, "info_music"
11113 &setup.internal.info_credits, "info_credits"
11117 &setup.internal.info_program, "info_program"
11121 &setup.internal.info_version, "info_version"
11125 &setup.internal.info_levelset, "info_levelset"
11129 &setup.internal.info_exit, "info_exit"
11133 static struct TokenInfo debug_setup_tokens[] =
11137 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11141 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11145 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11149 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11153 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11157 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11161 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11165 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11169 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11173 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11177 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11181 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11185 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11189 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11193 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11197 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11201 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11205 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11209 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11213 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11217 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11220 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11224 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11228 &setup.debug.xsn_mode, "debug.xsn_mode"
11232 &setup.debug.xsn_percent, "debug.xsn_percent"
11236 static struct TokenInfo options_setup_tokens[] =
11240 &setup.options.verbose, "options.verbose"
11244 &setup.options.debug, "options.debug"
11248 &setup.options.debug_mode, "options.debug_mode"
11252 static void setSetupInfoToDefaults(struct SetupInfo *si)
11256 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11258 si->multiple_users = TRUE;
11261 si->sound_loops = TRUE;
11262 si->sound_music = TRUE;
11263 si->sound_simple = TRUE;
11265 si->global_animations = TRUE;
11266 si->scroll_delay = TRUE;
11267 si->forced_scroll_delay = FALSE;
11268 si->scroll_delay_value = STD_SCROLL_DELAY;
11269 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11270 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11271 si->fade_screens = TRUE;
11272 si->autorecord = TRUE;
11273 si->autorecord_after_replay = TRUE;
11274 si->auto_pause_on_start = FALSE;
11275 si->show_titlescreen = TRUE;
11276 si->quick_doors = FALSE;
11277 si->team_mode = FALSE;
11278 si->handicap = TRUE;
11279 si->skip_levels = TRUE;
11280 si->increment_levels = TRUE;
11281 si->auto_play_next_level = TRUE;
11282 si->count_score_after_game = TRUE;
11283 si->show_scores_after_game = TRUE;
11284 si->time_limit = TRUE;
11285 si->fullscreen = FALSE;
11286 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11287 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11288 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11289 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11290 si->ask_on_escape = TRUE;
11291 si->ask_on_escape_editor = TRUE;
11292 si->ask_on_game_over = TRUE;
11293 si->ask_on_quit_game = TRUE;
11294 si->ask_on_quit_program = TRUE;
11295 si->quick_switch = FALSE;
11296 si->input_on_focus = FALSE;
11297 si->prefer_aga_graphics = TRUE;
11298 si->prefer_lowpass_sounds = FALSE;
11299 si->prefer_extra_panel_items = TRUE;
11300 si->game_speed_extended = FALSE;
11301 si->game_frame_delay = GAME_FRAME_DELAY;
11302 si->bd_skip_uncovering = FALSE;
11303 si->bd_skip_hatching = FALSE;
11304 si->bd_scroll_delay = TRUE;
11305 si->bd_smooth_movements = AUTO;
11306 si->sp_show_border_elements = FALSE;
11307 si->small_game_graphics = FALSE;
11308 si->show_load_save_buttons = FALSE;
11309 si->show_undo_redo_buttons = FALSE;
11310 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11312 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11313 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11314 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11316 si->override_level_graphics = FALSE;
11317 si->override_level_sounds = FALSE;
11318 si->override_level_music = FALSE;
11320 si->volume_simple = 100; // percent
11321 si->volume_loops = 100; // percent
11322 si->volume_music = 100; // percent
11324 si->network_mode = FALSE;
11325 si->network_player_nr = 0; // first player
11326 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11328 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11329 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11330 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11331 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11332 si->touch.draw_outlined = TRUE;
11333 si->touch.draw_pressed = TRUE;
11335 for (i = 0; i < 2; i++)
11337 char *default_grid_button[6][2] =
11343 { "111222", " vv " },
11344 { "111222", " vv " }
11346 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11347 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11348 int min_xsize = MIN(6, grid_xsize);
11349 int min_ysize = MIN(6, grid_ysize);
11350 int startx = grid_xsize - min_xsize;
11351 int starty = grid_ysize - min_ysize;
11354 // virtual buttons grid can only be set to defaults if video is initialized
11355 // (this will be repeated if virtual buttons are not loaded from setup file)
11356 if (video.initialized)
11358 si->touch.grid_xsize[i] = grid_xsize;
11359 si->touch.grid_ysize[i] = grid_ysize;
11363 si->touch.grid_xsize[i] = -1;
11364 si->touch.grid_ysize[i] = -1;
11367 for (x = 0; x < MAX_GRID_XSIZE; x++)
11368 for (y = 0; y < MAX_GRID_YSIZE; y++)
11369 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11371 for (x = 0; x < min_xsize; x++)
11372 for (y = 0; y < min_ysize; y++)
11373 si->touch.grid_button[i][x][starty + y] =
11374 default_grid_button[y][0][x];
11376 for (x = 0; x < min_xsize; x++)
11377 for (y = 0; y < min_ysize; y++)
11378 si->touch.grid_button[i][startx + x][starty + y] =
11379 default_grid_button[y][1][x];
11382 si->touch.grid_initialized = video.initialized;
11384 si->touch.overlay_buttons = FALSE;
11386 si->editor.el_boulderdash = TRUE;
11387 si->editor.el_boulderdash_native = TRUE;
11388 si->editor.el_boulderdash_effects = TRUE;
11389 si->editor.el_emerald_mine = TRUE;
11390 si->editor.el_emerald_mine_club = TRUE;
11391 si->editor.el_more = TRUE;
11392 si->editor.el_sokoban = TRUE;
11393 si->editor.el_supaplex = TRUE;
11394 si->editor.el_diamond_caves = TRUE;
11395 si->editor.el_dx_boulderdash = TRUE;
11397 si->editor.el_mirror_magic = TRUE;
11398 si->editor.el_deflektor = TRUE;
11400 si->editor.el_chars = TRUE;
11401 si->editor.el_steel_chars = TRUE;
11403 si->editor.el_classic = TRUE;
11404 si->editor.el_custom = TRUE;
11406 si->editor.el_user_defined = FALSE;
11407 si->editor.el_dynamic = TRUE;
11409 si->editor.el_headlines = TRUE;
11411 si->editor.show_element_token = FALSE;
11413 si->editor.show_read_only_warning = TRUE;
11415 si->editor.use_template_for_new_levels = TRUE;
11417 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11418 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11419 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11420 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11421 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11423 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11424 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11425 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11426 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11427 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11429 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11430 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11431 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11432 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11433 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11434 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11436 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11437 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11438 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11440 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11441 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11442 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11443 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11445 for (i = 0; i < MAX_PLAYERS; i++)
11447 si->input[i].use_joystick = FALSE;
11448 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11449 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11450 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11451 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11452 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11453 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11454 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11455 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11456 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11457 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11458 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11459 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11460 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11461 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11462 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11465 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11466 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11467 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11468 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11470 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11471 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11472 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11473 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11474 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11475 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11476 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11478 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11480 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11481 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11482 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11484 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11485 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11486 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11488 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11489 si->internal.choose_from_top_leveldir = FALSE;
11490 si->internal.show_scaling_in_title = TRUE;
11491 si->internal.create_user_levelset = TRUE;
11492 si->internal.info_screens_from_main = FALSE;
11494 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11495 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11497 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11498 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11499 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11500 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11501 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11502 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11503 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11504 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11505 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11506 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11508 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11509 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11510 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11511 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11512 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11513 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11514 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11515 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11516 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11517 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11519 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11520 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11522 si->debug.show_frames_per_second = FALSE;
11524 si->debug.xsn_mode = AUTO;
11525 si->debug.xsn_percent = 0;
11527 si->options.verbose = FALSE;
11528 si->options.debug = FALSE;
11529 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11531 #if defined(PLATFORM_ANDROID)
11532 si->fullscreen = TRUE;
11533 si->touch.overlay_buttons = TRUE;
11536 setHideSetupEntry(&setup.debug.xsn_mode);
11539 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11541 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11544 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11546 si->player_uuid = NULL; // (will be set later)
11547 si->player_version = 1; // (will be set later)
11549 si->use_api_server = TRUE;
11550 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11551 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11552 si->ask_for_uploading_tapes = TRUE;
11553 si->ask_for_remaining_tapes = FALSE;
11554 si->provide_uploading_tapes = TRUE;
11555 si->ask_for_using_api_server = TRUE;
11556 si->has_remaining_tapes = FALSE;
11559 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11561 si->editor_cascade.el_bd = TRUE;
11562 si->editor_cascade.el_bd_native = TRUE;
11563 si->editor_cascade.el_bd_effects = FALSE;
11564 si->editor_cascade.el_em = TRUE;
11565 si->editor_cascade.el_emc = TRUE;
11566 si->editor_cascade.el_rnd = TRUE;
11567 si->editor_cascade.el_sb = TRUE;
11568 si->editor_cascade.el_sp = TRUE;
11569 si->editor_cascade.el_dc = TRUE;
11570 si->editor_cascade.el_dx = TRUE;
11572 si->editor_cascade.el_mm = TRUE;
11573 si->editor_cascade.el_df = TRUE;
11575 si->editor_cascade.el_chars = FALSE;
11576 si->editor_cascade.el_steel_chars = FALSE;
11577 si->editor_cascade.el_ce = FALSE;
11578 si->editor_cascade.el_ge = FALSE;
11579 si->editor_cascade.el_es = FALSE;
11580 si->editor_cascade.el_ref = FALSE;
11581 si->editor_cascade.el_user = FALSE;
11582 si->editor_cascade.el_dynamic = FALSE;
11585 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11587 static char *getHideSetupToken(void *setup_value)
11589 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11591 if (setup_value != NULL)
11592 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11594 return hide_setup_token;
11597 void setHideSetupEntry(void *setup_value)
11599 char *hide_setup_token = getHideSetupToken(setup_value);
11601 if (hide_setup_hash == NULL)
11602 hide_setup_hash = newSetupFileHash();
11604 if (setup_value != NULL)
11605 setHashEntry(hide_setup_hash, hide_setup_token, "");
11608 void removeHideSetupEntry(void *setup_value)
11610 char *hide_setup_token = getHideSetupToken(setup_value);
11612 if (setup_value != NULL)
11613 removeHashEntry(hide_setup_hash, hide_setup_token);
11616 boolean hideSetupEntry(void *setup_value)
11618 char *hide_setup_token = getHideSetupToken(setup_value);
11620 return (setup_value != NULL &&
11621 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11624 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11625 struct TokenInfo *token_info,
11626 int token_nr, char *token_text)
11628 char *token_hide_text = getStringCat2(token_text, ".hide");
11629 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11631 // set the value of this setup option in the setup option structure
11632 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11634 // check if this setup option should be hidden in the setup menu
11635 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11636 setHideSetupEntry(token_info[token_nr].value);
11638 free(token_hide_text);
11641 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11642 struct TokenInfo *token_info,
11645 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11646 token_info[token_nr].text);
11649 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11653 if (!setup_file_hash)
11656 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11657 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11659 setup.touch.grid_initialized = TRUE;
11660 for (i = 0; i < 2; i++)
11662 int grid_xsize = setup.touch.grid_xsize[i];
11663 int grid_ysize = setup.touch.grid_ysize[i];
11666 // if virtual buttons are not loaded from setup file, repeat initializing
11667 // virtual buttons grid with default values later when video is initialized
11668 if (grid_xsize == -1 ||
11671 setup.touch.grid_initialized = FALSE;
11676 for (y = 0; y < grid_ysize; y++)
11678 char token_string[MAX_LINE_LEN];
11680 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11682 char *value_string = getHashEntry(setup_file_hash, token_string);
11684 if (value_string == NULL)
11687 for (x = 0; x < grid_xsize; x++)
11689 char c = value_string[x];
11691 setup.touch.grid_button[i][x][y] =
11692 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11697 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11698 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11700 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11701 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11703 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11707 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11709 setup_input = setup.input[pnr];
11710 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11712 char full_token[100];
11714 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11715 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11718 setup.input[pnr] = setup_input;
11721 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11722 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11724 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11725 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11727 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11728 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11730 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11731 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11733 setHideRelatedSetupEntries();
11736 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11740 if (!setup_file_hash)
11743 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11744 setSetupInfo(auto_setup_tokens, i,
11745 getHashEntry(setup_file_hash,
11746 auto_setup_tokens[i].text));
11749 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11753 if (!setup_file_hash)
11756 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11757 setSetupInfo(server_setup_tokens, i,
11758 getHashEntry(setup_file_hash,
11759 server_setup_tokens[i].text));
11762 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11766 if (!setup_file_hash)
11769 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11770 setSetupInfo(editor_cascade_setup_tokens, i,
11771 getHashEntry(setup_file_hash,
11772 editor_cascade_setup_tokens[i].text));
11775 void LoadUserNames(void)
11777 int last_user_nr = user.nr;
11780 if (global.user_names != NULL)
11782 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11783 checked_free(global.user_names[i]);
11785 checked_free(global.user_names);
11788 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11790 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11794 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11796 if (setup_file_hash)
11798 char *player_name = getHashEntry(setup_file_hash, "player_name");
11800 global.user_names[i] = getFixedUserName(player_name);
11802 freeSetupFileHash(setup_file_hash);
11805 if (global.user_names[i] == NULL)
11806 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11809 user.nr = last_user_nr;
11812 void LoadSetupFromFilename(char *filename)
11814 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11816 if (setup_file_hash)
11818 decodeSetupFileHash_Default(setup_file_hash);
11820 freeSetupFileHash(setup_file_hash);
11824 Debug("setup", "using default setup values");
11828 static void LoadSetup_SpecialPostProcessing(void)
11830 char *player_name_new;
11832 // needed to work around problems with fixed length strings
11833 player_name_new = getFixedUserName(setup.player_name);
11834 free(setup.player_name);
11835 setup.player_name = player_name_new;
11837 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11838 if (setup.scroll_delay == FALSE)
11840 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11841 setup.scroll_delay = TRUE; // now always "on"
11844 // make sure that scroll delay value stays inside valid range
11845 setup.scroll_delay_value =
11846 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11849 void LoadSetup_Default(void)
11853 // always start with reliable default values
11854 setSetupInfoToDefaults(&setup);
11856 // try to load setup values from default setup file
11857 filename = getDefaultSetupFilename();
11859 if (fileExists(filename))
11860 LoadSetupFromFilename(filename);
11862 // try to load setup values from platform setup file
11863 filename = getPlatformSetupFilename();
11865 if (fileExists(filename))
11866 LoadSetupFromFilename(filename);
11868 // try to load setup values from user setup file
11869 filename = getSetupFilename();
11871 LoadSetupFromFilename(filename);
11873 LoadSetup_SpecialPostProcessing();
11876 void LoadSetup_AutoSetup(void)
11878 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11879 SetupFileHash *setup_file_hash = NULL;
11881 // always start with reliable default values
11882 setSetupInfoToDefaults_AutoSetup(&setup);
11884 setup_file_hash = loadSetupFileHash(filename);
11886 if (setup_file_hash)
11888 decodeSetupFileHash_AutoSetup(setup_file_hash);
11890 freeSetupFileHash(setup_file_hash);
11896 void LoadSetup_ServerSetup(void)
11898 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11899 SetupFileHash *setup_file_hash = NULL;
11901 // always start with reliable default values
11902 setSetupInfoToDefaults_ServerSetup(&setup);
11904 setup_file_hash = loadSetupFileHash(filename);
11906 if (setup_file_hash)
11908 decodeSetupFileHash_ServerSetup(setup_file_hash);
11910 freeSetupFileHash(setup_file_hash);
11915 if (setup.player_uuid == NULL)
11917 // player UUID does not yet exist in setup file
11918 setup.player_uuid = getStringCopy(getUUID());
11919 setup.player_version = 2;
11921 SaveSetup_ServerSetup();
11925 void LoadSetup_EditorCascade(void)
11927 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11928 SetupFileHash *setup_file_hash = NULL;
11930 // always start with reliable default values
11931 setSetupInfoToDefaults_EditorCascade(&setup);
11933 setup_file_hash = loadSetupFileHash(filename);
11935 if (setup_file_hash)
11937 decodeSetupFileHash_EditorCascade(setup_file_hash);
11939 freeSetupFileHash(setup_file_hash);
11945 void LoadSetup(void)
11947 LoadSetup_Default();
11948 LoadSetup_AutoSetup();
11949 LoadSetup_ServerSetup();
11950 LoadSetup_EditorCascade();
11953 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11954 char *mapping_line)
11956 char mapping_guid[MAX_LINE_LEN];
11957 char *mapping_start, *mapping_end;
11959 // get GUID from game controller mapping line: copy complete line
11960 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11961 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11963 // get GUID from game controller mapping line: cut after GUID part
11964 mapping_start = strchr(mapping_guid, ',');
11965 if (mapping_start != NULL)
11966 *mapping_start = '\0';
11968 // cut newline from game controller mapping line
11969 mapping_end = strchr(mapping_line, '\n');
11970 if (mapping_end != NULL)
11971 *mapping_end = '\0';
11973 // add mapping entry to game controller mappings hash
11974 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11977 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11982 if (!(file = fopen(filename, MODE_READ)))
11984 Warn("cannot read game controller mappings file '%s'", filename);
11989 while (!feof(file))
11991 char line[MAX_LINE_LEN];
11993 if (!fgets(line, MAX_LINE_LEN, file))
11996 addGameControllerMappingToHash(mappings_hash, line);
12002 void SaveSetup_Default(void)
12004 char *filename = getSetupFilename();
12008 InitUserDataDirectory();
12010 if (!(file = fopen(filename, MODE_WRITE)))
12012 Warn("cannot write setup file '%s'", filename);
12017 fprintFileHeader(file, SETUP_FILENAME);
12019 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12021 // just to make things nicer :)
12022 if (global_setup_tokens[i].value == &setup.multiple_users ||
12023 global_setup_tokens[i].value == &setup.sound ||
12024 global_setup_tokens[i].value == &setup.graphics_set ||
12025 global_setup_tokens[i].value == &setup.volume_simple ||
12026 global_setup_tokens[i].value == &setup.network_mode ||
12027 global_setup_tokens[i].value == &setup.touch.control_type ||
12028 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12029 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12030 fprintf(file, "\n");
12032 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12035 for (i = 0; i < 2; i++)
12037 int grid_xsize = setup.touch.grid_xsize[i];
12038 int grid_ysize = setup.touch.grid_ysize[i];
12041 fprintf(file, "\n");
12043 for (y = 0; y < grid_ysize; y++)
12045 char token_string[MAX_LINE_LEN];
12046 char value_string[MAX_LINE_LEN];
12048 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12050 for (x = 0; x < grid_xsize; x++)
12052 char c = setup.touch.grid_button[i][x][y];
12054 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12057 value_string[grid_xsize] = '\0';
12059 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12063 fprintf(file, "\n");
12064 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12065 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12067 fprintf(file, "\n");
12068 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12069 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12071 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12075 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12076 fprintf(file, "\n");
12078 setup_input = setup.input[pnr];
12079 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12080 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12083 fprintf(file, "\n");
12084 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12085 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12087 // (internal setup values not saved to user setup file)
12089 fprintf(file, "\n");
12090 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12091 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12092 setup.debug.xsn_mode != AUTO)
12093 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12095 fprintf(file, "\n");
12096 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12097 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12101 SetFilePermissions(filename, PERMS_PRIVATE);
12104 void SaveSetup_AutoSetup(void)
12106 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12110 InitUserDataDirectory();
12112 if (!(file = fopen(filename, MODE_WRITE)))
12114 Warn("cannot write auto setup file '%s'", filename);
12121 fprintFileHeader(file, AUTOSETUP_FILENAME);
12123 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12124 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12128 SetFilePermissions(filename, PERMS_PRIVATE);
12133 void SaveSetup_ServerSetup(void)
12135 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12139 InitUserDataDirectory();
12141 if (!(file = fopen(filename, MODE_WRITE)))
12143 Warn("cannot write server setup file '%s'", filename);
12150 fprintFileHeader(file, SERVERSETUP_FILENAME);
12152 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12154 // just to make things nicer :)
12155 if (server_setup_tokens[i].value == &setup.use_api_server)
12156 fprintf(file, "\n");
12158 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12163 SetFilePermissions(filename, PERMS_PRIVATE);
12168 void SaveSetup_EditorCascade(void)
12170 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12174 InitUserDataDirectory();
12176 if (!(file = fopen(filename, MODE_WRITE)))
12178 Warn("cannot write editor cascade state file '%s'", filename);
12185 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12187 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12188 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12192 SetFilePermissions(filename, PERMS_PRIVATE);
12197 void SaveSetup(void)
12199 SaveSetup_Default();
12200 SaveSetup_AutoSetup();
12201 SaveSetup_ServerSetup();
12202 SaveSetup_EditorCascade();
12205 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12210 if (!(file = fopen(filename, MODE_WRITE)))
12212 Warn("cannot write game controller mappings file '%s'", filename);
12217 BEGIN_HASH_ITERATION(mappings_hash, itr)
12219 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12221 END_HASH_ITERATION(mappings_hash, itr)
12226 void SaveSetup_AddGameControllerMapping(char *mapping)
12228 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12229 SetupFileHash *mappings_hash = newSetupFileHash();
12231 InitUserDataDirectory();
12233 // load existing personal game controller mappings
12234 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12236 // add new mapping to personal game controller mappings
12237 addGameControllerMappingToHash(mappings_hash, mapping);
12239 // save updated personal game controller mappings
12240 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12242 freeSetupFileHash(mappings_hash);
12246 void LoadCustomElementDescriptions(void)
12248 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12249 SetupFileHash *setup_file_hash;
12252 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12254 if (element_info[i].custom_description != NULL)
12256 free(element_info[i].custom_description);
12257 element_info[i].custom_description = NULL;
12261 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12264 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12266 char *token = getStringCat2(element_info[i].token_name, ".name");
12267 char *value = getHashEntry(setup_file_hash, token);
12270 element_info[i].custom_description = getStringCopy(value);
12275 freeSetupFileHash(setup_file_hash);
12278 static int getElementFromToken(char *token)
12280 char *value = getHashEntry(element_token_hash, token);
12283 return atoi(value);
12285 Warn("unknown element token '%s'", token);
12287 return EL_UNDEFINED;
12290 void FreeGlobalAnimEventInfo(void)
12292 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12294 if (gaei->event_list == NULL)
12299 for (i = 0; i < gaei->num_event_lists; i++)
12301 checked_free(gaei->event_list[i]->event_value);
12302 checked_free(gaei->event_list[i]);
12305 checked_free(gaei->event_list);
12307 gaei->event_list = NULL;
12308 gaei->num_event_lists = 0;
12311 static int AddGlobalAnimEventList(void)
12313 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12314 int list_pos = gaei->num_event_lists++;
12316 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12317 sizeof(struct GlobalAnimEventListInfo *));
12319 gaei->event_list[list_pos] =
12320 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12322 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12324 gaeli->event_value = NULL;
12325 gaeli->num_event_values = 0;
12330 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12332 // do not add empty global animation events
12333 if (event_value == ANIM_EVENT_NONE)
12336 // if list position is undefined, create new list
12337 if (list_pos == ANIM_EVENT_UNDEFINED)
12338 list_pos = AddGlobalAnimEventList();
12340 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12341 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12342 int value_pos = gaeli->num_event_values++;
12344 gaeli->event_value = checked_realloc(gaeli->event_value,
12345 gaeli->num_event_values * sizeof(int *));
12347 gaeli->event_value[value_pos] = event_value;
12352 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12354 if (list_pos == ANIM_EVENT_UNDEFINED)
12355 return ANIM_EVENT_NONE;
12357 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12358 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12360 return gaeli->event_value[value_pos];
12363 int GetGlobalAnimEventValueCount(int list_pos)
12365 if (list_pos == ANIM_EVENT_UNDEFINED)
12368 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12369 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12371 return gaeli->num_event_values;
12374 // This function checks if a string <s> of the format "string1, string2, ..."
12375 // exactly contains a string <s_contained>.
12377 static boolean string_has_parameter(char *s, char *s_contained)
12381 if (s == NULL || s_contained == NULL)
12384 if (strlen(s_contained) > strlen(s))
12387 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12389 char next_char = s[strlen(s_contained)];
12391 // check if next character is delimiter or whitespace
12392 if (next_char == ',' || next_char == '\0' ||
12393 next_char == ' ' || next_char == '\t')
12397 // check if string contains another parameter string after a comma
12398 substring = strchr(s, ',');
12399 if (substring == NULL) // string does not contain a comma
12402 // advance string pointer to next character after the comma
12405 // skip potential whitespaces after the comma
12406 while (*substring == ' ' || *substring == '\t')
12409 return string_has_parameter(substring, s_contained);
12412 static int get_anim_parameter_value_ce(char *s)
12415 char *pattern_1 = "ce_change:custom_";
12416 char *pattern_2 = ".page_";
12417 int pattern_1_len = strlen(pattern_1);
12418 char *matching_char = strstr(s_ptr, pattern_1);
12419 int result = ANIM_EVENT_NONE;
12421 if (matching_char == NULL)
12422 return ANIM_EVENT_NONE;
12424 result = ANIM_EVENT_CE_CHANGE;
12426 s_ptr = matching_char + pattern_1_len;
12428 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12429 if (*s_ptr >= '0' && *s_ptr <= '9')
12431 int gic_ce_nr = (*s_ptr++ - '0');
12433 if (*s_ptr >= '0' && *s_ptr <= '9')
12435 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12437 if (*s_ptr >= '0' && *s_ptr <= '9')
12438 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12441 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12442 return ANIM_EVENT_NONE;
12444 // custom element stored as 0 to 255
12447 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12451 // invalid custom element number specified
12453 return ANIM_EVENT_NONE;
12456 // check for change page number ("page_X" or "page_XX") (optional)
12457 if (strPrefix(s_ptr, pattern_2))
12459 s_ptr += strlen(pattern_2);
12461 if (*s_ptr >= '0' && *s_ptr <= '9')
12463 int gic_page_nr = (*s_ptr++ - '0');
12465 if (*s_ptr >= '0' && *s_ptr <= '9')
12466 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12468 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12469 return ANIM_EVENT_NONE;
12471 // change page stored as 1 to 32 (0 means "all change pages")
12473 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12477 // invalid animation part number specified
12479 return ANIM_EVENT_NONE;
12483 // discard result if next character is neither delimiter nor whitespace
12484 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12485 *s_ptr == ' ' || *s_ptr == '\t'))
12486 return ANIM_EVENT_NONE;
12491 static int get_anim_parameter_value(char *s)
12493 int event_value[] =
12501 char *pattern_1[] =
12509 char *pattern_2 = ".part_";
12510 char *matching_char = NULL;
12512 int pattern_1_len = 0;
12513 int result = ANIM_EVENT_NONE;
12516 result = get_anim_parameter_value_ce(s);
12518 if (result != ANIM_EVENT_NONE)
12521 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12523 matching_char = strstr(s_ptr, pattern_1[i]);
12524 pattern_1_len = strlen(pattern_1[i]);
12525 result = event_value[i];
12527 if (matching_char != NULL)
12531 if (matching_char == NULL)
12532 return ANIM_EVENT_NONE;
12534 s_ptr = matching_char + pattern_1_len;
12536 // check for main animation number ("anim_X" or "anim_XX")
12537 if (*s_ptr >= '0' && *s_ptr <= '9')
12539 int gic_anim_nr = (*s_ptr++ - '0');
12541 if (*s_ptr >= '0' && *s_ptr <= '9')
12542 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12544 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12545 return ANIM_EVENT_NONE;
12547 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12551 // invalid main animation number specified
12553 return ANIM_EVENT_NONE;
12556 // check for animation part number ("part_X" or "part_XX") (optional)
12557 if (strPrefix(s_ptr, pattern_2))
12559 s_ptr += strlen(pattern_2);
12561 if (*s_ptr >= '0' && *s_ptr <= '9')
12563 int gic_part_nr = (*s_ptr++ - '0');
12565 if (*s_ptr >= '0' && *s_ptr <= '9')
12566 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12568 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12569 return ANIM_EVENT_NONE;
12571 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12575 // invalid animation part number specified
12577 return ANIM_EVENT_NONE;
12581 // discard result if next character is neither delimiter nor whitespace
12582 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12583 *s_ptr == ' ' || *s_ptr == '\t'))
12584 return ANIM_EVENT_NONE;
12589 static int get_anim_parameter_values(char *s)
12591 int list_pos = ANIM_EVENT_UNDEFINED;
12592 int event_value = ANIM_EVENT_DEFAULT;
12594 if (string_has_parameter(s, "any"))
12595 event_value |= ANIM_EVENT_ANY;
12597 if (string_has_parameter(s, "click:self") ||
12598 string_has_parameter(s, "click") ||
12599 string_has_parameter(s, "self"))
12600 event_value |= ANIM_EVENT_SELF;
12602 if (string_has_parameter(s, "unclick:any"))
12603 event_value |= ANIM_EVENT_UNCLICK_ANY;
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);
12611 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12612 event_value = get_anim_parameter_value(s);
12614 // if animation event found, add it to global animation event list
12615 if (event_value != ANIM_EVENT_NONE)
12616 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12618 // continue with next part of the string, starting with next comma
12619 s = strchr(s + 1, ',');
12625 static int get_anim_action_parameter_value(char *token)
12627 // check most common default case first to massively speed things up
12628 if (strEqual(token, ARG_UNDEFINED))
12629 return ANIM_EVENT_ACTION_NONE;
12631 int result = getImageIDFromToken(token);
12635 char *gfx_token = getStringCat2("gfx.", token);
12637 result = getImageIDFromToken(gfx_token);
12639 checked_free(gfx_token);
12644 Key key = getKeyFromX11KeyName(token);
12646 if (key != KSYM_UNDEFINED)
12647 result = -(int)key;
12654 result = get_hash_from_string(token); // unsigned int => int
12655 result = ABS(result); // may be negative now
12656 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12658 setHashEntry(anim_url_hash, int2str(result, 0), token);
12663 result = ANIM_EVENT_ACTION_NONE;
12668 int get_parameter_value(char *value_raw, char *suffix, int type)
12670 char *value = getStringToLower(value_raw);
12671 int result = 0; // probably a save default value
12673 if (strEqual(suffix, ".direction"))
12675 result = (strEqual(value, "left") ? MV_LEFT :
12676 strEqual(value, "right") ? MV_RIGHT :
12677 strEqual(value, "up") ? MV_UP :
12678 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12680 else if (strEqual(suffix, ".position"))
12682 result = (strEqual(value, "left") ? POS_LEFT :
12683 strEqual(value, "right") ? POS_RIGHT :
12684 strEqual(value, "top") ? POS_TOP :
12685 strEqual(value, "upper") ? POS_UPPER :
12686 strEqual(value, "middle") ? POS_MIDDLE :
12687 strEqual(value, "lower") ? POS_LOWER :
12688 strEqual(value, "bottom") ? POS_BOTTOM :
12689 strEqual(value, "any") ? POS_ANY :
12690 strEqual(value, "ce") ? POS_CE :
12691 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12692 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12694 else if (strEqual(suffix, ".align"))
12696 result = (strEqual(value, "left") ? ALIGN_LEFT :
12697 strEqual(value, "right") ? ALIGN_RIGHT :
12698 strEqual(value, "center") ? ALIGN_CENTER :
12699 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12701 else if (strEqual(suffix, ".valign"))
12703 result = (strEqual(value, "top") ? VALIGN_TOP :
12704 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12705 strEqual(value, "middle") ? VALIGN_MIDDLE :
12706 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12708 else if (strEqual(suffix, ".anim_mode"))
12710 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12711 string_has_parameter(value, "loop") ? ANIM_LOOP :
12712 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12713 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12714 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12715 string_has_parameter(value, "random") ? ANIM_RANDOM :
12716 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12717 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12718 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12719 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12720 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12721 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12722 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12723 string_has_parameter(value, "all") ? ANIM_ALL :
12724 string_has_parameter(value, "tiled") ? ANIM_TILED :
12725 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12728 if (string_has_parameter(value, "once"))
12729 result |= ANIM_ONCE;
12731 if (string_has_parameter(value, "reverse"))
12732 result |= ANIM_REVERSE;
12734 if (string_has_parameter(value, "opaque_player"))
12735 result |= ANIM_OPAQUE_PLAYER;
12737 if (string_has_parameter(value, "static_panel"))
12738 result |= ANIM_STATIC_PANEL;
12740 else if (strEqual(suffix, ".init_event") ||
12741 strEqual(suffix, ".anim_event"))
12743 result = get_anim_parameter_values(value);
12745 else if (strEqual(suffix, ".init_delay_action") ||
12746 strEqual(suffix, ".anim_delay_action") ||
12747 strEqual(suffix, ".post_delay_action") ||
12748 strEqual(suffix, ".init_event_action") ||
12749 strEqual(suffix, ".anim_event_action"))
12751 result = get_anim_action_parameter_value(value_raw);
12753 else if (strEqual(suffix, ".class"))
12755 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12756 get_hash_from_string(value));
12758 else if (strEqual(suffix, ".style"))
12760 result = STYLE_DEFAULT;
12762 if (string_has_parameter(value, "accurate_borders"))
12763 result |= STYLE_ACCURATE_BORDERS;
12765 if (string_has_parameter(value, "inner_corners"))
12766 result |= STYLE_INNER_CORNERS;
12768 if (string_has_parameter(value, "reverse"))
12769 result |= STYLE_REVERSE;
12771 if (string_has_parameter(value, "leftmost_position"))
12772 result |= STYLE_LEFTMOST_POSITION;
12774 if (string_has_parameter(value, "block_clicks"))
12775 result |= STYLE_BLOCK;
12777 if (string_has_parameter(value, "passthrough_clicks"))
12778 result |= STYLE_PASSTHROUGH;
12780 if (string_has_parameter(value, "multiple_actions"))
12781 result |= STYLE_MULTIPLE_ACTIONS;
12783 if (string_has_parameter(value, "consume_ce_event"))
12784 result |= STYLE_CONSUME_CE_EVENT;
12786 else if (strEqual(suffix, ".fade_mode"))
12788 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12789 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12790 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12791 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12792 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12793 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12794 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12795 FADE_MODE_DEFAULT);
12797 else if (strEqual(suffix, ".auto_delay_unit"))
12799 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12800 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12801 AUTO_DELAY_UNIT_DEFAULT);
12803 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12805 result = gfx.get_font_from_token_function(value);
12807 else // generic parameter of type integer or boolean
12809 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12810 type == TYPE_INTEGER ? get_integer_from_string(value) :
12811 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12812 ARG_UNDEFINED_VALUE);
12820 static int get_token_parameter_value(char *token, char *value_raw)
12824 if (token == NULL || value_raw == NULL)
12825 return ARG_UNDEFINED_VALUE;
12827 suffix = strrchr(token, '.');
12828 if (suffix == NULL)
12831 if (strEqual(suffix, ".element"))
12832 return getElementFromToken(value_raw);
12834 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12835 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12838 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12839 boolean ignore_defaults)
12843 for (i = 0; image_config_vars[i].token != NULL; i++)
12845 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12847 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12848 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12852 *image_config_vars[i].value =
12853 get_token_parameter_value(image_config_vars[i].token, value);
12857 void InitMenuDesignSettings_Static(void)
12859 // always start with reliable default values from static default config
12860 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12863 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12867 // the following initializes hierarchical values from static configuration
12869 // special case: initialize "ARG_DEFAULT" values in static default config
12870 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12871 titlescreen_initial_first_default.fade_mode =
12872 title_initial_first_default.fade_mode;
12873 titlescreen_initial_first_default.fade_delay =
12874 title_initial_first_default.fade_delay;
12875 titlescreen_initial_first_default.post_delay =
12876 title_initial_first_default.post_delay;
12877 titlescreen_initial_first_default.auto_delay =
12878 title_initial_first_default.auto_delay;
12879 titlescreen_initial_first_default.auto_delay_unit =
12880 title_initial_first_default.auto_delay_unit;
12881 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12882 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12883 titlescreen_first_default.post_delay = title_first_default.post_delay;
12884 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12885 titlescreen_first_default.auto_delay_unit =
12886 title_first_default.auto_delay_unit;
12887 titlemessage_initial_first_default.fade_mode =
12888 title_initial_first_default.fade_mode;
12889 titlemessage_initial_first_default.fade_delay =
12890 title_initial_first_default.fade_delay;
12891 titlemessage_initial_first_default.post_delay =
12892 title_initial_first_default.post_delay;
12893 titlemessage_initial_first_default.auto_delay =
12894 title_initial_first_default.auto_delay;
12895 titlemessage_initial_first_default.auto_delay_unit =
12896 title_initial_first_default.auto_delay_unit;
12897 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12898 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12899 titlemessage_first_default.post_delay = title_first_default.post_delay;
12900 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12901 titlemessage_first_default.auto_delay_unit =
12902 title_first_default.auto_delay_unit;
12904 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12905 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12906 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12907 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12908 titlescreen_initial_default.auto_delay_unit =
12909 title_initial_default.auto_delay_unit;
12910 titlescreen_default.fade_mode = title_default.fade_mode;
12911 titlescreen_default.fade_delay = title_default.fade_delay;
12912 titlescreen_default.post_delay = title_default.post_delay;
12913 titlescreen_default.auto_delay = title_default.auto_delay;
12914 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12915 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12916 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12917 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12918 titlemessage_initial_default.auto_delay_unit =
12919 title_initial_default.auto_delay_unit;
12920 titlemessage_default.fade_mode = title_default.fade_mode;
12921 titlemessage_default.fade_delay = title_default.fade_delay;
12922 titlemessage_default.post_delay = title_default.post_delay;
12923 titlemessage_default.auto_delay = title_default.auto_delay;
12924 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12926 // special case: initialize "ARG_DEFAULT" values in static default config
12927 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12928 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12930 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12931 titlescreen_first[i] = titlescreen_first_default;
12932 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12933 titlemessage_first[i] = titlemessage_first_default;
12935 titlescreen_initial[i] = titlescreen_initial_default;
12936 titlescreen[i] = titlescreen_default;
12937 titlemessage_initial[i] = titlemessage_initial_default;
12938 titlemessage[i] = titlemessage_default;
12941 // special case: initialize "ARG_DEFAULT" values in static default config
12942 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12943 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12945 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12948 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12949 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12950 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12953 // special case: initialize "ARG_DEFAULT" values in static default config
12954 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12955 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12957 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12958 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12959 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12961 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12964 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12968 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12972 struct XY *dst, *src;
12974 game_buttons_xy[] =
12976 { &game.button.save, &game.button.stop },
12977 { &game.button.pause2, &game.button.pause },
12978 { &game.button.load, &game.button.play },
12979 { &game.button.undo, &game.button.stop },
12980 { &game.button.redo, &game.button.play },
12986 // special case: initialize later added SETUP list size from LEVELS value
12987 if (menu.list_size[GAME_MODE_SETUP] == -1)
12988 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12990 // set default position for snapshot buttons to stop/pause/play buttons
12991 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12992 if ((*game_buttons_xy[i].dst).x == -1 &&
12993 (*game_buttons_xy[i].dst).y == -1)
12994 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12996 // --------------------------------------------------------------------------
12997 // dynamic viewports (including playfield margins, borders and alignments)
12998 // --------------------------------------------------------------------------
13000 // dynamic viewports currently only supported for landscape mode
13001 int display_width = MAX(video.display_width, video.display_height);
13002 int display_height = MIN(video.display_width, video.display_height);
13004 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13006 struct RectWithBorder *vp_window = &viewport.window[i];
13007 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13008 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13009 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13010 boolean dynamic_window_width = (vp_window->min_width != -1);
13011 boolean dynamic_window_height = (vp_window->min_height != -1);
13012 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13013 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13015 // adjust window size if min/max width/height is specified
13017 if (vp_window->min_width != -1)
13019 int window_width = display_width;
13021 // when using static window height, use aspect ratio of display
13022 if (vp_window->min_height == -1)
13023 window_width = vp_window->height * display_width / display_height;
13025 vp_window->width = MAX(vp_window->min_width, window_width);
13028 if (vp_window->min_height != -1)
13030 int window_height = display_height;
13032 // when using static window width, use aspect ratio of display
13033 if (vp_window->min_width == -1)
13034 window_height = vp_window->width * display_height / display_width;
13036 vp_window->height = MAX(vp_window->min_height, window_height);
13039 if (vp_window->max_width != -1)
13040 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13042 if (vp_window->max_height != -1)
13043 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13045 int playfield_width = vp_window->width;
13046 int playfield_height = vp_window->height;
13048 // adjust playfield size and position according to specified margins
13050 playfield_width -= vp_playfield->margin_left;
13051 playfield_width -= vp_playfield->margin_right;
13053 playfield_height -= vp_playfield->margin_top;
13054 playfield_height -= vp_playfield->margin_bottom;
13056 // adjust playfield size if min/max width/height is specified
13058 if (vp_playfield->min_width != -1)
13059 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13061 if (vp_playfield->min_height != -1)
13062 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13064 if (vp_playfield->max_width != -1)
13065 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13067 if (vp_playfield->max_height != -1)
13068 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13070 // adjust playfield position according to specified alignment
13072 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13073 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13074 else if (vp_playfield->align == ALIGN_CENTER)
13075 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13076 else if (vp_playfield->align == ALIGN_RIGHT)
13077 vp_playfield->x += playfield_width - vp_playfield->width;
13079 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13080 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13081 else if (vp_playfield->valign == VALIGN_MIDDLE)
13082 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13083 else if (vp_playfield->valign == VALIGN_BOTTOM)
13084 vp_playfield->y += playfield_height - vp_playfield->height;
13086 vp_playfield->x += vp_playfield->margin_left;
13087 vp_playfield->y += vp_playfield->margin_top;
13089 // adjust individual playfield borders if only default border is specified
13091 if (vp_playfield->border_left == -1)
13092 vp_playfield->border_left = vp_playfield->border_size;
13093 if (vp_playfield->border_right == -1)
13094 vp_playfield->border_right = vp_playfield->border_size;
13095 if (vp_playfield->border_top == -1)
13096 vp_playfield->border_top = vp_playfield->border_size;
13097 if (vp_playfield->border_bottom == -1)
13098 vp_playfield->border_bottom = vp_playfield->border_size;
13100 // set dynamic playfield borders if borders are specified as undefined
13101 // (but only if window size was dynamic and playfield size was static)
13103 if (dynamic_window_width && !dynamic_playfield_width)
13105 if (vp_playfield->border_left == -1)
13107 vp_playfield->border_left = (vp_playfield->x -
13108 vp_playfield->margin_left);
13109 vp_playfield->x -= vp_playfield->border_left;
13110 vp_playfield->width += vp_playfield->border_left;
13113 if (vp_playfield->border_right == -1)
13115 vp_playfield->border_right = (vp_window->width -
13117 vp_playfield->width -
13118 vp_playfield->margin_right);
13119 vp_playfield->width += vp_playfield->border_right;
13123 if (dynamic_window_height && !dynamic_playfield_height)
13125 if (vp_playfield->border_top == -1)
13127 vp_playfield->border_top = (vp_playfield->y -
13128 vp_playfield->margin_top);
13129 vp_playfield->y -= vp_playfield->border_top;
13130 vp_playfield->height += vp_playfield->border_top;
13133 if (vp_playfield->border_bottom == -1)
13135 vp_playfield->border_bottom = (vp_window->height -
13137 vp_playfield->height -
13138 vp_playfield->margin_bottom);
13139 vp_playfield->height += vp_playfield->border_bottom;
13143 // adjust playfield size to be a multiple of a defined alignment tile size
13145 int align_size = vp_playfield->align_size;
13146 int playfield_xtiles = vp_playfield->width / align_size;
13147 int playfield_ytiles = vp_playfield->height / align_size;
13148 int playfield_width_corrected = playfield_xtiles * align_size;
13149 int playfield_height_corrected = playfield_ytiles * align_size;
13150 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13151 i == GFX_SPECIAL_ARG_EDITOR);
13153 if (is_playfield_mode &&
13154 dynamic_playfield_width &&
13155 vp_playfield->width != playfield_width_corrected)
13157 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13159 vp_playfield->width = playfield_width_corrected;
13161 if (vp_playfield->align == ALIGN_LEFT)
13163 vp_playfield->border_left += playfield_xdiff;
13165 else if (vp_playfield->align == ALIGN_RIGHT)
13167 vp_playfield->border_right += playfield_xdiff;
13169 else if (vp_playfield->align == ALIGN_CENTER)
13171 int border_left_diff = playfield_xdiff / 2;
13172 int border_right_diff = playfield_xdiff - border_left_diff;
13174 vp_playfield->border_left += border_left_diff;
13175 vp_playfield->border_right += border_right_diff;
13179 if (is_playfield_mode &&
13180 dynamic_playfield_height &&
13181 vp_playfield->height != playfield_height_corrected)
13183 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13185 vp_playfield->height = playfield_height_corrected;
13187 if (vp_playfield->valign == VALIGN_TOP)
13189 vp_playfield->border_top += playfield_ydiff;
13191 else if (vp_playfield->align == VALIGN_BOTTOM)
13193 vp_playfield->border_right += playfield_ydiff;
13195 else if (vp_playfield->align == VALIGN_MIDDLE)
13197 int border_top_diff = playfield_ydiff / 2;
13198 int border_bottom_diff = playfield_ydiff - border_top_diff;
13200 vp_playfield->border_top += border_top_diff;
13201 vp_playfield->border_bottom += border_bottom_diff;
13205 // adjust door positions according to specified alignment
13207 for (j = 0; j < 2; j++)
13209 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13211 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13212 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13213 else if (vp_door->align == ALIGN_CENTER)
13214 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13215 else if (vp_door->align == ALIGN_RIGHT)
13216 vp_door->x += vp_window->width - vp_door->width;
13218 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13219 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13220 else if (vp_door->valign == VALIGN_MIDDLE)
13221 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13222 else if (vp_door->valign == VALIGN_BOTTOM)
13223 vp_door->y += vp_window->height - vp_door->height;
13228 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13232 struct XYTileSize *dst, *src;
13235 editor_buttons_xy[] =
13238 &editor.button.element_left, &editor.palette.element_left,
13239 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13242 &editor.button.element_middle, &editor.palette.element_middle,
13243 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13246 &editor.button.element_right, &editor.palette.element_right,
13247 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13254 // set default position for element buttons to element graphics
13255 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13257 if ((*editor_buttons_xy[i].dst).x == -1 &&
13258 (*editor_buttons_xy[i].dst).y == -1)
13260 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13262 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13264 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13268 // adjust editor palette rows and columns if specified to be dynamic
13270 if (editor.palette.cols == -1)
13272 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13273 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13274 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13276 editor.palette.cols = (vp_width - sc_width) / bt_width;
13278 if (editor.palette.x == -1)
13280 int palette_width = editor.palette.cols * bt_width + sc_width;
13282 editor.palette.x = (vp_width - palette_width) / 2;
13286 if (editor.palette.rows == -1)
13288 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13289 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13290 int tx_height = getFontHeight(FONT_TEXT_2);
13292 editor.palette.rows = (vp_height - tx_height) / bt_height;
13294 if (editor.palette.y == -1)
13296 int palette_height = editor.palette.rows * bt_height + tx_height;
13298 editor.palette.y = (vp_height - palette_height) / 2;
13303 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13304 boolean initialize)
13306 // special case: check if network and preview player positions are redefined,
13307 // to compare this later against the main menu level preview being redefined
13308 struct TokenIntPtrInfo menu_config_players[] =
13310 { "main.network_players.x", &menu.main.network_players.redefined },
13311 { "main.network_players.y", &menu.main.network_players.redefined },
13312 { "main.preview_players.x", &menu.main.preview_players.redefined },
13313 { "main.preview_players.y", &menu.main.preview_players.redefined },
13314 { "preview.x", &preview.redefined },
13315 { "preview.y", &preview.redefined }
13321 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13322 *menu_config_players[i].value = FALSE;
13326 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13327 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13328 *menu_config_players[i].value = TRUE;
13332 static void InitMenuDesignSettings_PreviewPlayers(void)
13334 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13337 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13339 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13342 static void LoadMenuDesignSettingsFromFilename(char *filename)
13344 static struct TitleFadingInfo tfi;
13345 static struct TitleMessageInfo tmi;
13346 static struct TokenInfo title_tokens[] =
13348 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13349 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13350 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13351 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13352 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13356 static struct TokenInfo titlemessage_tokens[] =
13358 { TYPE_INTEGER, &tmi.x, ".x" },
13359 { TYPE_INTEGER, &tmi.y, ".y" },
13360 { TYPE_INTEGER, &tmi.width, ".width" },
13361 { TYPE_INTEGER, &tmi.height, ".height" },
13362 { TYPE_INTEGER, &tmi.chars, ".chars" },
13363 { TYPE_INTEGER, &tmi.lines, ".lines" },
13364 { TYPE_INTEGER, &tmi.align, ".align" },
13365 { TYPE_INTEGER, &tmi.valign, ".valign" },
13366 { TYPE_INTEGER, &tmi.font, ".font" },
13367 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13368 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13369 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13370 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13371 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13372 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13373 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13374 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13375 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13381 struct TitleFadingInfo *info;
13386 // initialize first titles from "enter screen" definitions, if defined
13387 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13388 { &title_first_default, "menu.enter_screen.TITLE" },
13390 // initialize title screens from "next screen" definitions, if defined
13391 { &title_initial_default, "menu.next_screen.TITLE" },
13392 { &title_default, "menu.next_screen.TITLE" },
13398 struct TitleMessageInfo *array;
13401 titlemessage_arrays[] =
13403 // initialize first titles from "enter screen" definitions, if defined
13404 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13405 { titlescreen_first, "menu.enter_screen.TITLE" },
13406 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13407 { titlemessage_first, "menu.enter_screen.TITLE" },
13409 // initialize titles from "next screen" definitions, if defined
13410 { titlescreen_initial, "menu.next_screen.TITLE" },
13411 { titlescreen, "menu.next_screen.TITLE" },
13412 { titlemessage_initial, "menu.next_screen.TITLE" },
13413 { titlemessage, "menu.next_screen.TITLE" },
13415 // overwrite titles with title definitions, if defined
13416 { titlescreen_initial_first, "[title_initial]" },
13417 { titlescreen_first, "[title]" },
13418 { titlemessage_initial_first, "[title_initial]" },
13419 { titlemessage_first, "[title]" },
13421 { titlescreen_initial, "[title_initial]" },
13422 { titlescreen, "[title]" },
13423 { titlemessage_initial, "[title_initial]" },
13424 { titlemessage, "[title]" },
13426 // overwrite titles with title screen/message definitions, if defined
13427 { titlescreen_initial_first, "[titlescreen_initial]" },
13428 { titlescreen_first, "[titlescreen]" },
13429 { titlemessage_initial_first, "[titlemessage_initial]" },
13430 { titlemessage_first, "[titlemessage]" },
13432 { titlescreen_initial, "[titlescreen_initial]" },
13433 { titlescreen, "[titlescreen]" },
13434 { titlemessage_initial, "[titlemessage_initial]" },
13435 { titlemessage, "[titlemessage]" },
13439 SetupFileHash *setup_file_hash;
13442 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13445 // the following initializes hierarchical values from dynamic configuration
13447 // special case: initialize with default values that may be overwritten
13448 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13449 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13451 struct TokenIntPtrInfo menu_config[] =
13453 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13454 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13455 { "menu.list_size", &menu.list_size[i] }
13458 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13460 char *token = menu_config[j].token;
13461 char *value = getHashEntry(setup_file_hash, token);
13464 *menu_config[j].value = get_integer_from_string(value);
13468 // special case: initialize with default values that may be overwritten
13469 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13470 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13472 struct TokenIntPtrInfo menu_config[] =
13474 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13475 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13476 { "menu.list_size.INFO", &menu.list_size_info[i] },
13477 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13478 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13481 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13483 char *token = menu_config[j].token;
13484 char *value = getHashEntry(setup_file_hash, token);
13487 *menu_config[j].value = get_integer_from_string(value);
13491 // special case: initialize with default values that may be overwritten
13492 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13493 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13495 struct TokenIntPtrInfo menu_config[] =
13497 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13498 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13501 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13503 char *token = menu_config[j].token;
13504 char *value = getHashEntry(setup_file_hash, token);
13507 *menu_config[j].value = get_integer_from_string(value);
13511 // special case: initialize with default values that may be overwritten
13512 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13513 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13515 struct TokenIntPtrInfo menu_config[] =
13517 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13518 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13519 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13520 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13521 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13522 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13523 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13524 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13525 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13526 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13529 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13531 char *token = menu_config[j].token;
13532 char *value = getHashEntry(setup_file_hash, token);
13535 *menu_config[j].value = get_integer_from_string(value);
13539 // special case: initialize with default values that may be overwritten
13540 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13541 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13543 struct TokenIntPtrInfo menu_config[] =
13545 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13546 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13547 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13548 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13549 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13550 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13551 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13552 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13553 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13556 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13558 char *token = menu_config[j].token;
13559 char *value = getHashEntry(setup_file_hash, token);
13562 *menu_config[j].value = get_token_parameter_value(token, value);
13566 // special case: initialize with default values that may be overwritten
13567 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13568 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13572 char *token_prefix;
13573 struct RectWithBorder *struct_ptr;
13577 { "viewport.window", &viewport.window[i] },
13578 { "viewport.playfield", &viewport.playfield[i] },
13579 { "viewport.door_1", &viewport.door_1[i] },
13580 { "viewport.door_2", &viewport.door_2[i] }
13583 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13585 struct TokenIntPtrInfo vp_config[] =
13587 { ".x", &vp_struct[j].struct_ptr->x },
13588 { ".y", &vp_struct[j].struct_ptr->y },
13589 { ".width", &vp_struct[j].struct_ptr->width },
13590 { ".height", &vp_struct[j].struct_ptr->height },
13591 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13592 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13593 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13594 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13595 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13596 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13597 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13598 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13599 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13600 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13601 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13602 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13603 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13604 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13605 { ".align", &vp_struct[j].struct_ptr->align },
13606 { ".valign", &vp_struct[j].struct_ptr->valign }
13609 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13611 char *token = getStringCat2(vp_struct[j].token_prefix,
13612 vp_config[k].token);
13613 char *value = getHashEntry(setup_file_hash, token);
13616 *vp_config[k].value = get_token_parameter_value(token, value);
13623 // special case: initialize with default values that may be overwritten
13624 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13625 for (i = 0; title_info[i].info != NULL; i++)
13627 struct TitleFadingInfo *info = title_info[i].info;
13628 char *base_token = title_info[i].text;
13630 for (j = 0; title_tokens[j].type != -1; j++)
13632 char *token = getStringCat2(base_token, title_tokens[j].text);
13633 char *value = getHashEntry(setup_file_hash, token);
13637 int parameter_value = get_token_parameter_value(token, value);
13641 *(int *)title_tokens[j].value = (int)parameter_value;
13650 // special case: initialize with default values that may be overwritten
13651 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13652 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13654 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13655 char *base_token = titlemessage_arrays[i].text;
13657 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13659 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13660 char *value = getHashEntry(setup_file_hash, token);
13664 int parameter_value = get_token_parameter_value(token, value);
13666 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13670 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13671 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13673 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13683 // read (and overwrite with) values that may be specified in config file
13684 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13686 // special case: check if network and preview player positions are redefined
13687 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13689 freeSetupFileHash(setup_file_hash);
13692 void LoadMenuDesignSettings(void)
13694 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13696 InitMenuDesignSettings_Static();
13697 InitMenuDesignSettings_SpecialPreProcessing();
13698 InitMenuDesignSettings_PreviewPlayers();
13700 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13702 // first look for special settings configured in level series config
13703 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13705 if (fileExists(filename_base))
13706 LoadMenuDesignSettingsFromFilename(filename_base);
13709 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13711 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13712 LoadMenuDesignSettingsFromFilename(filename_local);
13714 InitMenuDesignSettings_SpecialPostProcessing();
13717 void LoadMenuDesignSettings_AfterGraphics(void)
13719 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13722 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13723 boolean ignore_defaults)
13727 for (i = 0; sound_config_vars[i].token != NULL; i++)
13729 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13731 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13732 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13736 *sound_config_vars[i].value =
13737 get_token_parameter_value(sound_config_vars[i].token, value);
13741 void InitSoundSettings_Static(void)
13743 // always start with reliable default values from static default config
13744 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13747 static void LoadSoundSettingsFromFilename(char *filename)
13749 SetupFileHash *setup_file_hash;
13751 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13754 // read (and overwrite with) values that may be specified in config file
13755 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13757 freeSetupFileHash(setup_file_hash);
13760 void LoadSoundSettings(void)
13762 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13764 InitSoundSettings_Static();
13766 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13768 // first look for special settings configured in level series config
13769 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13771 if (fileExists(filename_base))
13772 LoadSoundSettingsFromFilename(filename_base);
13775 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13777 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13778 LoadSoundSettingsFromFilename(filename_local);
13781 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13783 char *filename = getEditorSetupFilename();
13784 SetupFileList *setup_file_list, *list;
13785 SetupFileHash *element_hash;
13786 int num_unknown_tokens = 0;
13789 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13792 element_hash = newSetupFileHash();
13794 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13795 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13797 // determined size may be larger than needed (due to unknown elements)
13799 for (list = setup_file_list; list != NULL; list = list->next)
13802 // add space for up to 3 more elements for padding that may be needed
13803 *num_elements += 3;
13805 // free memory for old list of elements, if needed
13806 checked_free(*elements);
13808 // allocate memory for new list of elements
13809 *elements = checked_malloc(*num_elements * sizeof(int));
13812 for (list = setup_file_list; list != NULL; list = list->next)
13814 char *value = getHashEntry(element_hash, list->token);
13816 if (value == NULL) // try to find obsolete token mapping
13818 char *mapped_token = get_mapped_token(list->token);
13820 if (mapped_token != NULL)
13822 value = getHashEntry(element_hash, mapped_token);
13824 free(mapped_token);
13830 (*elements)[(*num_elements)++] = atoi(value);
13834 if (num_unknown_tokens == 0)
13837 Warn("unknown token(s) found in config file:");
13838 Warn("- config file: '%s'", filename);
13840 num_unknown_tokens++;
13843 Warn("- token: '%s'", list->token);
13847 if (num_unknown_tokens > 0)
13850 while (*num_elements % 4) // pad with empty elements, if needed
13851 (*elements)[(*num_elements)++] = EL_EMPTY;
13853 freeSetupFileList(setup_file_list);
13854 freeSetupFileHash(element_hash);
13857 for (i = 0; i < *num_elements; i++)
13858 Debug("editor", "element '%s' [%d]\n",
13859 element_info[(*elements)[i]].token_name, (*elements)[i]);
13863 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13866 SetupFileHash *setup_file_hash = NULL;
13867 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13868 char *filename_music, *filename_prefix, *filename_info;
13874 token_to_value_ptr[] =
13876 { "title_header", &tmp_music_file_info.title_header },
13877 { "artist_header", &tmp_music_file_info.artist_header },
13878 { "album_header", &tmp_music_file_info.album_header },
13879 { "year_header", &tmp_music_file_info.year_header },
13880 { "played_header", &tmp_music_file_info.played_header },
13882 { "title", &tmp_music_file_info.title },
13883 { "artist", &tmp_music_file_info.artist },
13884 { "album", &tmp_music_file_info.album },
13885 { "year", &tmp_music_file_info.year },
13886 { "played", &tmp_music_file_info.played },
13892 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13893 getCustomMusicFilename(basename));
13895 if (filename_music == NULL)
13898 // ---------- try to replace file extension ----------
13900 filename_prefix = getStringCopy(filename_music);
13901 if (strrchr(filename_prefix, '.') != NULL)
13902 *strrchr(filename_prefix, '.') = '\0';
13903 filename_info = getStringCat2(filename_prefix, ".txt");
13905 if (fileExists(filename_info))
13906 setup_file_hash = loadSetupFileHash(filename_info);
13908 free(filename_prefix);
13909 free(filename_info);
13911 if (setup_file_hash == NULL)
13913 // ---------- try to add file extension ----------
13915 filename_prefix = getStringCopy(filename_music);
13916 filename_info = getStringCat2(filename_prefix, ".txt");
13918 if (fileExists(filename_info))
13919 setup_file_hash = loadSetupFileHash(filename_info);
13921 free(filename_prefix);
13922 free(filename_info);
13925 if (setup_file_hash == NULL)
13928 // ---------- music file info found ----------
13930 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13932 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13934 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13936 *token_to_value_ptr[i].value_ptr =
13937 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13940 tmp_music_file_info.basename = getStringCopy(basename);
13941 tmp_music_file_info.music = music;
13942 tmp_music_file_info.is_sound = is_sound;
13944 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13945 *new_music_file_info = tmp_music_file_info;
13947 return new_music_file_info;
13950 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13952 return get_music_file_info_ext(basename, music, FALSE);
13955 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13957 return get_music_file_info_ext(basename, sound, TRUE);
13960 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13961 char *basename, boolean is_sound)
13963 for (; list != NULL; list = list->next)
13964 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13970 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13972 return music_info_listed_ext(list, basename, FALSE);
13975 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13977 return music_info_listed_ext(list, basename, TRUE);
13980 void LoadMusicInfo(void)
13982 int num_music_noconf = getMusicListSize_NoConf();
13983 int num_music = getMusicListSize();
13984 int num_sounds = getSoundListSize();
13985 struct FileInfo *music, *sound;
13986 struct MusicFileInfo *next, **new;
13990 while (music_file_info != NULL)
13992 next = music_file_info->next;
13994 checked_free(music_file_info->basename);
13996 checked_free(music_file_info->title_header);
13997 checked_free(music_file_info->artist_header);
13998 checked_free(music_file_info->album_header);
13999 checked_free(music_file_info->year_header);
14000 checked_free(music_file_info->played_header);
14002 checked_free(music_file_info->title);
14003 checked_free(music_file_info->artist);
14004 checked_free(music_file_info->album);
14005 checked_free(music_file_info->year);
14006 checked_free(music_file_info->played);
14008 free(music_file_info);
14010 music_file_info = next;
14013 new = &music_file_info;
14015 // get (configured or unconfigured) music file info for all levels
14016 for (i = leveldir_current->first_level;
14017 i <= leveldir_current->last_level; i++)
14021 if (levelset.music[i] != MUS_UNDEFINED)
14023 // get music file info for configured level music
14024 music_nr = levelset.music[i];
14026 else if (num_music_noconf > 0)
14028 // get music file info for unconfigured level music
14029 int level_pos = i - leveldir_current->first_level;
14031 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14038 char *basename = getMusicInfoEntryFilename(music_nr);
14040 if (basename == NULL)
14043 if (!music_info_listed(music_file_info, basename))
14045 *new = get_music_file_info(basename, music_nr);
14048 new = &(*new)->next;
14052 // get music file info for all remaining configured music files
14053 for (i = 0; i < num_music; i++)
14055 music = getMusicListEntry(i);
14057 if (music->filename == NULL)
14060 if (strEqual(music->filename, UNDEFINED_FILENAME))
14063 // a configured file may be not recognized as music
14064 if (!FileIsMusic(music->filename))
14067 if (!music_info_listed(music_file_info, music->filename))
14069 *new = get_music_file_info(music->filename, i);
14072 new = &(*new)->next;
14076 // get sound file info for all configured sound files
14077 for (i = 0; i < num_sounds; i++)
14079 sound = getSoundListEntry(i);
14081 if (sound->filename == NULL)
14084 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14087 // a configured file may be not recognized as sound
14088 if (!FileIsSound(sound->filename))
14091 if (!sound_info_listed(music_file_info, sound->filename))
14093 *new = get_sound_file_info(sound->filename, i);
14095 new = &(*new)->next;
14099 // add pointers to previous list nodes
14101 struct MusicFileInfo *node = music_file_info;
14103 while (node != NULL)
14106 node->next->prev = node;
14112 static void add_helpanim_entry(int element, int action, int direction,
14113 int delay, int *num_list_entries)
14115 struct HelpAnimInfo *new_list_entry;
14116 (*num_list_entries)++;
14119 checked_realloc(helpanim_info,
14120 *num_list_entries * sizeof(struct HelpAnimInfo));
14121 new_list_entry = &helpanim_info[*num_list_entries - 1];
14123 new_list_entry->element = element;
14124 new_list_entry->action = action;
14125 new_list_entry->direction = direction;
14126 new_list_entry->delay = delay;
14129 static void print_unknown_token(char *filename, char *token, int token_nr)
14134 Warn("unknown token(s) found in config file:");
14135 Warn("- config file: '%s'", filename);
14138 Warn("- token: '%s'", token);
14141 static void print_unknown_token_end(int token_nr)
14147 void LoadHelpAnimInfo(void)
14149 char *filename = getHelpAnimFilename();
14150 SetupFileList *setup_file_list = NULL, *list;
14151 SetupFileHash *element_hash, *action_hash, *direction_hash;
14152 int num_list_entries = 0;
14153 int num_unknown_tokens = 0;
14156 if (fileExists(filename))
14157 setup_file_list = loadSetupFileList(filename);
14159 if (setup_file_list == NULL)
14161 // use reliable default values from static configuration
14162 SetupFileList *insert_ptr;
14164 insert_ptr = setup_file_list =
14165 newSetupFileList(helpanim_config[0].token,
14166 helpanim_config[0].value);
14168 for (i = 1; helpanim_config[i].token; i++)
14169 insert_ptr = addListEntry(insert_ptr,
14170 helpanim_config[i].token,
14171 helpanim_config[i].value);
14174 element_hash = newSetupFileHash();
14175 action_hash = newSetupFileHash();
14176 direction_hash = newSetupFileHash();
14178 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14179 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14181 for (i = 0; i < NUM_ACTIONS; i++)
14182 setHashEntry(action_hash, element_action_info[i].suffix,
14183 i_to_a(element_action_info[i].value));
14185 // do not store direction index (bit) here, but direction value!
14186 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14187 setHashEntry(direction_hash, element_direction_info[i].suffix,
14188 i_to_a(1 << element_direction_info[i].value));
14190 for (list = setup_file_list; list != NULL; list = list->next)
14192 char *element_token, *action_token, *direction_token;
14193 char *element_value, *action_value, *direction_value;
14194 int delay = atoi(list->value);
14196 if (strEqual(list->token, "end"))
14198 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14203 /* first try to break element into element/action/direction parts;
14204 if this does not work, also accept combined "element[.act][.dir]"
14205 elements (like "dynamite.active"), which are unique elements */
14207 if (strchr(list->token, '.') == NULL) // token contains no '.'
14209 element_value = getHashEntry(element_hash, list->token);
14210 if (element_value != NULL) // element found
14211 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14212 &num_list_entries);
14215 // no further suffixes found -- this is not an element
14216 print_unknown_token(filename, list->token, num_unknown_tokens++);
14222 // token has format "<prefix>.<something>"
14224 action_token = strchr(list->token, '.'); // suffix may be action ...
14225 direction_token = action_token; // ... or direction
14227 element_token = getStringCopy(list->token);
14228 *strchr(element_token, '.') = '\0';
14230 element_value = getHashEntry(element_hash, element_token);
14232 if (element_value == NULL) // this is no element
14234 element_value = getHashEntry(element_hash, list->token);
14235 if (element_value != NULL) // combined element found
14236 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14237 &num_list_entries);
14239 print_unknown_token(filename, list->token, num_unknown_tokens++);
14241 free(element_token);
14246 action_value = getHashEntry(action_hash, action_token);
14248 if (action_value != NULL) // action found
14250 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14251 &num_list_entries);
14253 free(element_token);
14258 direction_value = getHashEntry(direction_hash, direction_token);
14260 if (direction_value != NULL) // direction found
14262 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14263 &num_list_entries);
14265 free(element_token);
14270 if (strchr(action_token + 1, '.') == NULL)
14272 // no further suffixes found -- this is not an action nor direction
14274 element_value = getHashEntry(element_hash, list->token);
14275 if (element_value != NULL) // combined element found
14276 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14277 &num_list_entries);
14279 print_unknown_token(filename, list->token, num_unknown_tokens++);
14281 free(element_token);
14286 // token has format "<prefix>.<suffix>.<something>"
14288 direction_token = strchr(action_token + 1, '.');
14290 action_token = getStringCopy(action_token);
14291 *strchr(action_token + 1, '.') = '\0';
14293 action_value = getHashEntry(action_hash, action_token);
14295 if (action_value == NULL) // this is no action
14297 element_value = getHashEntry(element_hash, list->token);
14298 if (element_value != NULL) // combined element found
14299 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14300 &num_list_entries);
14302 print_unknown_token(filename, list->token, num_unknown_tokens++);
14304 free(element_token);
14305 free(action_token);
14310 direction_value = getHashEntry(direction_hash, direction_token);
14312 if (direction_value != NULL) // direction found
14314 add_helpanim_entry(atoi(element_value), atoi(action_value),
14315 atoi(direction_value), delay, &num_list_entries);
14317 free(element_token);
14318 free(action_token);
14323 // this is no direction
14325 element_value = getHashEntry(element_hash, list->token);
14326 if (element_value != NULL) // combined element found
14327 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14328 &num_list_entries);
14330 print_unknown_token(filename, list->token, num_unknown_tokens++);
14332 free(element_token);
14333 free(action_token);
14336 print_unknown_token_end(num_unknown_tokens);
14338 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14339 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14341 freeSetupFileList(setup_file_list);
14342 freeSetupFileHash(element_hash);
14343 freeSetupFileHash(action_hash);
14344 freeSetupFileHash(direction_hash);
14347 for (i = 0; i < num_list_entries; i++)
14348 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14349 EL_NAME(helpanim_info[i].element),
14350 helpanim_info[i].element,
14351 helpanim_info[i].action,
14352 helpanim_info[i].direction,
14353 helpanim_info[i].delay);
14357 void LoadHelpTextInfo(void)
14359 char *filename = getHelpTextFilename();
14362 if (helptext_info != NULL)
14364 freeSetupFileHash(helptext_info);
14365 helptext_info = NULL;
14368 if (fileExists(filename))
14369 helptext_info = loadSetupFileHash(filename);
14371 if (helptext_info == NULL)
14373 // use reliable default values from static configuration
14374 helptext_info = newSetupFileHash();
14376 for (i = 0; helptext_config[i].token; i++)
14377 setHashEntry(helptext_info,
14378 helptext_config[i].token,
14379 helptext_config[i].value);
14383 BEGIN_HASH_ITERATION(helptext_info, itr)
14385 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14386 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14388 END_HASH_ITERATION(hash, itr)
14393 // ----------------------------------------------------------------------------
14395 // ----------------------------------------------------------------------------
14397 #define MAX_NUM_CONVERT_LEVELS 1000
14399 void ConvertLevels(void)
14401 static LevelDirTree *convert_leveldir = NULL;
14402 static int convert_level_nr = -1;
14403 static int num_levels_handled = 0;
14404 static int num_levels_converted = 0;
14405 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14408 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14409 global.convert_leveldir);
14411 if (convert_leveldir == NULL)
14412 Fail("no such level identifier: '%s'", global.convert_leveldir);
14414 leveldir_current = convert_leveldir;
14416 if (global.convert_level_nr != -1)
14418 convert_leveldir->first_level = global.convert_level_nr;
14419 convert_leveldir->last_level = global.convert_level_nr;
14422 convert_level_nr = convert_leveldir->first_level;
14424 PrintLine("=", 79);
14425 Print("Converting levels\n");
14426 PrintLine("-", 79);
14427 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14428 Print("Level series name: '%s'\n", convert_leveldir->name);
14429 Print("Level series author: '%s'\n", convert_leveldir->author);
14430 Print("Number of levels: %d\n", convert_leveldir->levels);
14431 PrintLine("=", 79);
14434 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14435 levels_failed[i] = FALSE;
14437 while (convert_level_nr <= convert_leveldir->last_level)
14439 char *level_filename;
14442 level_nr = convert_level_nr++;
14444 Print("Level %03d: ", level_nr);
14446 LoadLevel(level_nr);
14447 if (level.no_level_file || level.no_valid_file)
14449 Print("(no level)\n");
14453 Print("converting level ... ");
14456 // special case: conversion of some EMC levels as requested by ACME
14457 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14460 level_filename = getDefaultLevelFilename(level_nr);
14461 new_level = !fileExists(level_filename);
14465 SaveLevel(level_nr);
14467 num_levels_converted++;
14469 Print("converted.\n");
14473 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14474 levels_failed[level_nr] = TRUE;
14476 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14479 num_levels_handled++;
14483 PrintLine("=", 79);
14484 Print("Number of levels handled: %d\n", num_levels_handled);
14485 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14486 (num_levels_handled ?
14487 num_levels_converted * 100 / num_levels_handled : 0));
14488 PrintLine("-", 79);
14489 Print("Summary (for automatic parsing by scripts):\n");
14490 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14491 convert_leveldir->identifier, num_levels_converted,
14492 num_levels_handled,
14493 (num_levels_handled ?
14494 num_levels_converted * 100 / num_levels_handled : 0));
14496 if (num_levels_handled != num_levels_converted)
14498 Print(", FAILED:");
14499 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14500 if (levels_failed[i])
14505 PrintLine("=", 79);
14507 CloseAllAndExit(0);
14511 // ----------------------------------------------------------------------------
14512 // create and save images for use in level sketches (raw BMP format)
14513 // ----------------------------------------------------------------------------
14515 void CreateLevelSketchImages(void)
14521 InitElementPropertiesGfxElement();
14523 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14524 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14526 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14528 int element = getMappedElement(i);
14529 char basename1[16];
14530 char basename2[16];
14534 sprintf(basename1, "%04d.bmp", i);
14535 sprintf(basename2, "%04ds.bmp", i);
14537 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14538 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14540 DrawSizedElement(0, 0, element, TILESIZE);
14541 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14543 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14544 Fail("cannot save level sketch image file '%s'", filename1);
14546 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14547 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14549 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14550 Fail("cannot save level sketch image file '%s'", filename2);
14555 // create corresponding SQL statements (for normal and small images)
14558 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14559 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14562 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14563 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14565 // optional: create content for forum level sketch demonstration post
14567 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14570 FreeBitmap(bitmap1);
14571 FreeBitmap(bitmap2);
14574 fprintf(stderr, "\n");
14576 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14578 CloseAllAndExit(0);
14582 // ----------------------------------------------------------------------------
14583 // create and save images for element collecting animations (raw BMP format)
14584 // ----------------------------------------------------------------------------
14586 static boolean createCollectImage(int element)
14588 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14591 void CreateCollectElementImages(void)
14595 int anim_frames = num_steps - 1;
14596 int tile_size = TILESIZE;
14597 int anim_width = tile_size * anim_frames;
14598 int anim_height = tile_size;
14599 int num_collect_images = 0;
14600 int pos_collect_images = 0;
14602 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14603 if (createCollectImage(i))
14604 num_collect_images++;
14606 Info("Creating %d element collecting animation images ...",
14607 num_collect_images);
14609 int dst_width = anim_width * 2;
14610 int dst_height = anim_height * num_collect_images / 2;
14611 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14612 char *basename_bmp = "RocksCollect.bmp";
14613 char *basename_png = "RocksCollect.png";
14614 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14615 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14616 int len_filename_bmp = strlen(filename_bmp);
14617 int len_filename_png = strlen(filename_png);
14618 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14619 char cmd_convert[max_command_len];
14621 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14625 // force using RGBA surface for destination bitmap
14626 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14627 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14629 dst_bitmap->surface =
14630 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14632 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14634 if (!createCollectImage(i))
14637 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14638 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14639 int graphic = el2img(i);
14640 char *token_name = element_info[i].token_name;
14641 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14642 Bitmap *src_bitmap;
14645 Info("- creating collecting image for '%s' ...", token_name);
14647 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14649 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14650 tile_size, tile_size, 0, 0);
14652 // force using RGBA surface for temporary bitmap (using transparent black)
14653 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14654 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14656 tmp_bitmap->surface =
14657 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14659 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14661 for (j = 0; j < anim_frames; j++)
14663 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14664 int frame_size = frame_size_final * num_steps;
14665 int offset = (tile_size - frame_size_final) / 2;
14666 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14668 while (frame_size > frame_size_final)
14672 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14674 FreeBitmap(frame_bitmap);
14676 frame_bitmap = half_bitmap;
14679 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14680 frame_size_final, frame_size_final,
14681 dst_x + j * tile_size + offset, dst_y + offset);
14683 FreeBitmap(frame_bitmap);
14686 tmp_bitmap->surface_masked = NULL;
14688 FreeBitmap(tmp_bitmap);
14690 pos_collect_images++;
14693 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14694 Fail("cannot save element collecting image file '%s'", filename_bmp);
14696 FreeBitmap(dst_bitmap);
14698 Info("Converting image file from BMP to PNG ...");
14700 if (system(cmd_convert) != 0)
14701 Fail("converting image file failed");
14703 unlink(filename_bmp);
14707 CloseAllAndExit(0);
14711 // ----------------------------------------------------------------------------
14712 // create and save images for custom and group elements (raw BMP format)
14713 // ----------------------------------------------------------------------------
14715 void CreateCustomElementImages(char *directory)
14717 char *src_basename = "RocksCE-template.ilbm";
14718 char *dst_basename = "RocksCE.bmp";
14719 char *src_filename = getPath2(directory, src_basename);
14720 char *dst_filename = getPath2(directory, dst_basename);
14721 Bitmap *src_bitmap;
14723 int yoffset_ce = 0;
14724 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14727 InitVideoDefaults();
14729 ReCreateBitmap(&backbuffer, video.width, video.height);
14731 src_bitmap = LoadImage(src_filename);
14733 bitmap = CreateBitmap(TILEX * 16 * 2,
14734 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14737 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14744 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14745 TILEX * x, TILEY * y + yoffset_ce);
14747 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14749 TILEX * x + TILEX * 16,
14750 TILEY * y + yoffset_ce);
14752 for (j = 2; j >= 0; j--)
14756 BlitBitmap(src_bitmap, bitmap,
14757 TILEX + c * 7, 0, 6, 10,
14758 TILEX * x + 6 + j * 7,
14759 TILEY * y + 11 + yoffset_ce);
14761 BlitBitmap(src_bitmap, bitmap,
14762 TILEX + c * 8, TILEY, 6, 10,
14763 TILEX * 16 + TILEX * x + 6 + j * 8,
14764 TILEY * y + 10 + yoffset_ce);
14770 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14777 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14778 TILEX * x, TILEY * y + yoffset_ge);
14780 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14782 TILEX * x + TILEX * 16,
14783 TILEY * y + yoffset_ge);
14785 for (j = 1; j >= 0; j--)
14789 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14790 TILEX * x + 6 + j * 10,
14791 TILEY * y + 11 + yoffset_ge);
14793 BlitBitmap(src_bitmap, bitmap,
14794 TILEX + c * 8, TILEY + 12, 6, 10,
14795 TILEX * 16 + TILEX * x + 10 + j * 8,
14796 TILEY * y + 10 + yoffset_ge);
14802 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14803 Fail("cannot save CE graphics file '%s'", dst_filename);
14805 FreeBitmap(bitmap);
14807 CloseAllAndExit(0);