1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
659 EL_BD_MAGIC_WALL, -1,
660 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
661 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
664 EL_BD_MAGIC_WALL, -1,
665 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
666 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
669 EL_BD_MAGIC_WALL, -1,
670 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
671 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
674 EL_BD_MAGIC_WALL, -1,
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
676 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
679 EL_BD_MAGIC_WALL, -1,
680 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
681 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
684 EL_BD_MAGIC_WALL, -1,
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
686 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
689 EL_BD_MAGIC_WALL, -1,
690 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
691 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
696 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
697 &li.bd_clock_extra_time, 30
701 EL_BD_VOODOO_DOLL, -1,
702 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
703 &li.bd_voodoo_collects_diamonds, FALSE
706 EL_BD_VOODOO_DOLL, -1,
707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
708 &li.bd_voodoo_hurt_kills_player, FALSE
711 EL_BD_VOODOO_DOLL, -1,
712 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
713 &li.bd_voodoo_dies_by_rock, FALSE
716 EL_BD_VOODOO_DOLL, -1,
717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
718 &li.bd_voodoo_vanish_by_explosion, TRUE
721 EL_BD_VOODOO_DOLL, -1,
722 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
723 &li.bd_voodoo_penalty_time, 30
728 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
729 &li.bd_slime_is_predictable, TRUE
733 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
734 &li.bd_slime_permeability_rate, 100
738 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
739 &li.bd_slime_permeability_bits_c64, 0
743 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
744 &li.bd_slime_random_seed_c64, -1
748 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
749 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
753 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
754 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
758 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
759 &li.bd_slime_eats_element_2, EL_BD_ROCK
763 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
764 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
768 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
769 &li.bd_slime_eats_element_3, EL_BD_NUT
773 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
774 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
779 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
780 &li.bd_acid_eats_element, EL_BD_SAND
784 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
785 &li.bd_acid_spread_rate, 3
789 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
790 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
795 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
796 &li.bd_biter_move_delay, 0
800 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
801 &li.bd_biter_eats_element, EL_BD_DIAMOND
806 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
807 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
811 EL_BD_EXPANDABLE_WALL_ANY, -1,
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.bd_change_expanding_wall, FALSE
816 EL_BD_EXPANDABLE_WALL_ANY, -1,
817 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
818 &li.bd_expanding_wall_looks_like, EL_BD_WALL
822 EL_BD_REPLICATOR, -1,
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
824 &li.bd_replicators_active, TRUE
827 EL_BD_REPLICATOR, -1,
828 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
829 &li.bd_replicator_create_delay, 4
833 EL_BD_CONVEYOR_LEFT, -1,
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
835 &li.bd_conveyor_belts_active, TRUE
838 EL_BD_CONVEYOR_LEFT, -1,
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
840 &li.bd_conveyor_belts_changed, FALSE
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
846 &li.bd_water_cannot_flow_down, FALSE
851 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
852 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
856 EL_BD_PNEUMATIC_HAMMER, -1,
857 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
858 &li.bd_hammer_walls_break_delay, 5
861 EL_BD_PNEUMATIC_HAMMER, -1,
862 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
863 &li.bd_hammer_walls_reappear, FALSE
866 EL_BD_PNEUMATIC_HAMMER, -1,
867 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
868 &li.bd_hammer_walls_reappear_delay, 100
873 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
874 &li.bd_num_skeletons_needed_for_pot, 5
878 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
879 &li.bd_skeleton_worth_num_diamonds, 0
884 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
885 &li.bd_sand_looks_like, EL_BD_SAND
888 // (the following values are related to various game elements)
892 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
893 &li.score[SC_EMERALD], 10
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
899 &li.score[SC_DIAMOND], 10
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
905 &li.score[SC_BUG], 10
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
911 &li.score[SC_SPACESHIP], 10
916 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
917 &li.score[SC_PACMAN], 10
922 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
923 &li.score[SC_NUT], 10
928 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
929 &li.score[SC_DYNAMITE], 10
934 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
935 &li.score[SC_KEY], 10
940 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
941 &li.score[SC_PEARL], 10
946 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
947 &li.score[SC_CRYSTAL], 10
950 // (amoeba values used by R'n'D game engine only)
953 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
954 &li.amoeba_content, EL_DIAMOND
958 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
963 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
964 &li.grow_into_diggable, TRUE
966 // (amoeba values used by BD game engine only)
969 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
970 &li.bd_amoeba_wait_for_hatching, FALSE
974 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
975 &li.bd_amoeba_start_immediately, TRUE
979 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
980 &li.bd_amoeba_2_explode_by_amoeba, TRUE
984 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
985 &li.bd_amoeba_threshold_too_big, 200
989 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
990 &li.bd_amoeba_slow_growth_time, 200
994 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
995 &li.bd_amoeba_slow_growth_rate, 3
999 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1000 &li.bd_amoeba_fast_growth_rate, 25
1004 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1005 &li.bd_amoeba_content_too_big, EL_BD_ROCK
1009 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1010 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1016 &li.bd_amoeba_2_threshold_too_big, 200
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1021 &li.bd_amoeba_2_slow_growth_time, 200
1025 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1026 &li.bd_amoeba_2_slow_growth_rate, 3
1030 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1031 &li.bd_amoeba_2_fast_growth_rate, 25
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1036 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1040 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1041 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1045 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1046 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1050 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1051 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1056 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1057 &li.yamyam_content, EL_ROCK, NULL,
1058 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 &li.score[SC_YAMYAM], 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1069 &li.score[SC_ROBOT], 10
1073 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1079 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1085 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1086 &li.time_magic_wall, 10
1090 EL_GAME_OF_LIFE, -1,
1091 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1092 &li.game_of_life[0], 2
1095 EL_GAME_OF_LIFE, -1,
1096 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1097 &li.game_of_life[1], 3
1100 EL_GAME_OF_LIFE, -1,
1101 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1102 &li.game_of_life[2], 3
1105 EL_GAME_OF_LIFE, -1,
1106 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1107 &li.game_of_life[3], 3
1110 EL_GAME_OF_LIFE, -1,
1111 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1112 &li.use_life_bugs, FALSE
1117 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1122 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1127 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1137 EL_TIMEGATE_SWITCH, -1,
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1139 &li.time_timegate, 10
1143 EL_LIGHT_SWITCH_ACTIVE, -1,
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1149 EL_SHIELD_NORMAL, -1,
1150 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1151 &li.shield_normal_time, 10
1154 EL_SHIELD_NORMAL, -1,
1155 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1156 &li.score[SC_SHIELD], 10
1160 EL_SHIELD_DEADLY, -1,
1161 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1162 &li.shield_deadly_time, 10
1165 EL_SHIELD_DEADLY, -1,
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1167 &li.score[SC_SHIELD], 10
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1178 &li.extra_time_score, 10
1182 EL_TIME_ORB_FULL, -1,
1183 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1184 &li.time_orb_time, 10
1187 EL_TIME_ORB_FULL, -1,
1188 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1189 &li.use_time_orb_bug, FALSE
1194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1195 &li.use_spring_bug, FALSE
1200 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1201 &li.android_move_time, 10
1205 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1206 &li.android_clone_time, 10
1209 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1210 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1211 &li.android_clone_element[0], EL_EMPTY, NULL,
1212 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1216 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1217 &li.android_clone_element[0], EL_EMPTY, NULL,
1218 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1223 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1224 &li.lenses_score, 10
1228 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1233 EL_EMC_MAGNIFIER, -1,
1234 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1235 &li.magnify_score, 10
1238 EL_EMC_MAGNIFIER, -1,
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1240 &li.magnify_time, 10
1244 EL_EMC_MAGIC_BALL, -1,
1245 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1249 EL_EMC_MAGIC_BALL, -1,
1250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1251 &li.ball_random, FALSE
1254 EL_EMC_MAGIC_BALL, -1,
1255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1256 &li.ball_active_initial, FALSE
1259 EL_EMC_MAGIC_BALL, -1,
1260 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1261 &li.ball_content, EL_EMPTY, NULL,
1262 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1266 EL_SOKOBAN_FIELD_EMPTY, -1,
1267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1268 &li.sb_fields_needed, TRUE
1272 EL_SOKOBAN_OBJECT, -1,
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1274 &li.sb_objects_needed, TRUE
1279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1280 &li.mm_laser_red, FALSE
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1285 &li.mm_laser_green, FALSE
1289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1290 &li.mm_laser_blue, TRUE
1295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1296 &li.df_laser_red, TRUE
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1301 &li.df_laser_green, TRUE
1305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1306 &li.df_laser_blue, FALSE
1310 EL_MM_FUSE_ACTIVE, -1,
1311 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1312 &li.mm_time_fuse, 25
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1317 &li.mm_time_bomb, 75
1321 EL_MM_GRAY_BALL, -1,
1322 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1323 &li.mm_time_ball, 75
1326 EL_MM_GRAY_BALL, -1,
1327 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1328 &li.mm_ball_choice_mode, ANIM_RANDOM
1331 EL_MM_GRAY_BALL, -1,
1332 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1333 &li.mm_ball_content, EL_EMPTY, NULL,
1334 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1337 EL_MM_GRAY_BALL, -1,
1338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1339 &li.rotate_mm_ball_content, TRUE
1342 EL_MM_GRAY_BALL, -1,
1343 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1344 &li.explode_mm_ball, FALSE
1348 EL_MM_STEEL_BLOCK, -1,
1349 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1350 &li.mm_time_block, 75
1353 EL_MM_LIGHTBALL, -1,
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1355 &li.score[SC_ELEM_BONUS], 10
1365 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1369 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1370 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1374 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1375 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1381 &xx_envelope.autowrap, FALSE
1385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1386 &xx_envelope.centered, FALSE
1391 TYPE_STRING, CONF_VALUE_BYTES(1),
1392 &xx_envelope.text, -1, NULL,
1393 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1394 &xx_default_string_empty[0]
1404 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1408 TYPE_STRING, CONF_VALUE_BYTES(1),
1409 &xx_ei.description[0], -1,
1410 &yy_ei.description[0],
1411 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1412 &xx_default_description[0]
1417 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1418 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1419 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1421 #if ENABLE_RESERVED_CODE
1422 // (reserved for later use)
1425 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1426 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1427 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1434 &xx_ei.use_gfx_element, FALSE,
1435 &yy_ei.use_gfx_element
1439 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1440 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1441 &yy_ei.gfx_element_initial
1446 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1447 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1448 &yy_ei.access_direction
1453 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1454 &xx_ei.collect_score_initial, 10,
1455 &yy_ei.collect_score_initial
1459 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1460 &xx_ei.collect_count_initial, 1,
1461 &yy_ei.collect_count_initial
1466 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1467 &xx_ei.ce_value_fixed_initial, 0,
1468 &yy_ei.ce_value_fixed_initial
1472 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1473 &xx_ei.ce_value_random_initial, 0,
1474 &yy_ei.ce_value_random_initial
1478 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1479 &xx_ei.use_last_ce_value, FALSE,
1480 &yy_ei.use_last_ce_value
1485 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1486 &xx_ei.push_delay_fixed, 8,
1487 &yy_ei.push_delay_fixed
1491 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1492 &xx_ei.push_delay_random, 8,
1493 &yy_ei.push_delay_random
1497 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1498 &xx_ei.drop_delay_fixed, 0,
1499 &yy_ei.drop_delay_fixed
1503 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1504 &xx_ei.drop_delay_random, 0,
1505 &yy_ei.drop_delay_random
1509 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1510 &xx_ei.move_delay_fixed, 0,
1511 &yy_ei.move_delay_fixed
1515 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1516 &xx_ei.move_delay_random, 0,
1517 &yy_ei.move_delay_random
1521 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1522 &xx_ei.step_delay_fixed, 0,
1523 &yy_ei.step_delay_fixed
1527 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1528 &xx_ei.step_delay_random, 0,
1529 &yy_ei.step_delay_random
1534 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1535 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1540 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1541 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1542 &yy_ei.move_direction_initial
1546 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1547 &xx_ei.move_stepsize, TILEX / 8,
1548 &yy_ei.move_stepsize
1553 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1554 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1555 &yy_ei.move_enter_element
1559 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1560 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1561 &yy_ei.move_leave_element
1565 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1566 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1567 &yy_ei.move_leave_type
1572 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1573 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1574 &yy_ei.slippery_type
1579 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1580 &xx_ei.explosion_type, EXPLODES_3X3,
1581 &yy_ei.explosion_type
1585 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1586 &xx_ei.explosion_delay, 16,
1587 &yy_ei.explosion_delay
1591 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1592 &xx_ei.ignition_delay, 8,
1593 &yy_ei.ignition_delay
1598 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1599 &xx_ei.content, EL_EMPTY_SPACE,
1601 &xx_num_contents, 1, 1
1604 // ---------- "num_change_pages" must be the last entry ---------------------
1607 -1, SAVE_CONF_ALWAYS,
1608 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1609 &xx_ei.num_change_pages, 1,
1610 &yy_ei.num_change_pages
1621 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1623 // ---------- "current_change_page" must be the first entry -----------------
1626 -1, SAVE_CONF_ALWAYS,
1627 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1628 &xx_current_change_page, -1
1631 // ---------- (the remaining entries can be in any order) -------------------
1635 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1636 &xx_change.can_change, FALSE
1641 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1642 &xx_event_bits[0], 0
1646 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1647 &xx_event_bits[1], 0
1652 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1653 &xx_change.trigger_player, CH_PLAYER_ANY
1657 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1658 &xx_change.trigger_side, CH_SIDE_ANY
1662 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1663 &xx_change.trigger_page, CH_PAGE_ANY
1668 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1669 &xx_change.target_element, EL_EMPTY_SPACE
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1675 &xx_change.delay_fixed, 0
1679 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1680 &xx_change.delay_random, 0
1684 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1685 &xx_change.delay_frames, FRAMES_PER_SECOND
1690 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1691 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1696 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1697 &xx_change.explode, FALSE
1701 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1702 &xx_change.use_target_content, FALSE
1706 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1707 &xx_change.only_if_complete, FALSE
1711 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1712 &xx_change.use_random_replace, FALSE
1716 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1717 &xx_change.random_percentage, 100
1721 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1722 &xx_change.replace_when, CP_WHEN_EMPTY
1727 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1728 &xx_change.has_action, FALSE
1732 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1733 &xx_change.action_type, CA_NO_ACTION
1737 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1738 &xx_change.action_mode, CA_MODE_UNDEFINED
1742 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1743 &xx_change.action_arg, CA_ARG_UNDEFINED
1748 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1749 &xx_change.action_element, EL_EMPTY_SPACE
1754 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1755 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1756 &xx_num_contents, 1, 1
1766 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1770 TYPE_STRING, CONF_VALUE_BYTES(1),
1771 &xx_ei.description[0], -1, NULL,
1772 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1773 &xx_default_description[0]
1778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1779 &xx_ei.use_gfx_element, FALSE
1783 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1784 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1789 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1790 &xx_group.choice_mode, ANIM_RANDOM
1795 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1796 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1797 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1807 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1812 &xx_ei.use_gfx_element, FALSE
1816 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1817 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1827 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1831 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1832 &li.block_snap_field, TRUE
1836 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1837 &li.continuous_snapping, TRUE
1841 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1842 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1846 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1847 &li.use_start_element[0], FALSE
1851 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1852 &li.start_element[0], EL_PLAYER_1
1856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1857 &li.use_artwork_element[0], FALSE
1861 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1862 &li.artwork_element[0], EL_PLAYER_1
1866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1867 &li.use_explosion_element[0], FALSE
1871 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1872 &li.explosion_element[0], EL_PLAYER_1
1887 filetype_id_list[] =
1889 { LEVEL_FILE_TYPE_RND, "RND" },
1890 { LEVEL_FILE_TYPE_BD, "BD" },
1891 { LEVEL_FILE_TYPE_EM, "EM" },
1892 { LEVEL_FILE_TYPE_SP, "SP" },
1893 { LEVEL_FILE_TYPE_DX, "DX" },
1894 { LEVEL_FILE_TYPE_SB, "SB" },
1895 { LEVEL_FILE_TYPE_DC, "DC" },
1896 { LEVEL_FILE_TYPE_MM, "MM" },
1897 { LEVEL_FILE_TYPE_MM, "DF" },
1902 // ============================================================================
1903 // level file functions
1904 // ============================================================================
1906 static boolean check_special_flags(char *flag)
1908 if (strEqual(options.special_flags, flag) ||
1909 strEqual(leveldir_current->special_flags, flag))
1915 static struct DateInfo getCurrentDate(void)
1917 time_t epoch_seconds = time(NULL);
1918 struct tm *now = localtime(&epoch_seconds);
1919 struct DateInfo date;
1921 date.year = now->tm_year + 1900;
1922 date.month = now->tm_mon + 1;
1923 date.day = now->tm_mday;
1925 date.src = DATE_SRC_CLOCK;
1930 static void resetEventFlags(struct ElementChangeInfo *change)
1934 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1935 change->has_event[i] = FALSE;
1938 static void resetEventBits(void)
1942 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1943 xx_event_bits[i] = 0;
1946 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1950 /* important: only change event flag if corresponding event bit is set
1951 (this is because all xx_event_bits[] values are loaded separately,
1952 and all xx_event_bits[] values are set back to zero before loading
1953 another value xx_event_bits[x] (each value representing 32 flags)) */
1955 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1956 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1957 change->has_event[i] = TRUE;
1960 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1964 /* in contrast to the above function setEventFlagsFromEventBits(), it
1965 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1966 depending on the corresponding change->has_event[i] values here, as
1967 all xx_event_bits[] values are reset in resetEventBits() before */
1969 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1970 if (change->has_event[i])
1971 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1974 static char *getDefaultElementDescription(struct ElementInfo *ei)
1976 static char description[MAX_ELEMENT_NAME_LEN + 1];
1977 char *default_description = (ei->custom_description != NULL ?
1978 ei->custom_description :
1979 ei->editor_description);
1982 // always start with reliable default values
1983 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1984 description[i] = '\0';
1986 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1987 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1989 return &description[0];
1992 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1994 char *default_description = getDefaultElementDescription(ei);
1997 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1998 ei->description[i] = default_description[i];
2001 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2005 for (i = 0; conf[i].data_type != -1; i++)
2007 int default_value = conf[i].default_value;
2008 int data_type = conf[i].data_type;
2009 int conf_type = conf[i].conf_type;
2010 int byte_mask = conf_type & CONF_MASK_BYTES;
2012 if (byte_mask == CONF_MASK_MULTI_BYTES)
2014 int default_num_entities = conf[i].default_num_entities;
2015 int max_num_entities = conf[i].max_num_entities;
2017 *(int *)(conf[i].num_entities) = default_num_entities;
2019 if (data_type == TYPE_STRING)
2021 char *default_string = conf[i].default_string;
2022 char *string = (char *)(conf[i].value);
2024 strncpy(string, default_string, max_num_entities);
2026 else if (data_type == TYPE_ELEMENT_LIST)
2028 int *element_array = (int *)(conf[i].value);
2031 for (j = 0; j < max_num_entities; j++)
2032 element_array[j] = default_value;
2034 else if (data_type == TYPE_CONTENT_LIST)
2036 struct Content *content = (struct Content *)(conf[i].value);
2039 for (c = 0; c < max_num_entities; c++)
2040 for (y = 0; y < 3; y++)
2041 for (x = 0; x < 3; x++)
2042 content[c].e[x][y] = default_value;
2045 else // constant size configuration data (1, 2 or 4 bytes)
2047 if (data_type == TYPE_BOOLEAN)
2048 *(boolean *)(conf[i].value) = default_value;
2050 *(int *) (conf[i].value) = default_value;
2055 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2059 for (i = 0; conf[i].data_type != -1; i++)
2061 int data_type = conf[i].data_type;
2062 int conf_type = conf[i].conf_type;
2063 int byte_mask = conf_type & CONF_MASK_BYTES;
2065 if (byte_mask == CONF_MASK_MULTI_BYTES)
2067 int max_num_entities = conf[i].max_num_entities;
2069 if (data_type == TYPE_STRING)
2071 char *string = (char *)(conf[i].value);
2072 char *string_copy = (char *)(conf[i].value_copy);
2074 strncpy(string_copy, string, max_num_entities);
2076 else if (data_type == TYPE_ELEMENT_LIST)
2078 int *element_array = (int *)(conf[i].value);
2079 int *element_array_copy = (int *)(conf[i].value_copy);
2082 for (j = 0; j < max_num_entities; j++)
2083 element_array_copy[j] = element_array[j];
2085 else if (data_type == TYPE_CONTENT_LIST)
2087 struct Content *content = (struct Content *)(conf[i].value);
2088 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2091 for (c = 0; c < max_num_entities; c++)
2092 for (y = 0; y < 3; y++)
2093 for (x = 0; x < 3; x++)
2094 content_copy[c].e[x][y] = content[c].e[x][y];
2097 else // constant size configuration data (1, 2 or 4 bytes)
2099 if (data_type == TYPE_BOOLEAN)
2100 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2102 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2107 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2111 xx_ei = *ei_from; // copy element data into temporary buffer
2112 yy_ei = *ei_to; // copy element data into temporary buffer
2114 copyConfigFromConfigList(chunk_config_CUSX_base);
2119 // ---------- reinitialize and copy change pages ----------
2121 ei_to->num_change_pages = ei_from->num_change_pages;
2122 ei_to->current_change_page = ei_from->current_change_page;
2124 setElementChangePages(ei_to, ei_to->num_change_pages);
2126 for (i = 0; i < ei_to->num_change_pages; i++)
2127 ei_to->change_page[i] = ei_from->change_page[i];
2129 // ---------- copy group element info ----------
2130 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2131 *ei_to->group = *ei_from->group;
2133 // mark this custom element as modified
2134 ei_to->modified_settings = TRUE;
2137 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2139 int change_page_size = sizeof(struct ElementChangeInfo);
2141 ei->num_change_pages = MAX(1, change_pages);
2144 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2146 if (ei->current_change_page >= ei->num_change_pages)
2147 ei->current_change_page = ei->num_change_pages - 1;
2149 ei->change = &ei->change_page[ei->current_change_page];
2152 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2154 xx_change = *change; // copy change data into temporary buffer
2156 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2158 *change = xx_change;
2160 resetEventFlags(change);
2162 change->direct_action = 0;
2163 change->other_action = 0;
2165 change->pre_change_function = NULL;
2166 change->change_function = NULL;
2167 change->post_change_function = NULL;
2170 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2174 li = *level; // copy level data into temporary buffer
2175 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2176 *level = li; // copy temporary buffer back to level data
2178 setLevelInfoToDefaults_BD();
2179 setLevelInfoToDefaults_EM();
2180 setLevelInfoToDefaults_SP();
2181 setLevelInfoToDefaults_MM();
2183 level->native_bd_level = &native_bd_level;
2184 level->native_em_level = &native_em_level;
2185 level->native_sp_level = &native_sp_level;
2186 level->native_mm_level = &native_mm_level;
2188 level->file_version = FILE_VERSION_ACTUAL;
2189 level->game_version = GAME_VERSION_ACTUAL;
2191 level->creation_date = getCurrentDate();
2193 level->encoding_16bit_field = TRUE;
2194 level->encoding_16bit_yamyam = TRUE;
2195 level->encoding_16bit_amoeba = TRUE;
2197 // clear level name and level author string buffers
2198 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2199 level->name[i] = '\0';
2200 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2201 level->author[i] = '\0';
2203 // set level name and level author to default values
2204 strcpy(level->name, NAMELESS_LEVEL_NAME);
2205 strcpy(level->author, ANONYMOUS_NAME);
2207 // set level playfield to playable default level with player and exit
2208 for (x = 0; x < MAX_LEV_FIELDX; x++)
2209 for (y = 0; y < MAX_LEV_FIELDY; y++)
2210 level->field[x][y] = EL_SAND;
2212 level->field[0][0] = EL_PLAYER_1;
2213 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2215 BorderElement = EL_STEELWALL;
2217 // detect custom elements when loading them
2218 level->file_has_custom_elements = FALSE;
2220 // set all bug compatibility flags to "false" => do not emulate this bug
2221 level->use_action_after_change_bug = FALSE;
2223 if (leveldir_current)
2225 // try to determine better author name than 'anonymous'
2226 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2228 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2229 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2233 switch (LEVELCLASS(leveldir_current))
2235 case LEVELCLASS_TUTORIAL:
2236 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2239 case LEVELCLASS_CONTRIB:
2240 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2241 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2244 case LEVELCLASS_PRIVATE:
2245 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2246 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2250 // keep default value
2257 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2259 static boolean clipboard_elements_initialized = FALSE;
2262 InitElementPropertiesStatic();
2264 li = *level; // copy level data into temporary buffer
2265 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2266 *level = li; // copy temporary buffer back to level data
2268 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2271 struct ElementInfo *ei = &element_info[element];
2273 if (element == EL_MM_GRAY_BALL)
2275 struct LevelInfo_MM *level_mm = level->native_mm_level;
2278 for (j = 0; j < level->num_mm_ball_contents; j++)
2279 level->mm_ball_content[j] =
2280 map_element_MM_to_RND(level_mm->ball_content[j]);
2283 // never initialize clipboard elements after the very first time
2284 // (to be able to use clipboard elements between several levels)
2285 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2288 if (IS_ENVELOPE(element))
2290 int envelope_nr = element - EL_ENVELOPE_1;
2292 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2294 level->envelope[envelope_nr] = xx_envelope;
2297 if (IS_CUSTOM_ELEMENT(element) ||
2298 IS_GROUP_ELEMENT(element) ||
2299 IS_INTERNAL_ELEMENT(element))
2301 xx_ei = *ei; // copy element data into temporary buffer
2303 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2308 setElementChangePages(ei, 1);
2309 setElementChangeInfoToDefaults(ei->change);
2311 if (IS_CUSTOM_ELEMENT(element) ||
2312 IS_GROUP_ELEMENT(element))
2314 setElementDescriptionToDefault(ei);
2316 ei->modified_settings = FALSE;
2319 if (IS_CUSTOM_ELEMENT(element) ||
2320 IS_INTERNAL_ELEMENT(element))
2322 // internal values used in level editor
2324 ei->access_type = 0;
2325 ei->access_layer = 0;
2326 ei->access_protected = 0;
2327 ei->walk_to_action = 0;
2328 ei->smash_targets = 0;
2331 ei->can_explode_by_fire = FALSE;
2332 ei->can_explode_smashed = FALSE;
2333 ei->can_explode_impact = FALSE;
2335 ei->current_change_page = 0;
2338 if (IS_GROUP_ELEMENT(element) ||
2339 IS_INTERNAL_ELEMENT(element))
2341 struct ElementGroupInfo *group;
2343 // initialize memory for list of elements in group
2344 if (ei->group == NULL)
2345 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2349 xx_group = *group; // copy group data into temporary buffer
2351 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2356 if (IS_EMPTY_ELEMENT(element) ||
2357 IS_INTERNAL_ELEMENT(element))
2359 xx_ei = *ei; // copy element data into temporary buffer
2361 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2367 clipboard_elements_initialized = TRUE;
2370 static void setLevelInfoToDefaults(struct LevelInfo *level,
2371 boolean level_info_only,
2372 boolean reset_file_status)
2374 setLevelInfoToDefaults_Level(level);
2376 if (!level_info_only)
2377 setLevelInfoToDefaults_Elements(level);
2379 if (reset_file_status)
2381 level->no_valid_file = FALSE;
2382 level->no_level_file = FALSE;
2385 level->changed = FALSE;
2388 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2390 level_file_info->nr = 0;
2391 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2392 level_file_info->packed = FALSE;
2394 setString(&level_file_info->basename, NULL);
2395 setString(&level_file_info->filename, NULL);
2398 int getMappedElement_SB(int, boolean);
2400 static void ActivateLevelTemplate(void)
2404 if (check_special_flags("load_xsb_to_ces"))
2406 // fill smaller playfields with padding "beyond border wall" elements
2407 if (level.fieldx < level_template.fieldx ||
2408 level.fieldy < level_template.fieldy)
2410 short field[level.fieldx][level.fieldy];
2411 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2412 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2413 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2414 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2416 // copy old playfield (which is smaller than the visible area)
2417 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2418 field[x][y] = level.field[x][y];
2420 // fill new, larger playfield with "beyond border wall" elements
2421 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2422 level.field[x][y] = getMappedElement_SB('_', TRUE);
2424 // copy the old playfield to the middle of the new playfield
2425 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2426 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2428 level.fieldx = new_fieldx;
2429 level.fieldy = new_fieldy;
2433 // Currently there is no special action needed to activate the template
2434 // data, because 'element_info' property settings overwrite the original
2435 // level data, while all other variables do not change.
2437 // Exception: 'from_level_template' elements in the original level playfield
2438 // are overwritten with the corresponding elements at the same position in
2439 // playfield from the level template.
2441 for (x = 0; x < level.fieldx; x++)
2442 for (y = 0; y < level.fieldy; y++)
2443 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2444 level.field[x][y] = level_template.field[x][y];
2446 if (check_special_flags("load_xsb_to_ces"))
2448 struct LevelInfo level_backup = level;
2450 // overwrite all individual level settings from template level settings
2451 level = level_template;
2453 // restore level file info
2454 level.file_info = level_backup.file_info;
2456 // restore playfield size
2457 level.fieldx = level_backup.fieldx;
2458 level.fieldy = level_backup.fieldy;
2460 // restore playfield content
2461 for (x = 0; x < level.fieldx; x++)
2462 for (y = 0; y < level.fieldy; y++)
2463 level.field[x][y] = level_backup.field[x][y];
2465 // restore name and author from individual level
2466 strcpy(level.name, level_backup.name);
2467 strcpy(level.author, level_backup.author);
2469 // restore flag "use_custom_template"
2470 level.use_custom_template = level_backup.use_custom_template;
2474 static boolean checkForPackageFromBasename_BD(char *basename)
2476 // check for native BD level file extensions
2477 if (!strSuffixLower(basename, ".bd") &&
2478 !strSuffixLower(basename, ".bdr") &&
2479 !strSuffixLower(basename, ".brc") &&
2480 !strSuffixLower(basename, ".gds"))
2483 // check for standard single-level BD files (like "001.bd")
2484 if (strSuffixLower(basename, ".bd") &&
2485 strlen(basename) == 6 &&
2486 basename[0] >= '0' && basename[0] <= '9' &&
2487 basename[1] >= '0' && basename[1] <= '9' &&
2488 basename[2] >= '0' && basename[2] <= '9')
2491 // this is a level package in native BD file format
2495 static char *getLevelFilenameFromBasename(char *basename)
2497 static char *filename = NULL;
2499 checked_free(filename);
2501 filename = getPath2(getCurrentLevelDir(), basename);
2506 static int getFileTypeFromBasename(char *basename)
2508 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2510 static char *filename = NULL;
2511 struct stat file_status;
2513 // ---------- try to determine file type from filename ----------
2515 // check for typical filename of a Supaplex level package file
2516 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2517 return LEVEL_FILE_TYPE_SP;
2519 // check for typical filename of a Diamond Caves II level package file
2520 if (strSuffixLower(basename, ".dc") ||
2521 strSuffixLower(basename, ".dc2"))
2522 return LEVEL_FILE_TYPE_DC;
2524 // check for typical filename of a Sokoban level package file
2525 if (strSuffixLower(basename, ".xsb") &&
2526 strchr(basename, '%') == NULL)
2527 return LEVEL_FILE_TYPE_SB;
2529 // check for typical filename of a Boulder Dash (GDash) level package file
2530 if (checkForPackageFromBasename_BD(basename))
2531 return LEVEL_FILE_TYPE_BD;
2533 // ---------- try to determine file type from filesize ----------
2535 checked_free(filename);
2536 filename = getPath2(getCurrentLevelDir(), basename);
2538 if (stat(filename, &file_status) == 0)
2540 // check for typical filesize of a Supaplex level package file
2541 if (file_status.st_size == 170496)
2542 return LEVEL_FILE_TYPE_SP;
2545 return LEVEL_FILE_TYPE_UNKNOWN;
2548 static int getFileTypeFromMagicBytes(char *filename, int type)
2552 if ((file = openFile(filename, MODE_READ)))
2554 char chunk_name[CHUNK_ID_LEN + 1];
2556 getFileChunkBE(file, chunk_name, NULL);
2558 if (strEqual(chunk_name, "MMII") ||
2559 strEqual(chunk_name, "MIRR"))
2560 type = LEVEL_FILE_TYPE_MM;
2568 static boolean checkForPackageFromBasename(char *basename)
2570 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2571 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2573 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2576 static char *getSingleLevelBasenameExt(int nr, char *extension)
2578 static char basename[MAX_FILENAME_LEN];
2581 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2583 sprintf(basename, "%03d.%s", nr, extension);
2588 static char *getSingleLevelBasename(int nr)
2590 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2593 static char *getPackedLevelBasename(int type)
2595 static char basename[MAX_FILENAME_LEN];
2596 char *directory = getCurrentLevelDir();
2598 DirectoryEntry *dir_entry;
2600 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2602 if ((dir = openDirectory(directory)) == NULL)
2604 Warn("cannot read current level directory '%s'", directory);
2609 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2611 char *entry_basename = dir_entry->basename;
2612 int entry_type = getFileTypeFromBasename(entry_basename);
2614 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2616 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2619 strcpy(basename, entry_basename);
2626 closeDirectory(dir);
2631 static char *getSingleLevelFilename(int nr)
2633 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2636 #if ENABLE_UNUSED_CODE
2637 static char *getPackedLevelFilename(int type)
2639 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2643 char *getDefaultLevelFilename(int nr)
2645 return getSingleLevelFilename(nr);
2648 #if ENABLE_UNUSED_CODE
2649 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2653 lfi->packed = FALSE;
2655 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2656 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2660 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2661 int type, char *format, ...)
2663 static char basename[MAX_FILENAME_LEN];
2666 va_start(ap, format);
2667 vsprintf(basename, format, ap);
2671 lfi->packed = FALSE;
2673 setString(&lfi->basename, basename);
2674 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2677 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2683 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2684 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2687 static int getFiletypeFromID(char *filetype_id)
2689 char *filetype_id_lower;
2690 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2693 if (filetype_id == NULL)
2694 return LEVEL_FILE_TYPE_UNKNOWN;
2696 filetype_id_lower = getStringToLower(filetype_id);
2698 for (i = 0; filetype_id_list[i].id != NULL; i++)
2700 char *id_lower = getStringToLower(filetype_id_list[i].id);
2702 if (strEqual(filetype_id_lower, id_lower))
2703 filetype = filetype_id_list[i].filetype;
2707 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2711 free(filetype_id_lower);
2716 char *getLocalLevelTemplateFilename(void)
2718 return getDefaultLevelFilename(-1);
2721 char *getGlobalLevelTemplateFilename(void)
2723 // global variable "leveldir_current" must be modified in the loop below
2724 LevelDirTree *leveldir_current_last = leveldir_current;
2725 char *filename = NULL;
2727 // check for template level in path from current to topmost tree node
2729 while (leveldir_current != NULL)
2731 filename = getDefaultLevelFilename(-1);
2733 if (fileExists(filename))
2736 leveldir_current = leveldir_current->node_parent;
2739 // restore global variable "leveldir_current" modified in above loop
2740 leveldir_current = leveldir_current_last;
2745 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2749 // special case: level number is negative => check for level template file
2752 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2753 getSingleLevelBasename(-1));
2755 // replace local level template filename with global template filename
2756 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2758 // no fallback if template file not existing
2762 // special case: check for file name/pattern specified in "levelinfo.conf"
2763 if (leveldir_current->level_filename != NULL)
2765 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2767 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2768 leveldir_current->level_filename, nr);
2770 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2772 if (fileExists(lfi->filename))
2775 else if (leveldir_current->level_filetype != NULL)
2777 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2779 // check for specified native level file with standard file name
2780 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2781 "%03d.%s", nr, LEVELFILE_EXTENSION);
2782 if (fileExists(lfi->filename))
2786 // check for native Rocks'n'Diamonds level file
2787 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2788 "%03d.%s", nr, LEVELFILE_EXTENSION);
2789 if (fileExists(lfi->filename))
2792 // check for native Boulder Dash level file
2793 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2794 if (fileExists(lfi->filename))
2797 // check for Emerald Mine level file (V1)
2798 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2799 'a' + (nr / 10) % 26, '0' + nr % 10);
2800 if (fileExists(lfi->filename))
2802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2803 'A' + (nr / 10) % 26, '0' + nr % 10);
2804 if (fileExists(lfi->filename))
2807 // check for Emerald Mine level file (V2 to V5)
2808 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2809 if (fileExists(lfi->filename))
2812 // check for Emerald Mine level file (V6 / single mode)
2813 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2814 if (fileExists(lfi->filename))
2816 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2817 if (fileExists(lfi->filename))
2820 // check for Emerald Mine level file (V6 / teamwork mode)
2821 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2822 if (fileExists(lfi->filename))
2824 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2825 if (fileExists(lfi->filename))
2828 // check for various packed level file formats
2829 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2830 if (fileExists(lfi->filename))
2833 // no known level file found -- use default values (and fail later)
2834 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2835 "%03d.%s", nr, LEVELFILE_EXTENSION);
2838 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2840 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2841 lfi->type = getFileTypeFromBasename(lfi->basename);
2843 if (lfi->type == LEVEL_FILE_TYPE_RND)
2844 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2847 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2849 // always start with reliable default values
2850 setFileInfoToDefaults(level_file_info);
2852 level_file_info->nr = nr; // set requested level number
2854 determineLevelFileInfo_Filename(level_file_info);
2855 determineLevelFileInfo_Filetype(level_file_info);
2858 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2859 struct LevelFileInfo *lfi_to)
2861 lfi_to->nr = lfi_from->nr;
2862 lfi_to->type = lfi_from->type;
2863 lfi_to->packed = lfi_from->packed;
2865 setString(&lfi_to->basename, lfi_from->basename);
2866 setString(&lfi_to->filename, lfi_from->filename);
2869 // ----------------------------------------------------------------------------
2870 // functions for loading R'n'D level
2871 // ----------------------------------------------------------------------------
2873 int getMappedElement(int element)
2875 // remap some (historic, now obsolete) elements
2879 case EL_PLAYER_OBSOLETE:
2880 element = EL_PLAYER_1;
2883 case EL_KEY_OBSOLETE:
2887 case EL_EM_KEY_1_FILE_OBSOLETE:
2888 element = EL_EM_KEY_1;
2891 case EL_EM_KEY_2_FILE_OBSOLETE:
2892 element = EL_EM_KEY_2;
2895 case EL_EM_KEY_3_FILE_OBSOLETE:
2896 element = EL_EM_KEY_3;
2899 case EL_EM_KEY_4_FILE_OBSOLETE:
2900 element = EL_EM_KEY_4;
2903 case EL_ENVELOPE_OBSOLETE:
2904 element = EL_ENVELOPE_1;
2912 if (element >= NUM_FILE_ELEMENTS)
2914 Warn("invalid level element %d", element);
2916 element = EL_UNKNOWN;
2924 static int getMappedElementByVersion(int element, int game_version)
2926 // remap some elements due to certain game version
2928 if (game_version <= VERSION_IDENT(2,2,0,0))
2930 // map game font elements
2931 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2932 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2933 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2934 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2937 if (game_version < VERSION_IDENT(3,0,0,0))
2939 // map Supaplex gravity tube elements
2940 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2941 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2942 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2943 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2950 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2952 level->file_version = getFileVersion(file);
2953 level->game_version = getFileVersion(file);
2958 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2960 level->creation_date.year = getFile16BitBE(file);
2961 level->creation_date.month = getFile8Bit(file);
2962 level->creation_date.day = getFile8Bit(file);
2964 level->creation_date.src = DATE_SRC_LEVELFILE;
2969 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2971 int initial_player_stepsize;
2972 int initial_player_gravity;
2975 level->fieldx = getFile8Bit(file);
2976 level->fieldy = getFile8Bit(file);
2978 level->time = getFile16BitBE(file);
2979 level->gems_needed = getFile16BitBE(file);
2981 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2982 level->name[i] = getFile8Bit(file);
2983 level->name[MAX_LEVEL_NAME_LEN] = 0;
2985 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2986 level->score[i] = getFile8Bit(file);
2988 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2989 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2990 for (y = 0; y < 3; y++)
2991 for (x = 0; x < 3; x++)
2992 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2994 level->amoeba_speed = getFile8Bit(file);
2995 level->time_magic_wall = getFile8Bit(file);
2996 level->time_wheel = getFile8Bit(file);
2997 level->amoeba_content = getMappedElement(getFile8Bit(file));
2999 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3002 for (i = 0; i < MAX_PLAYERS; i++)
3003 level->initial_player_stepsize[i] = initial_player_stepsize;
3005 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3007 for (i = 0; i < MAX_PLAYERS; i++)
3008 level->initial_player_gravity[i] = initial_player_gravity;
3010 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3011 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3013 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3015 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3016 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3017 level->can_move_into_acid_bits = getFile32BitBE(file);
3018 level->dont_collide_with_bits = getFile8Bit(file);
3020 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3021 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3023 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3024 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3025 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3027 level->game_engine_type = getFile8Bit(file);
3029 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3034 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3038 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3039 level->name[i] = getFile8Bit(file);
3040 level->name[MAX_LEVEL_NAME_LEN] = 0;
3045 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3049 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3050 level->author[i] = getFile8Bit(file);
3051 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3056 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3059 int chunk_size_expected = level->fieldx * level->fieldy;
3061 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3062 stored with 16-bit encoding (and should be twice as big then).
3063 Even worse, playfield data was stored 16-bit when only yamyam content
3064 contained 16-bit elements and vice versa. */
3066 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3067 chunk_size_expected *= 2;
3069 if (chunk_size_expected != chunk_size)
3071 ReadUnusedBytesFromFile(file, chunk_size);
3072 return chunk_size_expected;
3075 for (y = 0; y < level->fieldy; y++)
3076 for (x = 0; x < level->fieldx; x++)
3077 level->field[x][y] =
3078 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3083 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3086 int header_size = 4;
3087 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3088 int chunk_size_expected = header_size + content_size;
3090 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3091 stored with 16-bit encoding (and should be twice as big then).
3092 Even worse, playfield data was stored 16-bit when only yamyam content
3093 contained 16-bit elements and vice versa. */
3095 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3096 chunk_size_expected += content_size;
3098 if (chunk_size_expected != chunk_size)
3100 ReadUnusedBytesFromFile(file, chunk_size);
3101 return chunk_size_expected;
3105 level->num_yamyam_contents = getFile8Bit(file);
3109 // correct invalid number of content fields -- should never happen
3110 if (level->num_yamyam_contents < 1 ||
3111 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3112 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3114 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3115 for (y = 0; y < 3; y++)
3116 for (x = 0; x < 3; x++)
3117 level->yamyam_content[i].e[x][y] =
3118 getMappedElement(level->encoding_16bit_field ?
3119 getFile16BitBE(file) : getFile8Bit(file));
3123 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3128 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3130 element = getMappedElement(getFile16BitBE(file));
3131 num_contents = getFile8Bit(file);
3133 getFile8Bit(file); // content x size (unused)
3134 getFile8Bit(file); // content y size (unused)
3136 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3138 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3139 for (y = 0; y < 3; y++)
3140 for (x = 0; x < 3; x++)
3141 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3143 // correct invalid number of content fields -- should never happen
3144 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3145 num_contents = STD_ELEMENT_CONTENTS;
3147 if (element == EL_YAMYAM)
3149 level->num_yamyam_contents = num_contents;
3151 for (i = 0; i < num_contents; i++)
3152 for (y = 0; y < 3; y++)
3153 for (x = 0; x < 3; x++)
3154 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3156 else if (element == EL_BD_AMOEBA)
3158 level->amoeba_content = content_array[0][0][0];
3162 Warn("cannot load content for element '%d'", element);
3168 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3174 int chunk_size_expected;
3176 element = getMappedElement(getFile16BitBE(file));
3177 if (!IS_ENVELOPE(element))
3178 element = EL_ENVELOPE_1;
3180 envelope_nr = element - EL_ENVELOPE_1;
3182 envelope_len = getFile16BitBE(file);
3184 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3185 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3187 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3189 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3190 if (chunk_size_expected != chunk_size)
3192 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3193 return chunk_size_expected;
3196 for (i = 0; i < envelope_len; i++)
3197 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3202 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3204 int num_changed_custom_elements = getFile16BitBE(file);
3205 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3208 if (chunk_size_expected != chunk_size)
3210 ReadUnusedBytesFromFile(file, chunk_size - 2);
3211 return chunk_size_expected;
3214 for (i = 0; i < num_changed_custom_elements; i++)
3216 int element = getMappedElement(getFile16BitBE(file));
3217 int properties = getFile32BitBE(file);
3219 if (IS_CUSTOM_ELEMENT(element))
3220 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3222 Warn("invalid custom element number %d", element);
3224 // older game versions that wrote level files with CUS1 chunks used
3225 // different default push delay values (not yet stored in level file)
3226 element_info[element].push_delay_fixed = 2;
3227 element_info[element].push_delay_random = 8;
3230 level->file_has_custom_elements = TRUE;
3235 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3237 int num_changed_custom_elements = getFile16BitBE(file);
3238 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3241 if (chunk_size_expected != chunk_size)
3243 ReadUnusedBytesFromFile(file, chunk_size - 2);
3244 return chunk_size_expected;
3247 for (i = 0; i < num_changed_custom_elements; i++)
3249 int element = getMappedElement(getFile16BitBE(file));
3250 int custom_target_element = getMappedElement(getFile16BitBE(file));
3252 if (IS_CUSTOM_ELEMENT(element))
3253 element_info[element].change->target_element = custom_target_element;
3255 Warn("invalid custom element number %d", element);
3258 level->file_has_custom_elements = TRUE;
3263 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3265 int num_changed_custom_elements = getFile16BitBE(file);
3266 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3269 if (chunk_size_expected != chunk_size)
3271 ReadUnusedBytesFromFile(file, chunk_size - 2);
3272 return chunk_size_expected;
3275 for (i = 0; i < num_changed_custom_elements; i++)
3277 int element = getMappedElement(getFile16BitBE(file));
3278 struct ElementInfo *ei = &element_info[element];
3279 unsigned int event_bits;
3281 if (!IS_CUSTOM_ELEMENT(element))
3283 Warn("invalid custom element number %d", element);
3285 element = EL_INTERNAL_DUMMY;
3288 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3289 ei->description[j] = getFile8Bit(file);
3290 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3292 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3294 // some free bytes for future properties and padding
3295 ReadUnusedBytesFromFile(file, 7);
3297 ei->use_gfx_element = getFile8Bit(file);
3298 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3300 ei->collect_score_initial = getFile8Bit(file);
3301 ei->collect_count_initial = getFile8Bit(file);
3303 ei->push_delay_fixed = getFile16BitBE(file);
3304 ei->push_delay_random = getFile16BitBE(file);
3305 ei->move_delay_fixed = getFile16BitBE(file);
3306 ei->move_delay_random = getFile16BitBE(file);
3308 ei->move_pattern = getFile16BitBE(file);
3309 ei->move_direction_initial = getFile8Bit(file);
3310 ei->move_stepsize = getFile8Bit(file);
3312 for (y = 0; y < 3; y++)
3313 for (x = 0; x < 3; x++)
3314 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3316 // bits 0 - 31 of "has_event[]"
3317 event_bits = getFile32BitBE(file);
3318 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3319 if (event_bits & (1u << j))
3320 ei->change->has_event[j] = TRUE;
3322 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3324 ei->change->delay_fixed = getFile16BitBE(file);
3325 ei->change->delay_random = getFile16BitBE(file);
3326 ei->change->delay_frames = getFile16BitBE(file);
3328 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3330 ei->change->explode = getFile8Bit(file);
3331 ei->change->use_target_content = getFile8Bit(file);
3332 ei->change->only_if_complete = getFile8Bit(file);
3333 ei->change->use_random_replace = getFile8Bit(file);
3335 ei->change->random_percentage = getFile8Bit(file);
3336 ei->change->replace_when = getFile8Bit(file);
3338 for (y = 0; y < 3; y++)
3339 for (x = 0; x < 3; x++)
3340 ei->change->target_content.e[x][y] =
3341 getMappedElement(getFile16BitBE(file));
3343 ei->slippery_type = getFile8Bit(file);
3345 // some free bytes for future properties and padding
3346 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3348 // mark that this custom element has been modified
3349 ei->modified_settings = TRUE;
3352 level->file_has_custom_elements = TRUE;
3357 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3359 struct ElementInfo *ei;
3360 int chunk_size_expected;
3364 // ---------- custom element base property values (96 bytes) ----------------
3366 element = getMappedElement(getFile16BitBE(file));
3368 if (!IS_CUSTOM_ELEMENT(element))
3370 Warn("invalid custom element number %d", element);
3372 ReadUnusedBytesFromFile(file, chunk_size - 2);
3377 ei = &element_info[element];
3379 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3380 ei->description[i] = getFile8Bit(file);
3381 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3383 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3385 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3387 ei->num_change_pages = getFile8Bit(file);
3389 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3390 if (chunk_size_expected != chunk_size)
3392 ReadUnusedBytesFromFile(file, chunk_size - 43);
3393 return chunk_size_expected;
3396 ei->ce_value_fixed_initial = getFile16BitBE(file);
3397 ei->ce_value_random_initial = getFile16BitBE(file);
3398 ei->use_last_ce_value = getFile8Bit(file);
3400 ei->use_gfx_element = getFile8Bit(file);
3401 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3403 ei->collect_score_initial = getFile8Bit(file);
3404 ei->collect_count_initial = getFile8Bit(file);
3406 ei->drop_delay_fixed = getFile8Bit(file);
3407 ei->push_delay_fixed = getFile8Bit(file);
3408 ei->drop_delay_random = getFile8Bit(file);
3409 ei->push_delay_random = getFile8Bit(file);
3410 ei->move_delay_fixed = getFile16BitBE(file);
3411 ei->move_delay_random = getFile16BitBE(file);
3413 // bits 0 - 15 of "move_pattern" ...
3414 ei->move_pattern = getFile16BitBE(file);
3415 ei->move_direction_initial = getFile8Bit(file);
3416 ei->move_stepsize = getFile8Bit(file);
3418 ei->slippery_type = getFile8Bit(file);
3420 for (y = 0; y < 3; y++)
3421 for (x = 0; x < 3; x++)
3422 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3424 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3425 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3426 ei->move_leave_type = getFile8Bit(file);
3428 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3429 ei->move_pattern |= (getFile16BitBE(file) << 16);
3431 ei->access_direction = getFile8Bit(file);
3433 ei->explosion_delay = getFile8Bit(file);
3434 ei->ignition_delay = getFile8Bit(file);
3435 ei->explosion_type = getFile8Bit(file);
3437 // some free bytes for future custom property values and padding
3438 ReadUnusedBytesFromFile(file, 1);
3440 // ---------- change page property values (48 bytes) ------------------------
3442 setElementChangePages(ei, ei->num_change_pages);
3444 for (i = 0; i < ei->num_change_pages; i++)
3446 struct ElementChangeInfo *change = &ei->change_page[i];
3447 unsigned int event_bits;
3449 // always start with reliable default values
3450 setElementChangeInfoToDefaults(change);
3452 // bits 0 - 31 of "has_event[]" ...
3453 event_bits = getFile32BitBE(file);
3454 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3455 if (event_bits & (1u << j))
3456 change->has_event[j] = TRUE;
3458 change->target_element = getMappedElement(getFile16BitBE(file));
3460 change->delay_fixed = getFile16BitBE(file);
3461 change->delay_random = getFile16BitBE(file);
3462 change->delay_frames = getFile16BitBE(file);
3464 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3466 change->explode = getFile8Bit(file);
3467 change->use_target_content = getFile8Bit(file);
3468 change->only_if_complete = getFile8Bit(file);
3469 change->use_random_replace = getFile8Bit(file);
3471 change->random_percentage = getFile8Bit(file);
3472 change->replace_when = getFile8Bit(file);
3474 for (y = 0; y < 3; y++)
3475 for (x = 0; x < 3; x++)
3476 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3478 change->can_change = getFile8Bit(file);
3480 change->trigger_side = getFile8Bit(file);
3482 change->trigger_player = getFile8Bit(file);
3483 change->trigger_page = getFile8Bit(file);
3485 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3486 CH_PAGE_ANY : (1 << change->trigger_page));
3488 change->has_action = getFile8Bit(file);
3489 change->action_type = getFile8Bit(file);
3490 change->action_mode = getFile8Bit(file);
3491 change->action_arg = getFile16BitBE(file);
3493 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3494 event_bits = getFile8Bit(file);
3495 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3496 if (event_bits & (1u << (j - 32)))
3497 change->has_event[j] = TRUE;
3500 // mark this custom element as modified
3501 ei->modified_settings = TRUE;
3503 level->file_has_custom_elements = TRUE;
3508 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3510 struct ElementInfo *ei;
3511 struct ElementGroupInfo *group;
3515 element = getMappedElement(getFile16BitBE(file));
3517 if (!IS_GROUP_ELEMENT(element))
3519 Warn("invalid group element number %d", element);
3521 ReadUnusedBytesFromFile(file, chunk_size - 2);
3526 ei = &element_info[element];
3528 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3529 ei->description[i] = getFile8Bit(file);
3530 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3532 group = element_info[element].group;
3534 group->num_elements = getFile8Bit(file);
3536 ei->use_gfx_element = getFile8Bit(file);
3537 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3539 group->choice_mode = getFile8Bit(file);
3541 // some free bytes for future values and padding
3542 ReadUnusedBytesFromFile(file, 3);
3544 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3545 group->element[i] = getMappedElement(getFile16BitBE(file));
3547 // mark this group element as modified
3548 element_info[element].modified_settings = TRUE;
3550 level->file_has_custom_elements = TRUE;
3555 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3556 int element, int real_element)
3558 int micro_chunk_size = 0;
3559 int conf_type = getFile8Bit(file);
3560 int byte_mask = conf_type & CONF_MASK_BYTES;
3561 boolean element_found = FALSE;
3564 micro_chunk_size += 1;
3566 if (byte_mask == CONF_MASK_MULTI_BYTES)
3568 int num_bytes = getFile16BitBE(file);
3569 byte *buffer = checked_malloc(num_bytes);
3571 ReadBytesFromFile(file, buffer, num_bytes);
3573 for (i = 0; conf[i].data_type != -1; i++)
3575 if (conf[i].element == element &&
3576 conf[i].conf_type == conf_type)
3578 int data_type = conf[i].data_type;
3579 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3580 int max_num_entities = conf[i].max_num_entities;
3582 if (num_entities > max_num_entities)
3584 Warn("truncating number of entities for element %d from %d to %d",
3585 element, num_entities, max_num_entities);
3587 num_entities = max_num_entities;
3590 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3591 data_type == TYPE_CONTENT_LIST))
3593 // for element and content lists, zero entities are not allowed
3594 Warn("found empty list of entities for element %d", element);
3596 // do not set "num_entities" here to prevent reading behind buffer
3598 *(int *)(conf[i].num_entities) = 1; // at least one is required
3602 *(int *)(conf[i].num_entities) = num_entities;
3605 element_found = TRUE;
3607 if (data_type == TYPE_STRING)
3609 char *string = (char *)(conf[i].value);
3612 for (j = 0; j < max_num_entities; j++)
3613 string[j] = (j < num_entities ? buffer[j] : '\0');
3615 else if (data_type == TYPE_ELEMENT_LIST)
3617 int *element_array = (int *)(conf[i].value);
3620 for (j = 0; j < num_entities; j++)
3622 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3624 else if (data_type == TYPE_CONTENT_LIST)
3626 struct Content *content= (struct Content *)(conf[i].value);
3629 for (c = 0; c < num_entities; c++)
3630 for (y = 0; y < 3; y++)
3631 for (x = 0; x < 3; x++)
3632 content[c].e[x][y] =
3633 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3636 element_found = FALSE;
3642 checked_free(buffer);
3644 micro_chunk_size += 2 + num_bytes;
3646 else // constant size configuration data (1, 2 or 4 bytes)
3648 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3649 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3650 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3652 for (i = 0; conf[i].data_type != -1; i++)
3654 if (conf[i].element == element &&
3655 conf[i].conf_type == conf_type)
3657 int data_type = conf[i].data_type;
3659 if (data_type == TYPE_ELEMENT)
3660 value = getMappedElement(value);
3662 if (data_type == TYPE_BOOLEAN)
3663 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3665 *(int *) (conf[i].value) = value;
3667 element_found = TRUE;
3673 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3678 char *error_conf_chunk_bytes =
3679 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3680 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3681 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3682 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3683 int error_element = real_element;
3685 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3686 error_conf_chunk_bytes, error_conf_chunk_token,
3687 error_element, EL_NAME(error_element));
3690 return micro_chunk_size;
3693 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3695 int real_chunk_size = 0;
3697 li = *level; // copy level data into temporary buffer
3699 while (!checkEndOfFile(file))
3701 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3703 if (real_chunk_size >= chunk_size)
3707 *level = li; // copy temporary buffer back to level data
3709 return real_chunk_size;
3712 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3714 int real_chunk_size = 0;
3716 li = *level; // copy level data into temporary buffer
3718 while (!checkEndOfFile(file))
3720 int element = getMappedElement(getFile16BitBE(file));
3722 real_chunk_size += 2;
3723 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3725 if (real_chunk_size >= chunk_size)
3729 *level = li; // copy temporary buffer back to level data
3731 return real_chunk_size;
3734 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3736 int real_chunk_size = 0;
3738 li = *level; // copy level data into temporary buffer
3740 while (!checkEndOfFile(file))
3742 int element = getMappedElement(getFile16BitBE(file));
3744 real_chunk_size += 2;
3745 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3747 if (real_chunk_size >= chunk_size)
3751 *level = li; // copy temporary buffer back to level data
3753 return real_chunk_size;
3756 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3758 int element = getMappedElement(getFile16BitBE(file));
3759 int envelope_nr = element - EL_ENVELOPE_1;
3760 int real_chunk_size = 2;
3762 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3764 while (!checkEndOfFile(file))
3766 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3769 if (real_chunk_size >= chunk_size)
3773 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3775 return real_chunk_size;
3778 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3780 int element = getMappedElement(getFile16BitBE(file));
3781 int real_chunk_size = 2;
3782 struct ElementInfo *ei = &element_info[element];
3785 xx_ei = *ei; // copy element data into temporary buffer
3787 xx_ei.num_change_pages = -1;
3789 while (!checkEndOfFile(file))
3791 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3793 if (xx_ei.num_change_pages != -1)
3796 if (real_chunk_size >= chunk_size)
3802 if (ei->num_change_pages == -1)
3804 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3807 ei->num_change_pages = 1;
3809 setElementChangePages(ei, 1);
3810 setElementChangeInfoToDefaults(ei->change);
3812 return real_chunk_size;
3815 // initialize number of change pages stored for this custom element
3816 setElementChangePages(ei, ei->num_change_pages);
3817 for (i = 0; i < ei->num_change_pages; i++)
3818 setElementChangeInfoToDefaults(&ei->change_page[i]);
3820 // start with reading properties for the first change page
3821 xx_current_change_page = 0;
3823 while (!checkEndOfFile(file))
3825 // level file might contain invalid change page number
3826 if (xx_current_change_page >= ei->num_change_pages)
3829 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3831 xx_change = *change; // copy change data into temporary buffer
3833 resetEventBits(); // reset bits; change page might have changed
3835 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3838 *change = xx_change;
3840 setEventFlagsFromEventBits(change);
3842 if (real_chunk_size >= chunk_size)
3846 level->file_has_custom_elements = TRUE;
3848 return real_chunk_size;
3851 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3853 int element = getMappedElement(getFile16BitBE(file));
3854 int real_chunk_size = 2;
3855 struct ElementInfo *ei = &element_info[element];
3856 struct ElementGroupInfo *group = ei->group;
3861 xx_ei = *ei; // copy element data into temporary buffer
3862 xx_group = *group; // copy group data into temporary buffer
3864 while (!checkEndOfFile(file))
3866 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3869 if (real_chunk_size >= chunk_size)
3876 level->file_has_custom_elements = TRUE;
3878 return real_chunk_size;
3881 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3883 int element = getMappedElement(getFile16BitBE(file));
3884 int real_chunk_size = 2;
3885 struct ElementInfo *ei = &element_info[element];
3887 xx_ei = *ei; // copy element data into temporary buffer
3889 while (!checkEndOfFile(file))
3891 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3894 if (real_chunk_size >= chunk_size)
3900 level->file_has_custom_elements = TRUE;
3902 return real_chunk_size;
3905 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3906 struct LevelFileInfo *level_file_info,
3907 boolean level_info_only)
3909 char *filename = level_file_info->filename;
3910 char cookie[MAX_LINE_LEN];
3911 char chunk_name[CHUNK_ID_LEN + 1];
3915 if (!(file = openFile(filename, MODE_READ)))
3917 level->no_valid_file = TRUE;
3918 level->no_level_file = TRUE;
3920 if (level_info_only)
3923 Warn("cannot read level '%s' -- using empty level", filename);
3925 if (!setup.editor.use_template_for_new_levels)
3928 // if level file not found, try to initialize level data from template
3929 filename = getGlobalLevelTemplateFilename();
3931 if (!(file = openFile(filename, MODE_READ)))
3934 // default: for empty levels, use level template for custom elements
3935 level->use_custom_template = TRUE;
3937 level->no_valid_file = FALSE;
3940 getFileChunkBE(file, chunk_name, NULL);
3941 if (strEqual(chunk_name, "RND1"))
3943 getFile32BitBE(file); // not used
3945 getFileChunkBE(file, chunk_name, NULL);
3946 if (!strEqual(chunk_name, "CAVE"))
3948 level->no_valid_file = TRUE;
3950 Warn("unknown format of level file '%s'", filename);
3957 else // check for pre-2.0 file format with cookie string
3959 strcpy(cookie, chunk_name);
3960 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3962 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3963 cookie[strlen(cookie) - 1] = '\0';
3965 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3967 level->no_valid_file = TRUE;
3969 Warn("unknown format of level file '%s'", filename);
3976 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3978 level->no_valid_file = TRUE;
3980 Warn("unsupported version of level file '%s'", filename);
3987 // pre-2.0 level files have no game version, so use file version here
3988 level->game_version = level->file_version;
3991 if (level->file_version < FILE_VERSION_1_2)
3993 // level files from versions before 1.2.0 without chunk structure
3994 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3995 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4003 int (*loader)(File *, int, struct LevelInfo *);
4007 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
4008 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
4009 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
4010 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
4011 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
4012 { "INFO", -1, LoadLevel_INFO },
4013 { "BODY", -1, LoadLevel_BODY },
4014 { "CONT", -1, LoadLevel_CONT },
4015 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
4016 { "CNT3", -1, LoadLevel_CNT3 },
4017 { "CUS1", -1, LoadLevel_CUS1 },
4018 { "CUS2", -1, LoadLevel_CUS2 },
4019 { "CUS3", -1, LoadLevel_CUS3 },
4020 { "CUS4", -1, LoadLevel_CUS4 },
4021 { "GRP1", -1, LoadLevel_GRP1 },
4022 { "CONF", -1, LoadLevel_CONF },
4023 { "ELEM", -1, LoadLevel_ELEM },
4024 { "NOTE", -1, LoadLevel_NOTE },
4025 { "CUSX", -1, LoadLevel_CUSX },
4026 { "GRPX", -1, LoadLevel_GRPX },
4027 { "EMPX", -1, LoadLevel_EMPX },
4032 while (getFileChunkBE(file, chunk_name, &chunk_size))
4036 while (chunk_info[i].name != NULL &&
4037 !strEqual(chunk_name, chunk_info[i].name))
4040 if (chunk_info[i].name == NULL)
4042 Warn("unknown chunk '%s' in level file '%s'",
4043 chunk_name, filename);
4045 ReadUnusedBytesFromFile(file, chunk_size);
4047 else if (chunk_info[i].size != -1 &&
4048 chunk_info[i].size != chunk_size)
4050 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4051 chunk_size, chunk_name, filename);
4053 ReadUnusedBytesFromFile(file, chunk_size);
4057 // call function to load this level chunk
4058 int chunk_size_expected =
4059 (chunk_info[i].loader)(file, chunk_size, level);
4061 if (chunk_size_expected < 0)
4063 Warn("error reading chunk '%s' in level file '%s'",
4064 chunk_name, filename);
4069 // the size of some chunks cannot be checked before reading other
4070 // chunks first (like "HEAD" and "BODY") that contain some header
4071 // information, so check them here
4072 if (chunk_size_expected != chunk_size)
4074 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4075 chunk_size, chunk_name, filename);
4087 // ----------------------------------------------------------------------------
4088 // functions for loading BD level
4089 // ----------------------------------------------------------------------------
4091 #define LEVEL_TO_CAVE(e) (map_element_RND_to_BD_cave(e))
4092 #define CAVE_TO_LEVEL(e) (map_element_BD_to_RND_cave(e))
4094 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4096 struct LevelInfo_BD *level_bd = level->native_bd_level;
4097 GdCave *cave = NULL; // will be changed below
4098 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4099 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4102 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4104 // cave and map newly allocated when set to defaults above
4105 cave = level_bd->cave;
4108 cave->intermission = level->bd_intermission;
4111 cave->level_time[0] = level->time;
4112 cave->level_diamonds[0] = level->gems_needed;
4115 cave->scheduling = level->bd_scheduling_type;
4116 cave->pal_timing = level->bd_pal_timing;
4117 cave->level_speed[0] = level->bd_cycle_delay_ms;
4118 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4119 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4120 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4123 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4124 cave->diamond_value = level->score[SC_EMERALD];
4125 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4127 // compatibility settings
4128 cave->lineshift = level->bd_line_shifting_borders;
4129 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4130 cave->short_explosions = level->bd_short_explosions;
4131 cave->gravity_affects_all = level->bd_gravity_affects_all;
4133 // player properties
4134 cave->diagonal_movements = level->bd_diagonal_movements;
4135 cave->active_is_first_found = level->bd_topmost_player_active;
4136 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4137 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4138 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4139 cave->snap_element = LEVEL_TO_CAVE(level->bd_snap_element);
4141 // element properties
4142 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4143 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4144 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4145 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4146 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4147 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4148 cave->level_magic_wall_time[0] = level->time_magic_wall;
4149 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4150 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4152 cave->magic_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4153 cave->magic_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4154 cave->magic_mega_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4155 cave->magic_nut_to = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4156 cave->magic_nitro_pack_to = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4157 cave->magic_flying_diamond_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4158 cave->magic_flying_stone_to = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4160 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4161 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4162 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4163 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4164 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4165 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4166 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4167 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4168 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4169 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4170 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4172 cave->amoeba_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4173 cave->amoeba_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4174 cave->amoeba_2_too_big_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4175 cave->amoeba_2_enclosed_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4176 cave->amoeba_2_explosion_effect = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4177 cave->amoeba_2_looks_like = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4179 cave->slime_predictable = level->bd_slime_is_predictable;
4180 cave->slime_correct_random = level->bd_slime_correct_random;
4181 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4182 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4183 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4184 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4185 cave->slime_eats_1 = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4186 cave->slime_converts_1 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4187 cave->slime_eats_2 = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4188 cave->slime_converts_2 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4189 cave->slime_eats_3 = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4190 cave->slime_converts_3 = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4192 cave->acid_eats_this = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4193 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4194 cave->acid_turns_to = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4196 cave->biter_delay_frame = level->bd_biter_move_delay;
4197 cave->biter_eat = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4199 cave->bladder_converts_by = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4201 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4203 cave->replicators_active = level->bd_replicators_active;
4204 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4206 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4207 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4209 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4211 cave->nut_turns_to_when_crushed = LEVEL_TO_CAVE(level->bd_nut_content);
4213 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4214 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4215 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4217 cave->skeletons_needed_for_pot = level->bd_num_skeletons_needed_for_pot;
4218 cave->skeletons_worth_diamonds = level->bd_skeleton_worth_num_diamonds;
4220 cave->expanding_wall_looks_like = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4221 cave->dirt_looks_like = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4224 strncpy(cave->name, level->name, sizeof(GdString));
4225 cave->name[sizeof(GdString) - 1] = '\0';
4227 // playfield elements
4228 for (x = 0; x < cave->w; x++)
4229 for (y = 0; y < cave->h; y++)
4230 cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4233 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4235 struct LevelInfo_BD *level_bd = level->native_bd_level;
4236 GdCave *cave = level_bd->cave;
4237 int bd_level_nr = level_bd->level_nr;
4240 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4241 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4244 level->bd_intermission = cave->intermission;
4247 level->time = cave->level_time[bd_level_nr];
4248 level->gems_needed = cave->level_diamonds[bd_level_nr];
4251 level->bd_scheduling_type = cave->scheduling;
4252 level->bd_pal_timing = cave->pal_timing;
4253 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4254 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4255 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4256 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4259 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4260 level->score[SC_EMERALD] = cave->diamond_value;
4261 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4263 // compatibility settings
4264 level->bd_line_shifting_borders = cave->lineshift;
4265 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4266 level->bd_short_explosions = cave->short_explosions;
4267 level->bd_gravity_affects_all = cave->gravity_affects_all;
4269 // player properties
4270 level->bd_diagonal_movements = cave->diagonal_movements;
4271 level->bd_topmost_player_active = cave->active_is_first_found;
4272 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4273 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4274 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4275 level->bd_snap_element = CAVE_TO_LEVEL(cave->snap_element);
4277 // element properties
4278 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4279 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4280 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4281 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4282 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4283 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4284 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4285 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4286 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4288 level->bd_magic_wall_diamond_to = CAVE_TO_LEVEL(cave->magic_diamond_to);
4289 level->bd_magic_wall_rock_to = CAVE_TO_LEVEL(cave->magic_stone_to);
4290 level->bd_magic_wall_mega_rock_to = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4291 level->bd_magic_wall_nut_to = CAVE_TO_LEVEL(cave->magic_nut_to);
4292 level->bd_magic_wall_nitro_pack_to = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4293 level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4294 level->bd_magic_wall_flying_rock_to = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4296 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4297 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4298 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4299 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4300 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4301 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4302 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4303 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4304 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4305 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4306 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4308 level->bd_amoeba_content_too_big = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4309 level->bd_amoeba_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4310 level->bd_amoeba_2_content_too_big = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4311 level->bd_amoeba_2_content_enclosed = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4312 level->bd_amoeba_2_content_exploding = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4313 level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4315 level->bd_slime_is_predictable = cave->slime_predictable;
4316 level->bd_slime_correct_random = cave->slime_correct_random;
4317 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4318 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4319 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4320 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4321 level->bd_slime_eats_element_1 = CAVE_TO_LEVEL(cave->slime_eats_1);
4322 level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4323 level->bd_slime_eats_element_2 = CAVE_TO_LEVEL(cave->slime_eats_2);
4324 level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4325 level->bd_slime_eats_element_3 = CAVE_TO_LEVEL(cave->slime_eats_3);
4326 level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4328 level->bd_acid_eats_element = CAVE_TO_LEVEL(cave->acid_eats_this);
4329 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4330 level->bd_acid_turns_to_element = CAVE_TO_LEVEL(cave->acid_turns_to);
4332 level->bd_biter_move_delay = cave->biter_delay_frame;
4333 level->bd_biter_eats_element = CAVE_TO_LEVEL(cave->biter_eat);
4335 level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4337 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4339 level->bd_replicators_active = cave->replicators_active;
4340 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4342 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4343 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4345 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4347 level->bd_nut_content = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4349 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4350 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4351 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4353 level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4354 level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4356 level->bd_expanding_wall_looks_like = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4357 level->bd_sand_looks_like = CAVE_TO_LEVEL(cave->dirt_looks_like);
4360 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4362 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4363 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4365 // playfield elements
4366 for (x = 0; x < level->fieldx; x++)
4367 for (y = 0; y < level->fieldy; y++)
4368 level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4370 checked_free(cave_name);
4373 static void setTapeInfoToDefaults(void);
4375 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4377 struct LevelInfo_BD *level_bd = level->native_bd_level;
4378 GdCave *cave = level_bd->cave;
4379 GdReplay *replay = level_bd->replay;
4385 // always start with reliable default values
4386 setTapeInfoToDefaults();
4388 tape.level_nr = level_nr; // (currently not used)
4389 tape.random_seed = replay->seed;
4391 TapeSetDateFromIsoDateString(replay->date);
4394 tape.pos[tape.counter].delay = 0;
4396 tape.bd_replay = TRUE;
4398 // all time calculations only used to display approximate tape time
4399 int cave_speed = cave->speed;
4400 int milliseconds_game = 0;
4401 int milliseconds_elapsed = 20;
4403 for (i = 0; i < replay->movements->len; i++)
4405 int replay_action = replay->movements->data[i];
4406 int tape_action = map_action_BD_to_RND(replay_action);
4407 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4408 boolean success = 0;
4412 success = TapeAddAction(action);
4414 milliseconds_game += milliseconds_elapsed;
4416 if (milliseconds_game >= cave_speed)
4418 milliseconds_game -= cave_speed;
4425 tape.pos[tape.counter].delay = 0;
4426 tape.pos[tape.counter].action[0] = 0;
4430 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4436 TapeHaltRecording();
4440 // ----------------------------------------------------------------------------
4441 // functions for loading EM level
4442 // ----------------------------------------------------------------------------
4444 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4446 static int ball_xy[8][2] =
4457 struct LevelInfo_EM *level_em = level->native_em_level;
4458 struct CAVE *cav = level_em->cav;
4461 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4462 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4464 cav->time_seconds = level->time;
4465 cav->gems_needed = level->gems_needed;
4467 cav->emerald_score = level->score[SC_EMERALD];
4468 cav->diamond_score = level->score[SC_DIAMOND];
4469 cav->alien_score = level->score[SC_ROBOT];
4470 cav->tank_score = level->score[SC_SPACESHIP];
4471 cav->bug_score = level->score[SC_BUG];
4472 cav->eater_score = level->score[SC_YAMYAM];
4473 cav->nut_score = level->score[SC_NUT];
4474 cav->dynamite_score = level->score[SC_DYNAMITE];
4475 cav->key_score = level->score[SC_KEY];
4476 cav->exit_score = level->score[SC_TIME_BONUS];
4478 cav->num_eater_arrays = level->num_yamyam_contents;
4480 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4481 for (y = 0; y < 3; y++)
4482 for (x = 0; x < 3; x++)
4483 cav->eater_array[i][y * 3 + x] =
4484 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4486 cav->amoeba_time = level->amoeba_speed;
4487 cav->wonderwall_time = level->time_magic_wall;
4488 cav->wheel_time = level->time_wheel;
4490 cav->android_move_time = level->android_move_time;
4491 cav->android_clone_time = level->android_clone_time;
4492 cav->ball_random = level->ball_random;
4493 cav->ball_active = level->ball_active_initial;
4494 cav->ball_time = level->ball_time;
4495 cav->num_ball_arrays = level->num_ball_contents;
4497 cav->lenses_score = level->lenses_score;
4498 cav->magnify_score = level->magnify_score;
4499 cav->slurp_score = level->slurp_score;
4501 cav->lenses_time = level->lenses_time;
4502 cav->magnify_time = level->magnify_time;
4504 cav->wind_time = 9999;
4505 cav->wind_direction =
4506 map_direction_RND_to_EM(level->wind_direction_initial);
4508 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4509 for (j = 0; j < 8; j++)
4510 cav->ball_array[i][j] =
4511 map_element_RND_to_EM_cave(level->ball_content[i].
4512 e[ball_xy[j][0]][ball_xy[j][1]]);
4514 map_android_clone_elements_RND_to_EM(level);
4516 // first fill the complete playfield with the empty space element
4517 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4518 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4519 cav->cave[x][y] = Cblank;
4521 // then copy the real level contents from level file into the playfield
4522 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4524 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4526 if (level->field[x][y] == EL_AMOEBA_DEAD)
4527 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4529 cav->cave[x][y] = new_element;
4532 for (i = 0; i < MAX_PLAYERS; i++)
4534 cav->player_x[i] = -1;
4535 cav->player_y[i] = -1;
4538 // initialize player positions and delete players from the playfield
4539 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4541 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4543 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4545 cav->player_x[player_nr] = x;
4546 cav->player_y[player_nr] = y;
4548 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4553 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4555 static int ball_xy[8][2] =
4566 struct LevelInfo_EM *level_em = level->native_em_level;
4567 struct CAVE *cav = level_em->cav;
4570 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4571 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4573 level->time = cav->time_seconds;
4574 level->gems_needed = cav->gems_needed;
4576 sprintf(level->name, "Level %d", level->file_info.nr);
4578 level->score[SC_EMERALD] = cav->emerald_score;
4579 level->score[SC_DIAMOND] = cav->diamond_score;
4580 level->score[SC_ROBOT] = cav->alien_score;
4581 level->score[SC_SPACESHIP] = cav->tank_score;
4582 level->score[SC_BUG] = cav->bug_score;
4583 level->score[SC_YAMYAM] = cav->eater_score;
4584 level->score[SC_NUT] = cav->nut_score;
4585 level->score[SC_DYNAMITE] = cav->dynamite_score;
4586 level->score[SC_KEY] = cav->key_score;
4587 level->score[SC_TIME_BONUS] = cav->exit_score;
4589 level->num_yamyam_contents = cav->num_eater_arrays;
4591 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4592 for (y = 0; y < 3; y++)
4593 for (x = 0; x < 3; x++)
4594 level->yamyam_content[i].e[x][y] =
4595 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4597 level->amoeba_speed = cav->amoeba_time;
4598 level->time_magic_wall = cav->wonderwall_time;
4599 level->time_wheel = cav->wheel_time;
4601 level->android_move_time = cav->android_move_time;
4602 level->android_clone_time = cav->android_clone_time;
4603 level->ball_random = cav->ball_random;
4604 level->ball_active_initial = cav->ball_active;
4605 level->ball_time = cav->ball_time;
4606 level->num_ball_contents = cav->num_ball_arrays;
4608 level->lenses_score = cav->lenses_score;
4609 level->magnify_score = cav->magnify_score;
4610 level->slurp_score = cav->slurp_score;
4612 level->lenses_time = cav->lenses_time;
4613 level->magnify_time = cav->magnify_time;
4615 level->wind_direction_initial =
4616 map_direction_EM_to_RND(cav->wind_direction);
4618 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4619 for (j = 0; j < 8; j++)
4620 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4621 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4623 map_android_clone_elements_EM_to_RND(level);
4625 // convert the playfield (some elements need special treatment)
4626 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4628 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4630 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4631 new_element = EL_AMOEBA_DEAD;
4633 level->field[x][y] = new_element;
4636 for (i = 0; i < MAX_PLAYERS; i++)
4638 // in case of all players set to the same field, use the first player
4639 int nr = MAX_PLAYERS - i - 1;
4640 int jx = cav->player_x[nr];
4641 int jy = cav->player_y[nr];
4643 if (jx != -1 && jy != -1)
4644 level->field[jx][jy] = EL_PLAYER_1 + nr;
4647 // time score is counted for each 10 seconds left in Emerald Mine levels
4648 level->time_score_base = 10;
4652 // ----------------------------------------------------------------------------
4653 // functions for loading SP level
4654 // ----------------------------------------------------------------------------
4656 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4658 struct LevelInfo_SP *level_sp = level->native_sp_level;
4659 LevelInfoType *header = &level_sp->header;
4662 level_sp->width = level->fieldx;
4663 level_sp->height = level->fieldy;
4665 for (x = 0; x < level->fieldx; x++)
4666 for (y = 0; y < level->fieldy; y++)
4667 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4669 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4671 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4672 header->LevelTitle[i] = level->name[i];
4673 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4675 header->InfotronsNeeded = level->gems_needed;
4677 header->SpecialPortCount = 0;
4679 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4681 boolean gravity_port_found = FALSE;
4682 boolean gravity_port_valid = FALSE;
4683 int gravity_port_flag;
4684 int gravity_port_base_element;
4685 int element = level->field[x][y];
4687 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4688 element <= EL_SP_GRAVITY_ON_PORT_UP)
4690 gravity_port_found = TRUE;
4691 gravity_port_valid = TRUE;
4692 gravity_port_flag = 1;
4693 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4695 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4696 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4698 gravity_port_found = TRUE;
4699 gravity_port_valid = TRUE;
4700 gravity_port_flag = 0;
4701 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4703 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4704 element <= EL_SP_GRAVITY_PORT_UP)
4706 // change R'n'D style gravity inverting special port to normal port
4707 // (there are no gravity inverting ports in native Supaplex engine)
4709 gravity_port_found = TRUE;
4710 gravity_port_valid = FALSE;
4711 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4714 if (gravity_port_found)
4716 if (gravity_port_valid &&
4717 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4719 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4721 port->PortLocation = (y * level->fieldx + x) * 2;
4722 port->Gravity = gravity_port_flag;
4724 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4726 header->SpecialPortCount++;
4730 // change special gravity port to normal port
4732 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4735 level_sp->playfield[x][y] = element - EL_SP_START;
4740 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4742 struct LevelInfo_SP *level_sp = level->native_sp_level;
4743 LevelInfoType *header = &level_sp->header;
4744 boolean num_invalid_elements = 0;
4747 level->fieldx = level_sp->width;
4748 level->fieldy = level_sp->height;
4750 for (x = 0; x < level->fieldx; x++)
4752 for (y = 0; y < level->fieldy; y++)
4754 int element_old = level_sp->playfield[x][y];
4755 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4757 if (element_new == EL_UNKNOWN)
4759 num_invalid_elements++;
4761 Debug("level:native:SP", "invalid element %d at position %d, %d",
4765 level->field[x][y] = element_new;
4769 if (num_invalid_elements > 0)
4770 Warn("found %d invalid elements%s", num_invalid_elements,
4771 (!options.debug ? " (use '--debug' for more details)" : ""));
4773 for (i = 0; i < MAX_PLAYERS; i++)
4774 level->initial_player_gravity[i] =
4775 (header->InitialGravity == 1 ? TRUE : FALSE);
4777 // skip leading spaces
4778 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4779 if (header->LevelTitle[i] != ' ')
4783 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4784 level->name[j] = header->LevelTitle[i];
4785 level->name[j] = '\0';
4787 // cut trailing spaces
4789 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4790 level->name[j - 1] = '\0';
4792 level->gems_needed = header->InfotronsNeeded;
4794 for (i = 0; i < header->SpecialPortCount; i++)
4796 SpecialPortType *port = &header->SpecialPort[i];
4797 int port_location = port->PortLocation;
4798 int gravity = port->Gravity;
4799 int port_x, port_y, port_element;
4801 port_x = (port_location / 2) % level->fieldx;
4802 port_y = (port_location / 2) / level->fieldx;
4804 if (port_x < 0 || port_x >= level->fieldx ||
4805 port_y < 0 || port_y >= level->fieldy)
4807 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4812 port_element = level->field[port_x][port_y];
4814 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4815 port_element > EL_SP_GRAVITY_PORT_UP)
4817 Warn("no special port at position (%d, %d)", port_x, port_y);
4822 // change previous (wrong) gravity inverting special port to either
4823 // gravity enabling special port or gravity disabling special port
4824 level->field[port_x][port_y] +=
4825 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4826 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4829 // change special gravity ports without database entries to normal ports
4830 for (x = 0; x < level->fieldx; x++)
4831 for (y = 0; y < level->fieldy; y++)
4832 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4833 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4834 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4836 level->time = 0; // no time limit
4837 level->amoeba_speed = 0;
4838 level->time_magic_wall = 0;
4839 level->time_wheel = 0;
4840 level->amoeba_content = EL_EMPTY;
4842 // original Supaplex does not use score values -- rate by playing time
4843 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4844 level->score[i] = 0;
4846 level->rate_time_over_score = TRUE;
4848 // there are no yamyams in supaplex levels
4849 for (i = 0; i < level->num_yamyam_contents; i++)
4850 for (x = 0; x < 3; x++)
4851 for (y = 0; y < 3; y++)
4852 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4855 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4857 struct LevelInfo_SP *level_sp = level->native_sp_level;
4858 struct DemoInfo_SP *demo = &level_sp->demo;
4861 // always start with reliable default values
4862 demo->is_available = FALSE;
4865 if (TAPE_IS_EMPTY(tape))
4868 demo->level_nr = tape.level_nr; // (currently not used)
4870 level_sp->header.DemoRandomSeed = tape.random_seed;
4874 for (i = 0; i < tape.length; i++)
4876 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4877 int demo_repeat = tape.pos[i].delay;
4878 int demo_entries = (demo_repeat + 15) / 16;
4880 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4882 Warn("tape truncated: size exceeds maximum SP demo size %d",
4888 for (j = 0; j < demo_repeat / 16; j++)
4889 demo->data[demo->length++] = 0xf0 | demo_action;
4891 if (demo_repeat % 16)
4892 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4895 demo->is_available = TRUE;
4898 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4900 struct LevelInfo_SP *level_sp = level->native_sp_level;
4901 struct DemoInfo_SP *demo = &level_sp->demo;
4902 char *filename = level->file_info.filename;
4905 // always start with reliable default values
4906 setTapeInfoToDefaults();
4908 if (!demo->is_available)
4911 tape.level_nr = demo->level_nr; // (currently not used)
4912 tape.random_seed = level_sp->header.DemoRandomSeed;
4914 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4917 tape.pos[tape.counter].delay = 0;
4919 for (i = 0; i < demo->length; i++)
4921 int demo_action = demo->data[i] & 0x0f;
4922 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4923 int tape_action = map_key_SP_to_RND(demo_action);
4924 int tape_repeat = demo_repeat + 1;
4925 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4926 boolean success = 0;
4929 for (j = 0; j < tape_repeat; j++)
4930 success = TapeAddAction(action);
4934 Warn("SP demo truncated: size exceeds maximum tape size %d",
4941 TapeHaltRecording();
4945 // ----------------------------------------------------------------------------
4946 // functions for loading MM level
4947 // ----------------------------------------------------------------------------
4949 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4951 struct LevelInfo_MM *level_mm = level->native_mm_level;
4954 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4955 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4957 level_mm->time = level->time;
4958 level_mm->kettles_needed = level->gems_needed;
4959 level_mm->auto_count_kettles = level->auto_count_gems;
4961 level_mm->mm_laser_red = level->mm_laser_red;
4962 level_mm->mm_laser_green = level->mm_laser_green;
4963 level_mm->mm_laser_blue = level->mm_laser_blue;
4965 level_mm->df_laser_red = level->df_laser_red;
4966 level_mm->df_laser_green = level->df_laser_green;
4967 level_mm->df_laser_blue = level->df_laser_blue;
4969 strcpy(level_mm->name, level->name);
4970 strcpy(level_mm->author, level->author);
4972 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4973 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4974 level_mm->score[SC_KEY] = level->score[SC_KEY];
4975 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4976 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4978 level_mm->amoeba_speed = level->amoeba_speed;
4979 level_mm->time_fuse = level->mm_time_fuse;
4980 level_mm->time_bomb = level->mm_time_bomb;
4981 level_mm->time_ball = level->mm_time_ball;
4982 level_mm->time_block = level->mm_time_block;
4984 level_mm->num_ball_contents = level->num_mm_ball_contents;
4985 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4986 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4987 level_mm->explode_ball = level->explode_mm_ball;
4989 for (i = 0; i < level->num_mm_ball_contents; i++)
4990 level_mm->ball_content[i] =
4991 map_element_RND_to_MM(level->mm_ball_content[i]);
4993 for (x = 0; x < level->fieldx; x++)
4994 for (y = 0; y < level->fieldy; y++)
4996 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4999 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5001 struct LevelInfo_MM *level_mm = level->native_mm_level;
5004 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5005 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5007 level->time = level_mm->time;
5008 level->gems_needed = level_mm->kettles_needed;
5009 level->auto_count_gems = level_mm->auto_count_kettles;
5011 level->mm_laser_red = level_mm->mm_laser_red;
5012 level->mm_laser_green = level_mm->mm_laser_green;
5013 level->mm_laser_blue = level_mm->mm_laser_blue;
5015 level->df_laser_red = level_mm->df_laser_red;
5016 level->df_laser_green = level_mm->df_laser_green;
5017 level->df_laser_blue = level_mm->df_laser_blue;
5019 strcpy(level->name, level_mm->name);
5021 // only overwrite author from 'levelinfo.conf' if author defined in level
5022 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5023 strcpy(level->author, level_mm->author);
5025 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
5026 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
5027 level->score[SC_KEY] = level_mm->score[SC_KEY];
5028 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5029 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5031 level->amoeba_speed = level_mm->amoeba_speed;
5032 level->mm_time_fuse = level_mm->time_fuse;
5033 level->mm_time_bomb = level_mm->time_bomb;
5034 level->mm_time_ball = level_mm->time_ball;
5035 level->mm_time_block = level_mm->time_block;
5037 level->num_mm_ball_contents = level_mm->num_ball_contents;
5038 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5039 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5040 level->explode_mm_ball = level_mm->explode_ball;
5042 for (i = 0; i < level->num_mm_ball_contents; i++)
5043 level->mm_ball_content[i] =
5044 map_element_MM_to_RND(level_mm->ball_content[i]);
5046 for (x = 0; x < level->fieldx; x++)
5047 for (y = 0; y < level->fieldy; y++)
5048 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5052 // ----------------------------------------------------------------------------
5053 // functions for loading DC level
5054 // ----------------------------------------------------------------------------
5056 #define DC_LEVEL_HEADER_SIZE 344
5058 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5061 static int last_data_encoded;
5065 int diff_hi, diff_lo;
5066 int data_hi, data_lo;
5067 unsigned short data_decoded;
5071 last_data_encoded = 0;
5078 diff = data_encoded - last_data_encoded;
5079 diff_hi = diff & ~0xff;
5080 diff_lo = diff & 0xff;
5084 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5085 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5086 data_hi = data_hi & 0xff00;
5088 data_decoded = data_hi | data_lo;
5090 last_data_encoded = data_encoded;
5092 offset1 = (offset1 + 1) % 31;
5093 offset2 = offset2 & 0xff;
5095 return data_decoded;
5098 static int getMappedElement_DC(int element)
5106 // 0x0117 - 0x036e: (?)
5109 // 0x042d - 0x0684: (?)
5125 element = EL_CRYSTAL;
5128 case 0x0e77: // quicksand (boulder)
5129 element = EL_QUICKSAND_FAST_FULL;
5132 case 0x0e99: // slow quicksand (boulder)
5133 element = EL_QUICKSAND_FULL;
5137 element = EL_EM_EXIT_OPEN;
5141 element = EL_EM_EXIT_CLOSED;
5145 element = EL_EM_STEEL_EXIT_OPEN;
5149 element = EL_EM_STEEL_EXIT_CLOSED;
5152 case 0x0f4f: // dynamite (lit 1)
5153 element = EL_EM_DYNAMITE_ACTIVE;
5156 case 0x0f57: // dynamite (lit 2)
5157 element = EL_EM_DYNAMITE_ACTIVE;
5160 case 0x0f5f: // dynamite (lit 3)
5161 element = EL_EM_DYNAMITE_ACTIVE;
5164 case 0x0f67: // dynamite (lit 4)
5165 element = EL_EM_DYNAMITE_ACTIVE;
5172 element = EL_AMOEBA_WET;
5176 element = EL_AMOEBA_DROP;
5180 element = EL_DC_MAGIC_WALL;
5184 element = EL_SPACESHIP_UP;
5188 element = EL_SPACESHIP_DOWN;
5192 element = EL_SPACESHIP_LEFT;
5196 element = EL_SPACESHIP_RIGHT;
5200 element = EL_BUG_UP;
5204 element = EL_BUG_DOWN;
5208 element = EL_BUG_LEFT;
5212 element = EL_BUG_RIGHT;
5216 element = EL_MOLE_UP;
5220 element = EL_MOLE_DOWN;
5224 element = EL_MOLE_LEFT;
5228 element = EL_MOLE_RIGHT;
5236 element = EL_YAMYAM_UP;
5240 element = EL_SWITCHGATE_OPEN;
5244 element = EL_SWITCHGATE_CLOSED;
5248 element = EL_DC_SWITCHGATE_SWITCH_UP;
5252 element = EL_TIMEGATE_CLOSED;
5255 case 0x144c: // conveyor belt switch (green)
5256 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5259 case 0x144f: // conveyor belt switch (red)
5260 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5263 case 0x1452: // conveyor belt switch (blue)
5264 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5268 element = EL_CONVEYOR_BELT_3_MIDDLE;
5272 element = EL_CONVEYOR_BELT_3_LEFT;
5276 element = EL_CONVEYOR_BELT_3_RIGHT;
5280 element = EL_CONVEYOR_BELT_1_MIDDLE;
5284 element = EL_CONVEYOR_BELT_1_LEFT;
5288 element = EL_CONVEYOR_BELT_1_RIGHT;
5292 element = EL_CONVEYOR_BELT_4_MIDDLE;
5296 element = EL_CONVEYOR_BELT_4_LEFT;
5300 element = EL_CONVEYOR_BELT_4_RIGHT;
5304 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5308 element = EL_EXPANDABLE_WALL_VERTICAL;
5312 element = EL_EXPANDABLE_WALL_ANY;
5315 case 0x14ce: // growing steel wall (left/right)
5316 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5319 case 0x14df: // growing steel wall (up/down)
5320 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5323 case 0x14e8: // growing steel wall (up/down/left/right)
5324 element = EL_EXPANDABLE_STEELWALL_ANY;
5328 element = EL_SHIELD_DEADLY;
5332 element = EL_EXTRA_TIME;
5340 element = EL_EMPTY_SPACE;
5343 case 0x1578: // quicksand (empty)
5344 element = EL_QUICKSAND_FAST_EMPTY;
5347 case 0x1579: // slow quicksand (empty)
5348 element = EL_QUICKSAND_EMPTY;
5358 element = EL_EM_DYNAMITE;
5361 case 0x15a1: // key (red)
5362 element = EL_EM_KEY_1;
5365 case 0x15a2: // key (yellow)
5366 element = EL_EM_KEY_2;
5369 case 0x15a3: // key (blue)
5370 element = EL_EM_KEY_4;
5373 case 0x15a4: // key (green)
5374 element = EL_EM_KEY_3;
5377 case 0x15a5: // key (white)
5378 element = EL_DC_KEY_WHITE;
5382 element = EL_WALL_SLIPPERY;
5389 case 0x15a8: // wall (not round)
5393 case 0x15a9: // (blue)
5394 element = EL_CHAR_A;
5397 case 0x15aa: // (blue)
5398 element = EL_CHAR_B;
5401 case 0x15ab: // (blue)
5402 element = EL_CHAR_C;
5405 case 0x15ac: // (blue)
5406 element = EL_CHAR_D;
5409 case 0x15ad: // (blue)
5410 element = EL_CHAR_E;
5413 case 0x15ae: // (blue)
5414 element = EL_CHAR_F;
5417 case 0x15af: // (blue)
5418 element = EL_CHAR_G;
5421 case 0x15b0: // (blue)
5422 element = EL_CHAR_H;
5425 case 0x15b1: // (blue)
5426 element = EL_CHAR_I;
5429 case 0x15b2: // (blue)
5430 element = EL_CHAR_J;
5433 case 0x15b3: // (blue)
5434 element = EL_CHAR_K;
5437 case 0x15b4: // (blue)
5438 element = EL_CHAR_L;
5441 case 0x15b5: // (blue)
5442 element = EL_CHAR_M;
5445 case 0x15b6: // (blue)
5446 element = EL_CHAR_N;
5449 case 0x15b7: // (blue)
5450 element = EL_CHAR_O;
5453 case 0x15b8: // (blue)
5454 element = EL_CHAR_P;
5457 case 0x15b9: // (blue)
5458 element = EL_CHAR_Q;
5461 case 0x15ba: // (blue)
5462 element = EL_CHAR_R;
5465 case 0x15bb: // (blue)
5466 element = EL_CHAR_S;
5469 case 0x15bc: // (blue)
5470 element = EL_CHAR_T;
5473 case 0x15bd: // (blue)
5474 element = EL_CHAR_U;
5477 case 0x15be: // (blue)
5478 element = EL_CHAR_V;
5481 case 0x15bf: // (blue)
5482 element = EL_CHAR_W;
5485 case 0x15c0: // (blue)
5486 element = EL_CHAR_X;
5489 case 0x15c1: // (blue)
5490 element = EL_CHAR_Y;
5493 case 0x15c2: // (blue)
5494 element = EL_CHAR_Z;
5497 case 0x15c3: // (blue)
5498 element = EL_CHAR_AUMLAUT;
5501 case 0x15c4: // (blue)
5502 element = EL_CHAR_OUMLAUT;
5505 case 0x15c5: // (blue)
5506 element = EL_CHAR_UUMLAUT;
5509 case 0x15c6: // (blue)
5510 element = EL_CHAR_0;
5513 case 0x15c7: // (blue)
5514 element = EL_CHAR_1;
5517 case 0x15c8: // (blue)
5518 element = EL_CHAR_2;
5521 case 0x15c9: // (blue)
5522 element = EL_CHAR_3;
5525 case 0x15ca: // (blue)
5526 element = EL_CHAR_4;
5529 case 0x15cb: // (blue)
5530 element = EL_CHAR_5;
5533 case 0x15cc: // (blue)
5534 element = EL_CHAR_6;
5537 case 0x15cd: // (blue)
5538 element = EL_CHAR_7;
5541 case 0x15ce: // (blue)
5542 element = EL_CHAR_8;
5545 case 0x15cf: // (blue)
5546 element = EL_CHAR_9;
5549 case 0x15d0: // (blue)
5550 element = EL_CHAR_PERIOD;
5553 case 0x15d1: // (blue)
5554 element = EL_CHAR_EXCLAM;
5557 case 0x15d2: // (blue)
5558 element = EL_CHAR_COLON;
5561 case 0x15d3: // (blue)
5562 element = EL_CHAR_LESS;
5565 case 0x15d4: // (blue)
5566 element = EL_CHAR_GREATER;
5569 case 0x15d5: // (blue)
5570 element = EL_CHAR_QUESTION;
5573 case 0x15d6: // (blue)
5574 element = EL_CHAR_COPYRIGHT;
5577 case 0x15d7: // (blue)
5578 element = EL_CHAR_UP;
5581 case 0x15d8: // (blue)
5582 element = EL_CHAR_DOWN;
5585 case 0x15d9: // (blue)
5586 element = EL_CHAR_BUTTON;
5589 case 0x15da: // (blue)
5590 element = EL_CHAR_PLUS;
5593 case 0x15db: // (blue)
5594 element = EL_CHAR_MINUS;
5597 case 0x15dc: // (blue)
5598 element = EL_CHAR_APOSTROPHE;
5601 case 0x15dd: // (blue)
5602 element = EL_CHAR_PARENLEFT;
5605 case 0x15de: // (blue)
5606 element = EL_CHAR_PARENRIGHT;
5609 case 0x15df: // (green)
5610 element = EL_CHAR_A;
5613 case 0x15e0: // (green)
5614 element = EL_CHAR_B;
5617 case 0x15e1: // (green)
5618 element = EL_CHAR_C;
5621 case 0x15e2: // (green)
5622 element = EL_CHAR_D;
5625 case 0x15e3: // (green)
5626 element = EL_CHAR_E;
5629 case 0x15e4: // (green)
5630 element = EL_CHAR_F;
5633 case 0x15e5: // (green)
5634 element = EL_CHAR_G;
5637 case 0x15e6: // (green)
5638 element = EL_CHAR_H;
5641 case 0x15e7: // (green)
5642 element = EL_CHAR_I;
5645 case 0x15e8: // (green)
5646 element = EL_CHAR_J;
5649 case 0x15e9: // (green)
5650 element = EL_CHAR_K;
5653 case 0x15ea: // (green)
5654 element = EL_CHAR_L;
5657 case 0x15eb: // (green)
5658 element = EL_CHAR_M;
5661 case 0x15ec: // (green)
5662 element = EL_CHAR_N;
5665 case 0x15ed: // (green)
5666 element = EL_CHAR_O;
5669 case 0x15ee: // (green)
5670 element = EL_CHAR_P;
5673 case 0x15ef: // (green)
5674 element = EL_CHAR_Q;
5677 case 0x15f0: // (green)
5678 element = EL_CHAR_R;
5681 case 0x15f1: // (green)
5682 element = EL_CHAR_S;
5685 case 0x15f2: // (green)
5686 element = EL_CHAR_T;
5689 case 0x15f3: // (green)
5690 element = EL_CHAR_U;
5693 case 0x15f4: // (green)
5694 element = EL_CHAR_V;
5697 case 0x15f5: // (green)
5698 element = EL_CHAR_W;
5701 case 0x15f6: // (green)
5702 element = EL_CHAR_X;
5705 case 0x15f7: // (green)
5706 element = EL_CHAR_Y;
5709 case 0x15f8: // (green)
5710 element = EL_CHAR_Z;
5713 case 0x15f9: // (green)
5714 element = EL_CHAR_AUMLAUT;
5717 case 0x15fa: // (green)
5718 element = EL_CHAR_OUMLAUT;
5721 case 0x15fb: // (green)
5722 element = EL_CHAR_UUMLAUT;
5725 case 0x15fc: // (green)
5726 element = EL_CHAR_0;
5729 case 0x15fd: // (green)
5730 element = EL_CHAR_1;
5733 case 0x15fe: // (green)
5734 element = EL_CHAR_2;
5737 case 0x15ff: // (green)
5738 element = EL_CHAR_3;
5741 case 0x1600: // (green)
5742 element = EL_CHAR_4;
5745 case 0x1601: // (green)
5746 element = EL_CHAR_5;
5749 case 0x1602: // (green)
5750 element = EL_CHAR_6;
5753 case 0x1603: // (green)
5754 element = EL_CHAR_7;
5757 case 0x1604: // (green)
5758 element = EL_CHAR_8;
5761 case 0x1605: // (green)
5762 element = EL_CHAR_9;
5765 case 0x1606: // (green)
5766 element = EL_CHAR_PERIOD;
5769 case 0x1607: // (green)
5770 element = EL_CHAR_EXCLAM;
5773 case 0x1608: // (green)
5774 element = EL_CHAR_COLON;
5777 case 0x1609: // (green)
5778 element = EL_CHAR_LESS;
5781 case 0x160a: // (green)
5782 element = EL_CHAR_GREATER;
5785 case 0x160b: // (green)
5786 element = EL_CHAR_QUESTION;
5789 case 0x160c: // (green)
5790 element = EL_CHAR_COPYRIGHT;
5793 case 0x160d: // (green)
5794 element = EL_CHAR_UP;
5797 case 0x160e: // (green)
5798 element = EL_CHAR_DOWN;
5801 case 0x160f: // (green)
5802 element = EL_CHAR_BUTTON;
5805 case 0x1610: // (green)
5806 element = EL_CHAR_PLUS;
5809 case 0x1611: // (green)
5810 element = EL_CHAR_MINUS;
5813 case 0x1612: // (green)
5814 element = EL_CHAR_APOSTROPHE;
5817 case 0x1613: // (green)
5818 element = EL_CHAR_PARENLEFT;
5821 case 0x1614: // (green)
5822 element = EL_CHAR_PARENRIGHT;
5825 case 0x1615: // (blue steel)
5826 element = EL_STEEL_CHAR_A;
5829 case 0x1616: // (blue steel)
5830 element = EL_STEEL_CHAR_B;
5833 case 0x1617: // (blue steel)
5834 element = EL_STEEL_CHAR_C;
5837 case 0x1618: // (blue steel)
5838 element = EL_STEEL_CHAR_D;
5841 case 0x1619: // (blue steel)
5842 element = EL_STEEL_CHAR_E;
5845 case 0x161a: // (blue steel)
5846 element = EL_STEEL_CHAR_F;
5849 case 0x161b: // (blue steel)
5850 element = EL_STEEL_CHAR_G;
5853 case 0x161c: // (blue steel)
5854 element = EL_STEEL_CHAR_H;
5857 case 0x161d: // (blue steel)
5858 element = EL_STEEL_CHAR_I;
5861 case 0x161e: // (blue steel)
5862 element = EL_STEEL_CHAR_J;
5865 case 0x161f: // (blue steel)
5866 element = EL_STEEL_CHAR_K;
5869 case 0x1620: // (blue steel)
5870 element = EL_STEEL_CHAR_L;
5873 case 0x1621: // (blue steel)
5874 element = EL_STEEL_CHAR_M;
5877 case 0x1622: // (blue steel)
5878 element = EL_STEEL_CHAR_N;
5881 case 0x1623: // (blue steel)
5882 element = EL_STEEL_CHAR_O;
5885 case 0x1624: // (blue steel)
5886 element = EL_STEEL_CHAR_P;
5889 case 0x1625: // (blue steel)
5890 element = EL_STEEL_CHAR_Q;
5893 case 0x1626: // (blue steel)
5894 element = EL_STEEL_CHAR_R;
5897 case 0x1627: // (blue steel)
5898 element = EL_STEEL_CHAR_S;
5901 case 0x1628: // (blue steel)
5902 element = EL_STEEL_CHAR_T;
5905 case 0x1629: // (blue steel)
5906 element = EL_STEEL_CHAR_U;
5909 case 0x162a: // (blue steel)
5910 element = EL_STEEL_CHAR_V;
5913 case 0x162b: // (blue steel)
5914 element = EL_STEEL_CHAR_W;
5917 case 0x162c: // (blue steel)
5918 element = EL_STEEL_CHAR_X;
5921 case 0x162d: // (blue steel)
5922 element = EL_STEEL_CHAR_Y;
5925 case 0x162e: // (blue steel)
5926 element = EL_STEEL_CHAR_Z;
5929 case 0x162f: // (blue steel)
5930 element = EL_STEEL_CHAR_AUMLAUT;
5933 case 0x1630: // (blue steel)
5934 element = EL_STEEL_CHAR_OUMLAUT;
5937 case 0x1631: // (blue steel)
5938 element = EL_STEEL_CHAR_UUMLAUT;
5941 case 0x1632: // (blue steel)
5942 element = EL_STEEL_CHAR_0;
5945 case 0x1633: // (blue steel)
5946 element = EL_STEEL_CHAR_1;
5949 case 0x1634: // (blue steel)
5950 element = EL_STEEL_CHAR_2;
5953 case 0x1635: // (blue steel)
5954 element = EL_STEEL_CHAR_3;
5957 case 0x1636: // (blue steel)
5958 element = EL_STEEL_CHAR_4;
5961 case 0x1637: // (blue steel)
5962 element = EL_STEEL_CHAR_5;
5965 case 0x1638: // (blue steel)
5966 element = EL_STEEL_CHAR_6;
5969 case 0x1639: // (blue steel)
5970 element = EL_STEEL_CHAR_7;
5973 case 0x163a: // (blue steel)
5974 element = EL_STEEL_CHAR_8;
5977 case 0x163b: // (blue steel)
5978 element = EL_STEEL_CHAR_9;
5981 case 0x163c: // (blue steel)
5982 element = EL_STEEL_CHAR_PERIOD;
5985 case 0x163d: // (blue steel)
5986 element = EL_STEEL_CHAR_EXCLAM;
5989 case 0x163e: // (blue steel)
5990 element = EL_STEEL_CHAR_COLON;
5993 case 0x163f: // (blue steel)
5994 element = EL_STEEL_CHAR_LESS;
5997 case 0x1640: // (blue steel)
5998 element = EL_STEEL_CHAR_GREATER;
6001 case 0x1641: // (blue steel)
6002 element = EL_STEEL_CHAR_QUESTION;
6005 case 0x1642: // (blue steel)
6006 element = EL_STEEL_CHAR_COPYRIGHT;
6009 case 0x1643: // (blue steel)
6010 element = EL_STEEL_CHAR_UP;
6013 case 0x1644: // (blue steel)
6014 element = EL_STEEL_CHAR_DOWN;
6017 case 0x1645: // (blue steel)
6018 element = EL_STEEL_CHAR_BUTTON;
6021 case 0x1646: // (blue steel)
6022 element = EL_STEEL_CHAR_PLUS;
6025 case 0x1647: // (blue steel)
6026 element = EL_STEEL_CHAR_MINUS;
6029 case 0x1648: // (blue steel)
6030 element = EL_STEEL_CHAR_APOSTROPHE;
6033 case 0x1649: // (blue steel)
6034 element = EL_STEEL_CHAR_PARENLEFT;
6037 case 0x164a: // (blue steel)
6038 element = EL_STEEL_CHAR_PARENRIGHT;
6041 case 0x164b: // (green steel)
6042 element = EL_STEEL_CHAR_A;
6045 case 0x164c: // (green steel)
6046 element = EL_STEEL_CHAR_B;
6049 case 0x164d: // (green steel)
6050 element = EL_STEEL_CHAR_C;
6053 case 0x164e: // (green steel)
6054 element = EL_STEEL_CHAR_D;
6057 case 0x164f: // (green steel)
6058 element = EL_STEEL_CHAR_E;
6061 case 0x1650: // (green steel)
6062 element = EL_STEEL_CHAR_F;
6065 case 0x1651: // (green steel)
6066 element = EL_STEEL_CHAR_G;
6069 case 0x1652: // (green steel)
6070 element = EL_STEEL_CHAR_H;
6073 case 0x1653: // (green steel)
6074 element = EL_STEEL_CHAR_I;
6077 case 0x1654: // (green steel)
6078 element = EL_STEEL_CHAR_J;
6081 case 0x1655: // (green steel)
6082 element = EL_STEEL_CHAR_K;
6085 case 0x1656: // (green steel)
6086 element = EL_STEEL_CHAR_L;
6089 case 0x1657: // (green steel)
6090 element = EL_STEEL_CHAR_M;
6093 case 0x1658: // (green steel)
6094 element = EL_STEEL_CHAR_N;
6097 case 0x1659: // (green steel)
6098 element = EL_STEEL_CHAR_O;
6101 case 0x165a: // (green steel)
6102 element = EL_STEEL_CHAR_P;
6105 case 0x165b: // (green steel)
6106 element = EL_STEEL_CHAR_Q;
6109 case 0x165c: // (green steel)
6110 element = EL_STEEL_CHAR_R;
6113 case 0x165d: // (green steel)
6114 element = EL_STEEL_CHAR_S;
6117 case 0x165e: // (green steel)
6118 element = EL_STEEL_CHAR_T;
6121 case 0x165f: // (green steel)
6122 element = EL_STEEL_CHAR_U;
6125 case 0x1660: // (green steel)
6126 element = EL_STEEL_CHAR_V;
6129 case 0x1661: // (green steel)
6130 element = EL_STEEL_CHAR_W;
6133 case 0x1662: // (green steel)
6134 element = EL_STEEL_CHAR_X;
6137 case 0x1663: // (green steel)
6138 element = EL_STEEL_CHAR_Y;
6141 case 0x1664: // (green steel)
6142 element = EL_STEEL_CHAR_Z;
6145 case 0x1665: // (green steel)
6146 element = EL_STEEL_CHAR_AUMLAUT;
6149 case 0x1666: // (green steel)
6150 element = EL_STEEL_CHAR_OUMLAUT;
6153 case 0x1667: // (green steel)
6154 element = EL_STEEL_CHAR_UUMLAUT;
6157 case 0x1668: // (green steel)
6158 element = EL_STEEL_CHAR_0;
6161 case 0x1669: // (green steel)
6162 element = EL_STEEL_CHAR_1;
6165 case 0x166a: // (green steel)
6166 element = EL_STEEL_CHAR_2;
6169 case 0x166b: // (green steel)
6170 element = EL_STEEL_CHAR_3;
6173 case 0x166c: // (green steel)
6174 element = EL_STEEL_CHAR_4;
6177 case 0x166d: // (green steel)
6178 element = EL_STEEL_CHAR_5;
6181 case 0x166e: // (green steel)
6182 element = EL_STEEL_CHAR_6;
6185 case 0x166f: // (green steel)
6186 element = EL_STEEL_CHAR_7;
6189 case 0x1670: // (green steel)
6190 element = EL_STEEL_CHAR_8;
6193 case 0x1671: // (green steel)
6194 element = EL_STEEL_CHAR_9;
6197 case 0x1672: // (green steel)
6198 element = EL_STEEL_CHAR_PERIOD;
6201 case 0x1673: // (green steel)
6202 element = EL_STEEL_CHAR_EXCLAM;
6205 case 0x1674: // (green steel)
6206 element = EL_STEEL_CHAR_COLON;
6209 case 0x1675: // (green steel)
6210 element = EL_STEEL_CHAR_LESS;
6213 case 0x1676: // (green steel)
6214 element = EL_STEEL_CHAR_GREATER;
6217 case 0x1677: // (green steel)
6218 element = EL_STEEL_CHAR_QUESTION;
6221 case 0x1678: // (green steel)
6222 element = EL_STEEL_CHAR_COPYRIGHT;
6225 case 0x1679: // (green steel)
6226 element = EL_STEEL_CHAR_UP;
6229 case 0x167a: // (green steel)
6230 element = EL_STEEL_CHAR_DOWN;
6233 case 0x167b: // (green steel)
6234 element = EL_STEEL_CHAR_BUTTON;
6237 case 0x167c: // (green steel)
6238 element = EL_STEEL_CHAR_PLUS;
6241 case 0x167d: // (green steel)
6242 element = EL_STEEL_CHAR_MINUS;
6245 case 0x167e: // (green steel)
6246 element = EL_STEEL_CHAR_APOSTROPHE;
6249 case 0x167f: // (green steel)
6250 element = EL_STEEL_CHAR_PARENLEFT;
6253 case 0x1680: // (green steel)
6254 element = EL_STEEL_CHAR_PARENRIGHT;
6257 case 0x1681: // gate (red)
6258 element = EL_EM_GATE_1;
6261 case 0x1682: // secret gate (red)
6262 element = EL_EM_GATE_1_GRAY;
6265 case 0x1683: // gate (yellow)
6266 element = EL_EM_GATE_2;
6269 case 0x1684: // secret gate (yellow)
6270 element = EL_EM_GATE_2_GRAY;
6273 case 0x1685: // gate (blue)
6274 element = EL_EM_GATE_4;
6277 case 0x1686: // secret gate (blue)
6278 element = EL_EM_GATE_4_GRAY;
6281 case 0x1687: // gate (green)
6282 element = EL_EM_GATE_3;
6285 case 0x1688: // secret gate (green)
6286 element = EL_EM_GATE_3_GRAY;
6289 case 0x1689: // gate (white)
6290 element = EL_DC_GATE_WHITE;
6293 case 0x168a: // secret gate (white)
6294 element = EL_DC_GATE_WHITE_GRAY;
6297 case 0x168b: // secret gate (no key)
6298 element = EL_DC_GATE_FAKE_GRAY;
6302 element = EL_ROBOT_WHEEL;
6306 element = EL_DC_TIMEGATE_SWITCH;
6310 element = EL_ACID_POOL_BOTTOM;
6314 element = EL_ACID_POOL_TOPLEFT;
6318 element = EL_ACID_POOL_TOPRIGHT;
6322 element = EL_ACID_POOL_BOTTOMLEFT;
6326 element = EL_ACID_POOL_BOTTOMRIGHT;
6330 element = EL_STEELWALL;
6334 element = EL_STEELWALL_SLIPPERY;
6337 case 0x1695: // steel wall (not round)
6338 element = EL_STEELWALL;
6341 case 0x1696: // steel wall (left)
6342 element = EL_DC_STEELWALL_1_LEFT;
6345 case 0x1697: // steel wall (bottom)
6346 element = EL_DC_STEELWALL_1_BOTTOM;
6349 case 0x1698: // steel wall (right)
6350 element = EL_DC_STEELWALL_1_RIGHT;
6353 case 0x1699: // steel wall (top)
6354 element = EL_DC_STEELWALL_1_TOP;
6357 case 0x169a: // steel wall (left/bottom)
6358 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6361 case 0x169b: // steel wall (right/bottom)
6362 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6365 case 0x169c: // steel wall (right/top)
6366 element = EL_DC_STEELWALL_1_TOPRIGHT;
6369 case 0x169d: // steel wall (left/top)
6370 element = EL_DC_STEELWALL_1_TOPLEFT;
6373 case 0x169e: // steel wall (right/bottom small)
6374 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6377 case 0x169f: // steel wall (left/bottom small)
6378 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6381 case 0x16a0: // steel wall (right/top small)
6382 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6385 case 0x16a1: // steel wall (left/top small)
6386 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6389 case 0x16a2: // steel wall (left/right)
6390 element = EL_DC_STEELWALL_1_VERTICAL;
6393 case 0x16a3: // steel wall (top/bottom)
6394 element = EL_DC_STEELWALL_1_HORIZONTAL;
6397 case 0x16a4: // steel wall 2 (left end)
6398 element = EL_DC_STEELWALL_2_LEFT;
6401 case 0x16a5: // steel wall 2 (right end)
6402 element = EL_DC_STEELWALL_2_RIGHT;
6405 case 0x16a6: // steel wall 2 (top end)
6406 element = EL_DC_STEELWALL_2_TOP;
6409 case 0x16a7: // steel wall 2 (bottom end)
6410 element = EL_DC_STEELWALL_2_BOTTOM;
6413 case 0x16a8: // steel wall 2 (left/right)
6414 element = EL_DC_STEELWALL_2_HORIZONTAL;
6417 case 0x16a9: // steel wall 2 (up/down)
6418 element = EL_DC_STEELWALL_2_VERTICAL;
6421 case 0x16aa: // steel wall 2 (mid)
6422 element = EL_DC_STEELWALL_2_MIDDLE;
6426 element = EL_SIGN_EXCLAMATION;
6430 element = EL_SIGN_RADIOACTIVITY;
6434 element = EL_SIGN_STOP;
6438 element = EL_SIGN_WHEELCHAIR;
6442 element = EL_SIGN_PARKING;
6446 element = EL_SIGN_NO_ENTRY;
6450 element = EL_SIGN_HEART;
6454 element = EL_SIGN_GIVE_WAY;
6458 element = EL_SIGN_ENTRY_FORBIDDEN;
6462 element = EL_SIGN_EMERGENCY_EXIT;
6466 element = EL_SIGN_YIN_YANG;
6470 element = EL_WALL_EMERALD;
6474 element = EL_WALL_DIAMOND;
6478 element = EL_WALL_PEARL;
6482 element = EL_WALL_CRYSTAL;
6486 element = EL_INVISIBLE_WALL;
6490 element = EL_INVISIBLE_STEELWALL;
6494 // EL_INVISIBLE_SAND
6497 element = EL_LIGHT_SWITCH;
6501 element = EL_ENVELOPE_1;
6505 if (element >= 0x0117 && element <= 0x036e) // (?)
6506 element = EL_DIAMOND;
6507 else if (element >= 0x042d && element <= 0x0684) // (?)
6508 element = EL_EMERALD;
6509 else if (element >= 0x157c && element <= 0x158b)
6511 else if (element >= 0x1590 && element <= 0x159f)
6512 element = EL_DC_LANDMINE;
6513 else if (element >= 0x16bc && element <= 0x16cb)
6514 element = EL_INVISIBLE_SAND;
6517 Warn("unknown Diamond Caves element 0x%04x", element);
6519 element = EL_UNKNOWN;
6524 return getMappedElement(element);
6527 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6529 byte header[DC_LEVEL_HEADER_SIZE];
6531 int envelope_header_pos = 62;
6532 int envelope_content_pos = 94;
6533 int level_name_pos = 251;
6534 int level_author_pos = 292;
6535 int envelope_header_len;
6536 int envelope_content_len;
6538 int level_author_len;
6540 int num_yamyam_contents;
6543 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6545 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6547 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6549 header[i * 2 + 0] = header_word >> 8;
6550 header[i * 2 + 1] = header_word & 0xff;
6553 // read some values from level header to check level decoding integrity
6554 fieldx = header[6] | (header[7] << 8);
6555 fieldy = header[8] | (header[9] << 8);
6556 num_yamyam_contents = header[60] | (header[61] << 8);
6558 // do some simple sanity checks to ensure that level was correctly decoded
6559 if (fieldx < 1 || fieldx > 256 ||
6560 fieldy < 1 || fieldy > 256 ||
6561 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6563 level->no_valid_file = TRUE;
6565 Warn("cannot decode level from stream -- using empty level");
6570 // maximum envelope header size is 31 bytes
6571 envelope_header_len = header[envelope_header_pos];
6572 // maximum envelope content size is 110 (156?) bytes
6573 envelope_content_len = header[envelope_content_pos];
6575 // maximum level title size is 40 bytes
6576 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6577 // maximum level author size is 30 (51?) bytes
6578 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6582 for (i = 0; i < envelope_header_len; i++)
6583 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6584 level->envelope[0].text[envelope_size++] =
6585 header[envelope_header_pos + 1 + i];
6587 if (envelope_header_len > 0 && envelope_content_len > 0)
6589 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6590 level->envelope[0].text[envelope_size++] = '\n';
6591 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6592 level->envelope[0].text[envelope_size++] = '\n';
6595 for (i = 0; i < envelope_content_len; i++)
6596 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6597 level->envelope[0].text[envelope_size++] =
6598 header[envelope_content_pos + 1 + i];
6600 level->envelope[0].text[envelope_size] = '\0';
6602 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6603 level->envelope[0].ysize = 10;
6604 level->envelope[0].autowrap = TRUE;
6605 level->envelope[0].centered = TRUE;
6607 for (i = 0; i < level_name_len; i++)
6608 level->name[i] = header[level_name_pos + 1 + i];
6609 level->name[level_name_len] = '\0';
6611 for (i = 0; i < level_author_len; i++)
6612 level->author[i] = header[level_author_pos + 1 + i];
6613 level->author[level_author_len] = '\0';
6615 num_yamyam_contents = header[60] | (header[61] << 8);
6616 level->num_yamyam_contents =
6617 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6619 for (i = 0; i < num_yamyam_contents; i++)
6621 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6623 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6624 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6626 if (i < MAX_ELEMENT_CONTENTS)
6627 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6631 fieldx = header[6] | (header[7] << 8);
6632 fieldy = header[8] | (header[9] << 8);
6633 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6634 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6636 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6638 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6639 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6641 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6642 level->field[x][y] = getMappedElement_DC(element_dc);
6645 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6646 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6647 level->field[x][y] = EL_PLAYER_1;
6649 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6650 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6651 level->field[x][y] = EL_PLAYER_2;
6653 level->gems_needed = header[18] | (header[19] << 8);
6655 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6656 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6657 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6658 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6659 level->score[SC_NUT] = header[28] | (header[29] << 8);
6660 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6661 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6662 level->score[SC_BUG] = header[34] | (header[35] << 8);
6663 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6664 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6665 level->score[SC_KEY] = header[40] | (header[41] << 8);
6666 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6668 level->time = header[44] | (header[45] << 8);
6670 level->amoeba_speed = header[46] | (header[47] << 8);
6671 level->time_light = header[48] | (header[49] << 8);
6672 level->time_timegate = header[50] | (header[51] << 8);
6673 level->time_wheel = header[52] | (header[53] << 8);
6674 level->time_magic_wall = header[54] | (header[55] << 8);
6675 level->extra_time = header[56] | (header[57] << 8);
6676 level->shield_normal_time = header[58] | (header[59] << 8);
6678 // shield and extra time elements do not have a score
6679 level->score[SC_SHIELD] = 0;
6680 level->extra_time_score = 0;
6682 // set time for normal and deadly shields to the same value
6683 level->shield_deadly_time = level->shield_normal_time;
6685 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6686 // can slip down from flat walls, like normal walls and steel walls
6687 level->em_slippery_gems = TRUE;
6689 // time score is counted for each 10 seconds left in Diamond Caves levels
6690 level->time_score_base = 10;
6693 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6694 struct LevelFileInfo *level_file_info,
6695 boolean level_info_only)
6697 char *filename = level_file_info->filename;
6699 int num_magic_bytes = 8;
6700 char magic_bytes[num_magic_bytes + 1];
6701 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6703 if (!(file = openFile(filename, MODE_READ)))
6705 level->no_valid_file = TRUE;
6707 if (!level_info_only)
6708 Warn("cannot read level '%s' -- using empty level", filename);
6713 // fseek(file, 0x0000, SEEK_SET);
6715 if (level_file_info->packed)
6717 // read "magic bytes" from start of file
6718 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6719 magic_bytes[0] = '\0';
6721 // check "magic bytes" for correct file format
6722 if (!strPrefix(magic_bytes, "DC2"))
6724 level->no_valid_file = TRUE;
6726 Warn("unknown DC level file '%s' -- using empty level", filename);
6731 if (strPrefix(magic_bytes, "DC2Win95") ||
6732 strPrefix(magic_bytes, "DC2Win98"))
6734 int position_first_level = 0x00fa;
6735 int extra_bytes = 4;
6738 // advance file stream to first level inside the level package
6739 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6741 // each block of level data is followed by block of non-level data
6742 num_levels_to_skip *= 2;
6744 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6745 while (num_levels_to_skip >= 0)
6747 // advance file stream to next level inside the level package
6748 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6750 level->no_valid_file = TRUE;
6752 Warn("cannot fseek in file '%s' -- using empty level", filename);
6757 // skip apparently unused extra bytes following each level
6758 ReadUnusedBytesFromFile(file, extra_bytes);
6760 // read size of next level in level package
6761 skip_bytes = getFile32BitLE(file);
6763 num_levels_to_skip--;
6768 level->no_valid_file = TRUE;
6770 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6776 LoadLevelFromFileStream_DC(file, level);
6782 // ----------------------------------------------------------------------------
6783 // functions for loading SB level
6784 // ----------------------------------------------------------------------------
6786 int getMappedElement_SB(int element_ascii, boolean use_ces)
6794 sb_element_mapping[] =
6796 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6797 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6798 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6799 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6800 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6801 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6802 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6803 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6810 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6811 if (element_ascii == sb_element_mapping[i].ascii)
6812 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6814 return EL_UNDEFINED;
6817 static void SetLevelSettings_SB(struct LevelInfo *level)
6821 level->use_step_counter = TRUE;
6824 level->score[SC_TIME_BONUS] = 0;
6825 level->time_score_base = 1;
6826 level->rate_time_over_score = TRUE;
6829 level->auto_exit_sokoban = TRUE;
6832 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6833 struct LevelFileInfo *level_file_info,
6834 boolean level_info_only)
6836 char *filename = level_file_info->filename;
6837 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6838 char last_comment[MAX_LINE_LEN];
6839 char level_name[MAX_LINE_LEN];
6842 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6843 boolean read_continued_line = FALSE;
6844 boolean reading_playfield = FALSE;
6845 boolean got_valid_playfield_line = FALSE;
6846 boolean invalid_playfield_char = FALSE;
6847 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6848 int file_level_nr = 0;
6849 int x = 0, y = 0; // initialized to make compilers happy
6851 last_comment[0] = '\0';
6852 level_name[0] = '\0';
6854 if (!(file = openFile(filename, MODE_READ)))
6856 level->no_valid_file = TRUE;
6858 if (!level_info_only)
6859 Warn("cannot read level '%s' -- using empty level", filename);
6864 while (!checkEndOfFile(file))
6866 // level successfully read, but next level may follow here
6867 if (!got_valid_playfield_line && reading_playfield)
6869 // read playfield from single level file -- skip remaining file
6870 if (!level_file_info->packed)
6873 if (file_level_nr >= num_levels_to_skip)
6878 last_comment[0] = '\0';
6879 level_name[0] = '\0';
6881 reading_playfield = FALSE;
6884 got_valid_playfield_line = FALSE;
6886 // read next line of input file
6887 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6890 // cut trailing line break (this can be newline and/or carriage return)
6891 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6892 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6895 // copy raw input line for later use (mainly debugging output)
6896 strcpy(line_raw, line);
6898 if (read_continued_line)
6900 // append new line to existing line, if there is enough space
6901 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6902 strcat(previous_line, line_ptr);
6904 strcpy(line, previous_line); // copy storage buffer to line
6906 read_continued_line = FALSE;
6909 // if the last character is '\', continue at next line
6910 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6912 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6913 strcpy(previous_line, line); // copy line to storage buffer
6915 read_continued_line = TRUE;
6921 if (line[0] == '\0')
6924 // extract comment text from comment line
6927 for (line_ptr = line; *line_ptr; line_ptr++)
6928 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6931 strcpy(last_comment, line_ptr);
6936 // extract level title text from line containing level title
6937 if (line[0] == '\'')
6939 strcpy(level_name, &line[1]);
6941 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6942 level_name[strlen(level_name) - 1] = '\0';
6947 // skip lines containing only spaces (or empty lines)
6948 for (line_ptr = line; *line_ptr; line_ptr++)
6949 if (*line_ptr != ' ')
6951 if (*line_ptr == '\0')
6954 // at this point, we have found a line containing part of a playfield
6956 got_valid_playfield_line = TRUE;
6958 if (!reading_playfield)
6960 reading_playfield = TRUE;
6961 invalid_playfield_char = FALSE;
6963 for (x = 0; x < MAX_LEV_FIELDX; x++)
6964 for (y = 0; y < MAX_LEV_FIELDY; y++)
6965 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6970 // start with topmost tile row
6974 // skip playfield line if larger row than allowed
6975 if (y >= MAX_LEV_FIELDY)
6978 // start with leftmost tile column
6981 // read playfield elements from line
6982 for (line_ptr = line; *line_ptr; line_ptr++)
6984 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6986 // stop parsing playfield line if larger column than allowed
6987 if (x >= MAX_LEV_FIELDX)
6990 if (mapped_sb_element == EL_UNDEFINED)
6992 invalid_playfield_char = TRUE;
6997 level->field[x][y] = mapped_sb_element;
6999 // continue with next tile column
7002 level->fieldx = MAX(x, level->fieldx);
7005 if (invalid_playfield_char)
7007 // if first playfield line, treat invalid lines as comment lines
7009 reading_playfield = FALSE;
7014 // continue with next tile row
7022 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7023 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7025 if (!reading_playfield)
7027 level->no_valid_file = TRUE;
7029 Warn("cannot read level '%s' -- using empty level", filename);
7034 if (*level_name != '\0')
7036 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7037 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7039 else if (*last_comment != '\0')
7041 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7042 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7046 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7049 // set all empty fields beyond the border walls to invisible steel wall
7050 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7052 if ((x == 0 || x == level->fieldx - 1 ||
7053 y == 0 || y == level->fieldy - 1) &&
7054 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7055 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7056 level->field, level->fieldx, level->fieldy);
7059 // set special level settings for Sokoban levels
7060 SetLevelSettings_SB(level);
7062 if (load_xsb_to_ces)
7064 // special global settings can now be set in level template
7065 level->use_custom_template = TRUE;
7070 // -------------------------------------------------------------------------
7071 // functions for handling native levels
7072 // -------------------------------------------------------------------------
7074 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7075 struct LevelFileInfo *level_file_info,
7076 boolean level_info_only)
7080 // determine position of requested level inside level package
7081 if (level_file_info->packed)
7082 pos = level_file_info->nr - leveldir_current->first_level;
7084 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7085 level->no_valid_file = TRUE;
7088 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7089 struct LevelFileInfo *level_file_info,
7090 boolean level_info_only)
7092 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7093 level->no_valid_file = TRUE;
7096 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7097 struct LevelFileInfo *level_file_info,
7098 boolean level_info_only)
7102 // determine position of requested level inside level package
7103 if (level_file_info->packed)
7104 pos = level_file_info->nr - leveldir_current->first_level;
7106 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7107 level->no_valid_file = TRUE;
7110 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7111 struct LevelFileInfo *level_file_info,
7112 boolean level_info_only)
7114 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7115 level->no_valid_file = TRUE;
7118 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7120 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7121 CopyNativeLevel_RND_to_BD(level);
7122 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7123 CopyNativeLevel_RND_to_EM(level);
7124 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7125 CopyNativeLevel_RND_to_SP(level);
7126 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7127 CopyNativeLevel_RND_to_MM(level);
7130 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7132 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7133 CopyNativeLevel_BD_to_RND(level);
7134 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7135 CopyNativeLevel_EM_to_RND(level);
7136 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7137 CopyNativeLevel_SP_to_RND(level);
7138 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7139 CopyNativeLevel_MM_to_RND(level);
7142 void SaveNativeLevel(struct LevelInfo *level)
7144 // saving native level files only supported for some game engines
7145 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7146 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7149 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7150 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7151 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7152 char *filename = getLevelFilenameFromBasename(basename);
7154 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7157 boolean success = FALSE;
7159 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7161 CopyNativeLevel_RND_to_BD(level);
7162 // CopyNativeTape_RND_to_BD(level);
7164 success = SaveNativeLevel_BD(filename);
7166 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7168 CopyNativeLevel_RND_to_SP(level);
7169 CopyNativeTape_RND_to_SP(level);
7171 success = SaveNativeLevel_SP(filename);
7175 Request("Native level file saved!", REQ_CONFIRM);
7177 Request("Failed to save native level file!", REQ_CONFIRM);
7181 // ----------------------------------------------------------------------------
7182 // functions for loading generic level
7183 // ----------------------------------------------------------------------------
7185 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7186 struct LevelFileInfo *level_file_info,
7187 boolean level_info_only)
7189 // always start with reliable default values
7190 setLevelInfoToDefaults(level, level_info_only, TRUE);
7192 switch (level_file_info->type)
7194 case LEVEL_FILE_TYPE_RND:
7195 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7198 case LEVEL_FILE_TYPE_BD:
7199 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7200 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7203 case LEVEL_FILE_TYPE_EM:
7204 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7205 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7208 case LEVEL_FILE_TYPE_SP:
7209 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7210 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7213 case LEVEL_FILE_TYPE_MM:
7214 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7215 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7218 case LEVEL_FILE_TYPE_DC:
7219 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7222 case LEVEL_FILE_TYPE_SB:
7223 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7227 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7231 // if level file is invalid, restore level structure to default values
7232 if (level->no_valid_file)
7233 setLevelInfoToDefaults(level, level_info_only, FALSE);
7235 if (check_special_flags("use_native_bd_game_engine"))
7236 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7238 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7239 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7241 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7242 CopyNativeLevel_Native_to_RND(level);
7245 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7247 static struct LevelFileInfo level_file_info;
7249 // always start with reliable default values
7250 setFileInfoToDefaults(&level_file_info);
7252 level_file_info.nr = 0; // unknown level number
7253 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7255 setString(&level_file_info.filename, filename);
7257 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7260 static void LoadLevel_InitVersion(struct LevelInfo *level)
7264 if (leveldir_current == NULL) // only when dumping level
7267 // all engine modifications also valid for levels which use latest engine
7268 if (level->game_version < VERSION_IDENT(3,2,0,5))
7270 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7271 level->time_score_base = 10;
7274 if (leveldir_current->latest_engine)
7276 // ---------- use latest game engine --------------------------------------
7278 /* For all levels which are forced to use the latest game engine version
7279 (normally all but user contributed, private and undefined levels), set
7280 the game engine version to the actual version; this allows for actual
7281 corrections in the game engine to take effect for existing, converted
7282 levels (from "classic" or other existing games) to make the emulation
7283 of the corresponding game more accurate, while (hopefully) not breaking
7284 existing levels created from other players. */
7286 level->game_version = GAME_VERSION_ACTUAL;
7288 /* Set special EM style gems behaviour: EM style gems slip down from
7289 normal, steel and growing wall. As this is a more fundamental change,
7290 it seems better to set the default behaviour to "off" (as it is more
7291 natural) and make it configurable in the level editor (as a property
7292 of gem style elements). Already existing converted levels (neither
7293 private nor contributed levels) are changed to the new behaviour. */
7295 if (level->file_version < FILE_VERSION_2_0)
7296 level->em_slippery_gems = TRUE;
7301 // ---------- use game engine the level was created with --------------------
7303 /* For all levels which are not forced to use the latest game engine
7304 version (normally user contributed, private and undefined levels),
7305 use the version of the game engine the levels were created for.
7307 Since 2.0.1, the game engine version is now directly stored
7308 in the level file (chunk "VERS"), so there is no need anymore
7309 to set the game version from the file version (except for old,
7310 pre-2.0 levels, where the game version is still taken from the
7311 file format version used to store the level -- see above). */
7313 // player was faster than enemies in 1.0.0 and before
7314 if (level->file_version == FILE_VERSION_1_0)
7315 for (i = 0; i < MAX_PLAYERS; i++)
7316 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7318 // default behaviour for EM style gems was "slippery" only in 2.0.1
7319 if (level->game_version == VERSION_IDENT(2,0,1,0))
7320 level->em_slippery_gems = TRUE;
7322 // springs could be pushed over pits before (pre-release version) 2.2.0
7323 if (level->game_version < VERSION_IDENT(2,2,0,0))
7324 level->use_spring_bug = TRUE;
7326 if (level->game_version < VERSION_IDENT(3,2,0,5))
7328 // time orb caused limited time in endless time levels before 3.2.0-5
7329 level->use_time_orb_bug = TRUE;
7331 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7332 level->block_snap_field = FALSE;
7334 // extra time score was same value as time left score before 3.2.0-5
7335 level->extra_time_score = level->score[SC_TIME_BONUS];
7338 if (level->game_version < VERSION_IDENT(3,2,0,7))
7340 // default behaviour for snapping was "not continuous" before 3.2.0-7
7341 level->continuous_snapping = FALSE;
7344 // only few elements were able to actively move into acid before 3.1.0
7345 // trigger settings did not exist before 3.1.0; set to default "any"
7346 if (level->game_version < VERSION_IDENT(3,1,0,0))
7348 // correct "can move into acid" settings (all zero in old levels)
7350 level->can_move_into_acid_bits = 0; // nothing can move into acid
7351 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7353 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7354 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7355 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7356 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7358 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7359 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7361 // correct trigger settings (stored as zero == "none" in old levels)
7363 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7365 int element = EL_CUSTOM_START + i;
7366 struct ElementInfo *ei = &element_info[element];
7368 for (j = 0; j < ei->num_change_pages; j++)
7370 struct ElementChangeInfo *change = &ei->change_page[j];
7372 change->trigger_player = CH_PLAYER_ANY;
7373 change->trigger_page = CH_PAGE_ANY;
7378 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7380 int element = EL_CUSTOM_256;
7381 struct ElementInfo *ei = &element_info[element];
7382 struct ElementChangeInfo *change = &ei->change_page[0];
7384 /* This is needed to fix a problem that was caused by a bugfix in function
7385 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7386 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7387 not replace walkable elements, but instead just placed the player on it,
7388 without placing the Sokoban field under the player). Unfortunately, this
7389 breaks "Snake Bite" style levels when the snake is halfway through a door
7390 that just closes (the snake head is still alive and can be moved in this
7391 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7392 player (without Sokoban element) which then gets killed as designed). */
7394 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7395 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7396 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7397 change->target_element = EL_PLAYER_1;
7400 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7401 if (level->game_version < VERSION_IDENT(3,2,5,0))
7403 /* This is needed to fix a problem that was caused by a bugfix in function
7404 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7405 corrects the behaviour when a custom element changes to another custom
7406 element with a higher element number that has change actions defined.
7407 Normally, only one change per frame is allowed for custom elements.
7408 Therefore, it is checked if a custom element already changed in the
7409 current frame; if it did, subsequent changes are suppressed.
7410 Unfortunately, this is only checked for element changes, but not for
7411 change actions, which are still executed. As the function above loops
7412 through all custom elements from lower to higher, an element change
7413 resulting in a lower CE number won't be checked again, while a target
7414 element with a higher number will also be checked, and potential change
7415 actions will get executed for this CE, too (which is wrong), while
7416 further changes are ignored (which is correct). As this bugfix breaks
7417 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7418 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7419 behaviour for existing levels and tapes that make use of this bug */
7421 level->use_action_after_change_bug = TRUE;
7424 // not centering level after relocating player was default only in 3.2.3
7425 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7426 level->shifted_relocation = TRUE;
7428 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7429 if (level->game_version < VERSION_IDENT(3,2,6,0))
7430 level->em_explodes_by_fire = TRUE;
7432 // levels were solved by the first player entering an exit up to 4.1.0.0
7433 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7434 level->solved_by_one_player = TRUE;
7436 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7437 if (level->game_version < VERSION_IDENT(4,1,1,1))
7438 level->use_life_bugs = TRUE;
7440 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7441 if (level->game_version < VERSION_IDENT(4,1,1,1))
7442 level->sb_objects_needed = FALSE;
7444 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7445 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7446 level->finish_dig_collect = FALSE;
7448 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7449 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7450 level->keep_walkable_ce = TRUE;
7453 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7455 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7458 // check if this level is (not) a Sokoban level
7459 for (y = 0; y < level->fieldy; y++)
7460 for (x = 0; x < level->fieldx; x++)
7461 if (!IS_SB_ELEMENT(Tile[x][y]))
7462 is_sokoban_level = FALSE;
7464 if (is_sokoban_level)
7466 // set special level settings for Sokoban levels
7467 SetLevelSettings_SB(level);
7471 static void LoadLevel_InitSettings(struct LevelInfo *level)
7473 // adjust level settings for (non-native) Sokoban-style levels
7474 LoadLevel_InitSettings_SB(level);
7476 // rename levels with title "nameless level" or if renaming is forced
7477 if (leveldir_current->empty_level_name != NULL &&
7478 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7479 leveldir_current->force_level_name))
7480 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7481 leveldir_current->empty_level_name, level_nr);
7484 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7488 // map elements that have changed in newer versions
7489 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7490 level->game_version);
7491 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7492 for (x = 0; x < 3; x++)
7493 for (y = 0; y < 3; y++)
7494 level->yamyam_content[i].e[x][y] =
7495 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7496 level->game_version);
7500 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7504 // map custom element change events that have changed in newer versions
7505 // (these following values were accidentally changed in version 3.0.1)
7506 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7507 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7509 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7511 int element = EL_CUSTOM_START + i;
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_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7517 if (HAS_CHANGE_EVENT(element, j - 2))
7519 SET_CHANGE_EVENT(element, j - 2, FALSE);
7520 SET_CHANGE_EVENT(element, j, TRUE);
7524 // order of checking and copying events to be mapped is important
7525 // (do not change the start and end value -- they are constant)
7526 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7528 if (HAS_CHANGE_EVENT(element, j - 1))
7530 SET_CHANGE_EVENT(element, j - 1, FALSE);
7531 SET_CHANGE_EVENT(element, j, TRUE);
7537 // initialize "can_change" field for old levels with only one change page
7538 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7540 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7542 int element = EL_CUSTOM_START + i;
7544 if (CAN_CHANGE(element))
7545 element_info[element].change->can_change = TRUE;
7549 // correct custom element values (for old levels without these options)
7550 if (level->game_version < VERSION_IDENT(3,1,1,0))
7552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7554 int element = EL_CUSTOM_START + i;
7555 struct ElementInfo *ei = &element_info[element];
7557 if (ei->access_direction == MV_NO_DIRECTION)
7558 ei->access_direction = MV_ALL_DIRECTIONS;
7562 // correct custom element values (fix invalid values for all versions)
7565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7567 int element = EL_CUSTOM_START + i;
7568 struct ElementInfo *ei = &element_info[element];
7570 for (j = 0; j < ei->num_change_pages; j++)
7572 struct ElementChangeInfo *change = &ei->change_page[j];
7574 if (change->trigger_player == CH_PLAYER_NONE)
7575 change->trigger_player = CH_PLAYER_ANY;
7577 if (change->trigger_side == CH_SIDE_NONE)
7578 change->trigger_side = CH_SIDE_ANY;
7583 // initialize "can_explode" field for old levels which did not store this
7584 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7585 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7587 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7589 int element = EL_CUSTOM_START + i;
7591 if (EXPLODES_1X1_OLD(element))
7592 element_info[element].explosion_type = EXPLODES_1X1;
7594 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7595 EXPLODES_SMASHED(element) ||
7596 EXPLODES_IMPACT(element)));
7600 // correct previously hard-coded move delay values for maze runner style
7601 if (level->game_version < VERSION_IDENT(3,1,1,0))
7603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7605 int element = EL_CUSTOM_START + i;
7607 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7609 // previously hard-coded and therefore ignored
7610 element_info[element].move_delay_fixed = 9;
7611 element_info[element].move_delay_random = 0;
7616 // set some other uninitialized values of custom elements in older levels
7617 if (level->game_version < VERSION_IDENT(3,1,0,0))
7619 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7621 int element = EL_CUSTOM_START + i;
7623 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7625 element_info[element].explosion_delay = 17;
7626 element_info[element].ignition_delay = 8;
7630 // set mouse click change events to work for left/middle/right mouse button
7631 if (level->game_version < VERSION_IDENT(4,2,3,0))
7633 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7635 int element = EL_CUSTOM_START + i;
7636 struct ElementInfo *ei = &element_info[element];
7638 for (j = 0; j < ei->num_change_pages; j++)
7640 struct ElementChangeInfo *change = &ei->change_page[j];
7642 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7643 change->has_event[CE_PRESSED_BY_MOUSE] ||
7644 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7645 change->has_event[CE_MOUSE_PRESSED_ON_X])
7646 change->trigger_side = CH_SIDE_ANY;
7652 static void LoadLevel_InitElements(struct LevelInfo *level)
7654 LoadLevel_InitStandardElements(level);
7656 if (level->file_has_custom_elements)
7657 LoadLevel_InitCustomElements(level);
7659 // initialize element properties for level editor etc.
7660 InitElementPropertiesEngine(level->game_version);
7661 InitElementPropertiesGfxElement();
7664 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7668 // map elements that have changed in newer versions
7669 for (y = 0; y < level->fieldy; y++)
7670 for (x = 0; x < level->fieldx; x++)
7671 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7672 level->game_version);
7674 // clear unused playfield data (nicer if level gets resized in editor)
7675 for (x = 0; x < MAX_LEV_FIELDX; x++)
7676 for (y = 0; y < MAX_LEV_FIELDY; y++)
7677 if (x >= level->fieldx || y >= level->fieldy)
7678 level->field[x][y] = EL_EMPTY;
7680 // copy elements to runtime playfield array
7681 for (x = 0; x < MAX_LEV_FIELDX; x++)
7682 for (y = 0; y < MAX_LEV_FIELDY; y++)
7683 Tile[x][y] = level->field[x][y];
7685 // initialize level size variables for faster access
7686 lev_fieldx = level->fieldx;
7687 lev_fieldy = level->fieldy;
7689 // determine border element for this level
7690 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7691 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7696 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7698 struct LevelFileInfo *level_file_info = &level->file_info;
7700 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7701 CopyNativeLevel_RND_to_Native(level);
7704 static void LoadLevelTemplate_LoadAndInit(void)
7706 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7708 LoadLevel_InitVersion(&level_template);
7709 LoadLevel_InitElements(&level_template);
7710 LoadLevel_InitSettings(&level_template);
7712 ActivateLevelTemplate();
7715 void LoadLevelTemplate(int nr)
7717 if (!fileExists(getGlobalLevelTemplateFilename()))
7719 Warn("no level template found for this level");
7724 setLevelFileInfo(&level_template.file_info, nr);
7726 LoadLevelTemplate_LoadAndInit();
7729 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7731 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7733 LoadLevelTemplate_LoadAndInit();
7736 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7738 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7740 if (level.use_custom_template)
7742 if (network_level != NULL)
7743 LoadNetworkLevelTemplate(network_level);
7745 LoadLevelTemplate(-1);
7748 LoadLevel_InitVersion(&level);
7749 LoadLevel_InitElements(&level);
7750 LoadLevel_InitPlayfield(&level);
7751 LoadLevel_InitSettings(&level);
7753 LoadLevel_InitNativeEngines(&level);
7756 void LoadLevel(int nr)
7758 SetLevelSetInfo(leveldir_current->identifier, nr);
7760 setLevelFileInfo(&level.file_info, nr);
7762 LoadLevel_LoadAndInit(NULL);
7765 void LoadLevelInfoOnly(int nr)
7767 setLevelFileInfo(&level.file_info, nr);
7769 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7772 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7774 SetLevelSetInfo(network_level->leveldir_identifier,
7775 network_level->file_info.nr);
7777 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7779 LoadLevel_LoadAndInit(network_level);
7782 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7786 chunk_size += putFileVersion(file, level->file_version);
7787 chunk_size += putFileVersion(file, level->game_version);
7792 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7796 chunk_size += putFile16BitBE(file, level->creation_date.year);
7797 chunk_size += putFile8Bit(file, level->creation_date.month);
7798 chunk_size += putFile8Bit(file, level->creation_date.day);
7803 #if ENABLE_HISTORIC_CHUNKS
7804 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7808 putFile8Bit(file, level->fieldx);
7809 putFile8Bit(file, level->fieldy);
7811 putFile16BitBE(file, level->time);
7812 putFile16BitBE(file, level->gems_needed);
7814 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7815 putFile8Bit(file, level->name[i]);
7817 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7818 putFile8Bit(file, level->score[i]);
7820 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7821 for (y = 0; y < 3; y++)
7822 for (x = 0; x < 3; x++)
7823 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7824 level->yamyam_content[i].e[x][y]));
7825 putFile8Bit(file, level->amoeba_speed);
7826 putFile8Bit(file, level->time_magic_wall);
7827 putFile8Bit(file, level->time_wheel);
7828 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7829 level->amoeba_content));
7830 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7831 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7832 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7833 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7835 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7837 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7838 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7839 putFile32BitBE(file, level->can_move_into_acid_bits);
7840 putFile8Bit(file, level->dont_collide_with_bits);
7842 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7843 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7845 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7846 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7847 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7849 putFile8Bit(file, level->game_engine_type);
7851 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7855 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7860 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7861 chunk_size += putFile8Bit(file, level->name[i]);
7866 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7871 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7872 chunk_size += putFile8Bit(file, level->author[i]);
7877 #if ENABLE_HISTORIC_CHUNKS
7878 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7883 for (y = 0; y < level->fieldy; y++)
7884 for (x = 0; x < level->fieldx; x++)
7885 if (level->encoding_16bit_field)
7886 chunk_size += putFile16BitBE(file, level->field[x][y]);
7888 chunk_size += putFile8Bit(file, level->field[x][y]);
7894 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7899 for (y = 0; y < level->fieldy; y++)
7900 for (x = 0; x < level->fieldx; x++)
7901 chunk_size += putFile16BitBE(file, level->field[x][y]);
7906 #if ENABLE_HISTORIC_CHUNKS
7907 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7911 putFile8Bit(file, EL_YAMYAM);
7912 putFile8Bit(file, level->num_yamyam_contents);
7913 putFile8Bit(file, 0);
7914 putFile8Bit(file, 0);
7916 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7917 for (y = 0; y < 3; y++)
7918 for (x = 0; x < 3; x++)
7919 if (level->encoding_16bit_field)
7920 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7922 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7926 #if ENABLE_HISTORIC_CHUNKS
7927 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7930 int num_contents, content_xsize, content_ysize;
7931 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7933 if (element == EL_YAMYAM)
7935 num_contents = level->num_yamyam_contents;
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] = level->yamyam_content[i].e[x][y];
7944 else if (element == EL_BD_AMOEBA)
7950 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7951 for (y = 0; y < 3; y++)
7952 for (x = 0; x < 3; x++)
7953 content_array[i][x][y] = EL_EMPTY;
7954 content_array[0][0][0] = level->amoeba_content;
7958 // chunk header already written -- write empty chunk data
7959 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7961 Warn("cannot save content for element '%d'", element);
7966 putFile16BitBE(file, element);
7967 putFile8Bit(file, num_contents);
7968 putFile8Bit(file, content_xsize);
7969 putFile8Bit(file, content_ysize);
7971 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7973 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7974 for (y = 0; y < 3; y++)
7975 for (x = 0; x < 3; x++)
7976 putFile16BitBE(file, content_array[i][x][y]);
7980 #if ENABLE_HISTORIC_CHUNKS
7981 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7983 int envelope_nr = element - EL_ENVELOPE_1;
7984 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7988 chunk_size += putFile16BitBE(file, element);
7989 chunk_size += putFile16BitBE(file, envelope_len);
7990 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7991 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7993 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7994 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7996 for (i = 0; i < envelope_len; i++)
7997 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8003 #if ENABLE_HISTORIC_CHUNKS
8004 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8005 int num_changed_custom_elements)
8009 putFile16BitBE(file, num_changed_custom_elements);
8011 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8013 int element = EL_CUSTOM_START + i;
8015 struct ElementInfo *ei = &element_info[element];
8017 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8019 if (check < num_changed_custom_elements)
8021 putFile16BitBE(file, element);
8022 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8029 if (check != num_changed_custom_elements) // should not happen
8030 Warn("inconsistent number of custom element properties");
8034 #if ENABLE_HISTORIC_CHUNKS
8035 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8036 int num_changed_custom_elements)
8040 putFile16BitBE(file, num_changed_custom_elements);
8042 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8044 int element = EL_CUSTOM_START + i;
8046 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8048 if (check < num_changed_custom_elements)
8050 putFile16BitBE(file, element);
8051 putFile16BitBE(file, element_info[element].change->target_element);
8058 if (check != num_changed_custom_elements) // should not happen
8059 Warn("inconsistent number of custom target elements");
8063 #if ENABLE_HISTORIC_CHUNKS
8064 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8065 int num_changed_custom_elements)
8067 int i, j, x, y, check = 0;
8069 putFile16BitBE(file, num_changed_custom_elements);
8071 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8073 int element = EL_CUSTOM_START + i;
8074 struct ElementInfo *ei = &element_info[element];
8076 if (ei->modified_settings)
8078 if (check < num_changed_custom_elements)
8080 putFile16BitBE(file, element);
8082 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8083 putFile8Bit(file, ei->description[j]);
8085 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8087 // some free bytes for future properties and padding
8088 WriteUnusedBytesToFile(file, 7);
8090 putFile8Bit(file, ei->use_gfx_element);
8091 putFile16BitBE(file, ei->gfx_element_initial);
8093 putFile8Bit(file, ei->collect_score_initial);
8094 putFile8Bit(file, ei->collect_count_initial);
8096 putFile16BitBE(file, ei->push_delay_fixed);
8097 putFile16BitBE(file, ei->push_delay_random);
8098 putFile16BitBE(file, ei->move_delay_fixed);
8099 putFile16BitBE(file, ei->move_delay_random);
8101 putFile16BitBE(file, ei->move_pattern);
8102 putFile8Bit(file, ei->move_direction_initial);
8103 putFile8Bit(file, ei->move_stepsize);
8105 for (y = 0; y < 3; y++)
8106 for (x = 0; x < 3; x++)
8107 putFile16BitBE(file, ei->content.e[x][y]);
8109 putFile32BitBE(file, ei->change->events);
8111 putFile16BitBE(file, ei->change->target_element);
8113 putFile16BitBE(file, ei->change->delay_fixed);
8114 putFile16BitBE(file, ei->change->delay_random);
8115 putFile16BitBE(file, ei->change->delay_frames);
8117 putFile16BitBE(file, ei->change->initial_trigger_element);
8119 putFile8Bit(file, ei->change->explode);
8120 putFile8Bit(file, ei->change->use_target_content);
8121 putFile8Bit(file, ei->change->only_if_complete);
8122 putFile8Bit(file, ei->change->use_random_replace);
8124 putFile8Bit(file, ei->change->random_percentage);
8125 putFile8Bit(file, ei->change->replace_when);
8127 for (y = 0; y < 3; y++)
8128 for (x = 0; x < 3; x++)
8129 putFile16BitBE(file, ei->change->content.e[x][y]);
8131 putFile8Bit(file, ei->slippery_type);
8133 // some free bytes for future properties and padding
8134 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8141 if (check != num_changed_custom_elements) // should not happen
8142 Warn("inconsistent number of custom element properties");
8146 #if ENABLE_HISTORIC_CHUNKS
8147 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8149 struct ElementInfo *ei = &element_info[element];
8152 // ---------- custom element base property values (96 bytes) ----------------
8154 putFile16BitBE(file, element);
8156 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8157 putFile8Bit(file, ei->description[i]);
8159 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8161 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8163 putFile8Bit(file, ei->num_change_pages);
8165 putFile16BitBE(file, ei->ce_value_fixed_initial);
8166 putFile16BitBE(file, ei->ce_value_random_initial);
8167 putFile8Bit(file, ei->use_last_ce_value);
8169 putFile8Bit(file, ei->use_gfx_element);
8170 putFile16BitBE(file, ei->gfx_element_initial);
8172 putFile8Bit(file, ei->collect_score_initial);
8173 putFile8Bit(file, ei->collect_count_initial);
8175 putFile8Bit(file, ei->drop_delay_fixed);
8176 putFile8Bit(file, ei->push_delay_fixed);
8177 putFile8Bit(file, ei->drop_delay_random);
8178 putFile8Bit(file, ei->push_delay_random);
8179 putFile16BitBE(file, ei->move_delay_fixed);
8180 putFile16BitBE(file, ei->move_delay_random);
8182 // bits 0 - 15 of "move_pattern" ...
8183 putFile16BitBE(file, ei->move_pattern & 0xffff);
8184 putFile8Bit(file, ei->move_direction_initial);
8185 putFile8Bit(file, ei->move_stepsize);
8187 putFile8Bit(file, ei->slippery_type);
8189 for (y = 0; y < 3; y++)
8190 for (x = 0; x < 3; x++)
8191 putFile16BitBE(file, ei->content.e[x][y]);
8193 putFile16BitBE(file, ei->move_enter_element);
8194 putFile16BitBE(file, ei->move_leave_element);
8195 putFile8Bit(file, ei->move_leave_type);
8197 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8198 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8200 putFile8Bit(file, ei->access_direction);
8202 putFile8Bit(file, ei->explosion_delay);
8203 putFile8Bit(file, ei->ignition_delay);
8204 putFile8Bit(file, ei->explosion_type);
8206 // some free bytes for future custom property values and padding
8207 WriteUnusedBytesToFile(file, 1);
8209 // ---------- change page property values (48 bytes) ------------------------
8211 for (i = 0; i < ei->num_change_pages; i++)
8213 struct ElementChangeInfo *change = &ei->change_page[i];
8214 unsigned int event_bits;
8216 // bits 0 - 31 of "has_event[]" ...
8218 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8219 if (change->has_event[j])
8220 event_bits |= (1u << j);
8221 putFile32BitBE(file, event_bits);
8223 putFile16BitBE(file, change->target_element);
8225 putFile16BitBE(file, change->delay_fixed);
8226 putFile16BitBE(file, change->delay_random);
8227 putFile16BitBE(file, change->delay_frames);
8229 putFile16BitBE(file, change->initial_trigger_element);
8231 putFile8Bit(file, change->explode);
8232 putFile8Bit(file, change->use_target_content);
8233 putFile8Bit(file, change->only_if_complete);
8234 putFile8Bit(file, change->use_random_replace);
8236 putFile8Bit(file, change->random_percentage);
8237 putFile8Bit(file, change->replace_when);
8239 for (y = 0; y < 3; y++)
8240 for (x = 0; x < 3; x++)
8241 putFile16BitBE(file, change->target_content.e[x][y]);
8243 putFile8Bit(file, change->can_change);
8245 putFile8Bit(file, change->trigger_side);
8247 putFile8Bit(file, change->trigger_player);
8248 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8249 log_2(change->trigger_page)));
8251 putFile8Bit(file, change->has_action);
8252 putFile8Bit(file, change->action_type);
8253 putFile8Bit(file, change->action_mode);
8254 putFile16BitBE(file, change->action_arg);
8256 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8258 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8259 if (change->has_event[j])
8260 event_bits |= (1u << (j - 32));
8261 putFile8Bit(file, event_bits);
8266 #if ENABLE_HISTORIC_CHUNKS
8267 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8269 struct ElementInfo *ei = &element_info[element];
8270 struct ElementGroupInfo *group = ei->group;
8273 putFile16BitBE(file, element);
8275 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8276 putFile8Bit(file, ei->description[i]);
8278 putFile8Bit(file, group->num_elements);
8280 putFile8Bit(file, ei->use_gfx_element);
8281 putFile16BitBE(file, ei->gfx_element_initial);
8283 putFile8Bit(file, group->choice_mode);
8285 // some free bytes for future values and padding
8286 WriteUnusedBytesToFile(file, 3);
8288 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8289 putFile16BitBE(file, group->element[i]);
8293 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8294 boolean write_element)
8296 int save_type = entry->save_type;
8297 int data_type = entry->data_type;
8298 int conf_type = entry->conf_type;
8299 int byte_mask = conf_type & CONF_MASK_BYTES;
8300 int element = entry->element;
8301 int default_value = entry->default_value;
8303 boolean modified = FALSE;
8305 if (byte_mask != CONF_MASK_MULTI_BYTES)
8307 void *value_ptr = entry->value;
8308 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8311 // check if any settings have been modified before saving them
8312 if (value != default_value)
8315 // do not save if explicitly told or if unmodified default settings
8316 if ((save_type == SAVE_CONF_NEVER) ||
8317 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8321 num_bytes += putFile16BitBE(file, element);
8323 num_bytes += putFile8Bit(file, conf_type);
8324 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8325 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8326 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8329 else if (data_type == TYPE_STRING)
8331 char *default_string = entry->default_string;
8332 char *string = (char *)(entry->value);
8333 int string_length = strlen(string);
8336 // check if any settings have been modified before saving them
8337 if (!strEqual(string, default_string))
8340 // do not save if explicitly told or if unmodified default settings
8341 if ((save_type == SAVE_CONF_NEVER) ||
8342 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8346 num_bytes += putFile16BitBE(file, element);
8348 num_bytes += putFile8Bit(file, conf_type);
8349 num_bytes += putFile16BitBE(file, string_length);
8351 for (i = 0; i < string_length; i++)
8352 num_bytes += putFile8Bit(file, string[i]);
8354 else if (data_type == TYPE_ELEMENT_LIST)
8356 int *element_array = (int *)(entry->value);
8357 int num_elements = *(int *)(entry->num_entities);
8360 // check if any settings have been modified before saving them
8361 for (i = 0; i < num_elements; i++)
8362 if (element_array[i] != default_value)
8365 // do not save if explicitly told or if unmodified default settings
8366 if ((save_type == SAVE_CONF_NEVER) ||
8367 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8371 num_bytes += putFile16BitBE(file, element);
8373 num_bytes += putFile8Bit(file, conf_type);
8374 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8376 for (i = 0; i < num_elements; i++)
8377 num_bytes += putFile16BitBE(file, element_array[i]);
8379 else if (data_type == TYPE_CONTENT_LIST)
8381 struct Content *content = (struct Content *)(entry->value);
8382 int num_contents = *(int *)(entry->num_entities);
8385 // check if any settings have been modified before saving them
8386 for (i = 0; i < num_contents; i++)
8387 for (y = 0; y < 3; y++)
8388 for (x = 0; x < 3; x++)
8389 if (content[i].e[x][y] != default_value)
8392 // do not save if explicitly told or if unmodified default settings
8393 if ((save_type == SAVE_CONF_NEVER) ||
8394 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8398 num_bytes += putFile16BitBE(file, element);
8400 num_bytes += putFile8Bit(file, conf_type);
8401 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8403 for (i = 0; i < num_contents; i++)
8404 for (y = 0; y < 3; y++)
8405 for (x = 0; x < 3; x++)
8406 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8412 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8417 li = *level; // copy level data into temporary buffer
8419 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8420 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8425 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8430 li = *level; // copy level data into temporary buffer
8432 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8433 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8438 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8440 int envelope_nr = element - EL_ENVELOPE_1;
8444 chunk_size += putFile16BitBE(file, element);
8446 // copy envelope data into temporary buffer
8447 xx_envelope = level->envelope[envelope_nr];
8449 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8450 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8455 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8457 struct ElementInfo *ei = &element_info[element];
8461 chunk_size += putFile16BitBE(file, element);
8463 xx_ei = *ei; // copy element data into temporary buffer
8465 // set default description string for this specific element
8466 strcpy(xx_default_description, getDefaultElementDescription(ei));
8468 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8469 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8471 for (i = 0; i < ei->num_change_pages; i++)
8473 struct ElementChangeInfo *change = &ei->change_page[i];
8475 xx_current_change_page = i;
8477 xx_change = *change; // copy change data into temporary buffer
8480 setEventBitsFromEventFlags(change);
8482 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8483 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8490 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8492 struct ElementInfo *ei = &element_info[element];
8493 struct ElementGroupInfo *group = ei->group;
8497 chunk_size += putFile16BitBE(file, element);
8499 xx_ei = *ei; // copy element data into temporary buffer
8500 xx_group = *group; // copy group data into temporary buffer
8502 // set default description string for this specific element
8503 strcpy(xx_default_description, getDefaultElementDescription(ei));
8505 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8506 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8511 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8513 struct ElementInfo *ei = &element_info[element];
8517 chunk_size += putFile16BitBE(file, element);
8519 xx_ei = *ei; // copy element data into temporary buffer
8521 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8522 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8527 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8528 boolean save_as_template)
8534 if (!(file = fopen(filename, MODE_WRITE)))
8536 Warn("cannot save level file '%s'", filename);
8541 level->file_version = FILE_VERSION_ACTUAL;
8542 level->game_version = GAME_VERSION_ACTUAL;
8544 level->creation_date = getCurrentDate();
8546 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8547 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8549 chunk_size = SaveLevel_VERS(NULL, level);
8550 putFileChunkBE(file, "VERS", chunk_size);
8551 SaveLevel_VERS(file, level);
8553 chunk_size = SaveLevel_DATE(NULL, level);
8554 putFileChunkBE(file, "DATE", chunk_size);
8555 SaveLevel_DATE(file, level);
8557 chunk_size = SaveLevel_NAME(NULL, level);
8558 putFileChunkBE(file, "NAME", chunk_size);
8559 SaveLevel_NAME(file, level);
8561 chunk_size = SaveLevel_AUTH(NULL, level);
8562 putFileChunkBE(file, "AUTH", chunk_size);
8563 SaveLevel_AUTH(file, level);
8565 chunk_size = SaveLevel_INFO(NULL, level);
8566 putFileChunkBE(file, "INFO", chunk_size);
8567 SaveLevel_INFO(file, level);
8569 chunk_size = SaveLevel_BODY(NULL, level);
8570 putFileChunkBE(file, "BODY", chunk_size);
8571 SaveLevel_BODY(file, level);
8573 chunk_size = SaveLevel_ELEM(NULL, level);
8574 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8576 putFileChunkBE(file, "ELEM", chunk_size);
8577 SaveLevel_ELEM(file, level);
8580 for (i = 0; i < NUM_ENVELOPES; i++)
8582 int element = EL_ENVELOPE_1 + i;
8584 chunk_size = SaveLevel_NOTE(NULL, level, element);
8585 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8587 putFileChunkBE(file, "NOTE", chunk_size);
8588 SaveLevel_NOTE(file, level, element);
8592 // if not using template level, check for non-default custom/group elements
8593 if (!level->use_custom_template || save_as_template)
8595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8597 int element = EL_CUSTOM_START + i;
8599 chunk_size = SaveLevel_CUSX(NULL, level, element);
8600 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8602 putFileChunkBE(file, "CUSX", chunk_size);
8603 SaveLevel_CUSX(file, level, element);
8607 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8609 int element = EL_GROUP_START + i;
8611 chunk_size = SaveLevel_GRPX(NULL, level, element);
8612 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8614 putFileChunkBE(file, "GRPX", chunk_size);
8615 SaveLevel_GRPX(file, level, element);
8619 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8621 int element = GET_EMPTY_ELEMENT(i);
8623 chunk_size = SaveLevel_EMPX(NULL, level, element);
8624 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8626 putFileChunkBE(file, "EMPX", chunk_size);
8627 SaveLevel_EMPX(file, level, element);
8634 SetFilePermissions(filename, PERMS_PRIVATE);
8637 void SaveLevel(int nr)
8639 char *filename = getDefaultLevelFilename(nr);
8641 SaveLevelFromFilename(&level, filename, FALSE);
8644 void SaveLevelTemplate(void)
8646 char *filename = getLocalLevelTemplateFilename();
8648 SaveLevelFromFilename(&level, filename, TRUE);
8651 boolean SaveLevelChecked(int nr)
8653 char *filename = getDefaultLevelFilename(nr);
8654 boolean new_level = !fileExists(filename);
8655 boolean level_saved = FALSE;
8657 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8662 Request("Level saved!", REQ_CONFIRM);
8670 void DumpLevel(struct LevelInfo *level)
8672 if (level->no_level_file || level->no_valid_file)
8674 Warn("cannot dump -- no valid level file found");
8680 Print("Level xxx (file version %08d, game version %08d)\n",
8681 level->file_version, level->game_version);
8684 Print("Level author: '%s'\n", level->author);
8685 Print("Level title: '%s'\n", level->name);
8687 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8689 Print("Level time: %d seconds\n", level->time);
8690 Print("Gems needed: %d\n", level->gems_needed);
8692 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8693 Print("Time for wheel: %d seconds\n", level->time_wheel);
8694 Print("Time for light: %d seconds\n", level->time_light);
8695 Print("Time for timegate: %d seconds\n", level->time_timegate);
8697 Print("Amoeba speed: %d\n", level->amoeba_speed);
8700 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8701 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8702 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8703 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8704 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8705 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8711 for (i = 0; i < NUM_ENVELOPES; i++)
8713 char *text = level->envelope[i].text;
8714 int text_len = strlen(text);
8715 boolean has_text = FALSE;
8717 for (j = 0; j < text_len; j++)
8718 if (text[j] != ' ' && text[j] != '\n')
8724 Print("Envelope %d:\n'%s'\n", i + 1, text);
8732 void DumpLevels(void)
8734 static LevelDirTree *dumplevel_leveldir = NULL;
8736 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8737 global.dumplevel_leveldir);
8739 if (dumplevel_leveldir == NULL)
8740 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8742 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8743 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8744 Fail("no such level number: %d", global.dumplevel_level_nr);
8746 leveldir_current = dumplevel_leveldir;
8748 LoadLevel(global.dumplevel_level_nr);
8755 // ============================================================================
8756 // tape file functions
8757 // ============================================================================
8759 static void setTapeInfoToDefaults(void)
8763 // always start with reliable default values (empty tape)
8766 // default values (also for pre-1.2 tapes) with only the first player
8767 tape.player_participates[0] = TRUE;
8768 for (i = 1; i < MAX_PLAYERS; i++)
8769 tape.player_participates[i] = FALSE;
8771 // at least one (default: the first) player participates in every tape
8772 tape.num_participating_players = 1;
8774 tape.property_bits = TAPE_PROPERTY_NONE;
8776 tape.level_nr = level_nr;
8778 tape.changed = FALSE;
8779 tape.solved = FALSE;
8781 tape.recording = FALSE;
8782 tape.playing = FALSE;
8783 tape.pausing = FALSE;
8785 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8786 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8788 tape.no_info_chunk = TRUE;
8789 tape.no_valid_file = FALSE;
8792 static int getTapePosSize(struct TapeInfo *tape)
8794 int tape_pos_size = 0;
8796 if (tape->use_key_actions)
8797 tape_pos_size += tape->num_participating_players;
8799 if (tape->use_mouse_actions)
8800 tape_pos_size += 3; // x and y position and mouse button mask
8802 tape_pos_size += 1; // tape action delay value
8804 return tape_pos_size;
8807 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8809 tape->use_key_actions = FALSE;
8810 tape->use_mouse_actions = FALSE;
8812 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8813 tape->use_key_actions = TRUE;
8815 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8816 tape->use_mouse_actions = TRUE;
8819 static int getTapeActionValue(struct TapeInfo *tape)
8821 return (tape->use_key_actions &&
8822 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8823 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8824 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8825 TAPE_ACTIONS_DEFAULT);
8828 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8830 tape->file_version = getFileVersion(file);
8831 tape->game_version = getFileVersion(file);
8836 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8840 tape->random_seed = getFile32BitBE(file);
8841 tape->date = getFile32BitBE(file);
8842 tape->length = getFile32BitBE(file);
8844 // read header fields that are new since version 1.2
8845 if (tape->file_version >= FILE_VERSION_1_2)
8847 byte store_participating_players = getFile8Bit(file);
8850 // since version 1.2, tapes store which players participate in the tape
8851 tape->num_participating_players = 0;
8852 for (i = 0; i < MAX_PLAYERS; i++)
8854 tape->player_participates[i] = FALSE;
8856 if (store_participating_players & (1 << i))
8858 tape->player_participates[i] = TRUE;
8859 tape->num_participating_players++;
8863 setTapeActionFlags(tape, getFile8Bit(file));
8865 tape->property_bits = getFile8Bit(file);
8866 tape->solved = getFile8Bit(file);
8868 engine_version = getFileVersion(file);
8869 if (engine_version > 0)
8870 tape->engine_version = engine_version;
8872 tape->engine_version = tape->game_version;
8878 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8880 tape->scr_fieldx = getFile8Bit(file);
8881 tape->scr_fieldy = getFile8Bit(file);
8886 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8888 char *level_identifier = NULL;
8889 int level_identifier_size;
8892 tape->no_info_chunk = FALSE;
8894 level_identifier_size = getFile16BitBE(file);
8896 level_identifier = checked_malloc(level_identifier_size);
8898 for (i = 0; i < level_identifier_size; i++)
8899 level_identifier[i] = getFile8Bit(file);
8901 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8902 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8904 checked_free(level_identifier);
8906 tape->level_nr = getFile16BitBE(file);
8908 chunk_size = 2 + level_identifier_size + 2;
8913 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8916 int tape_pos_size = getTapePosSize(tape);
8917 int chunk_size_expected = tape_pos_size * tape->length;
8919 if (chunk_size_expected != chunk_size)
8921 ReadUnusedBytesFromFile(file, chunk_size);
8922 return chunk_size_expected;
8925 for (i = 0; i < tape->length; i++)
8927 if (i >= MAX_TAPE_LEN)
8929 Warn("tape truncated -- size exceeds maximum tape size %d",
8932 // tape too large; read and ignore remaining tape data from this chunk
8933 for (;i < tape->length; i++)
8934 ReadUnusedBytesFromFile(file, tape_pos_size);
8939 if (tape->use_key_actions)
8941 for (j = 0; j < MAX_PLAYERS; j++)
8943 tape->pos[i].action[j] = MV_NONE;
8945 if (tape->player_participates[j])
8946 tape->pos[i].action[j] = getFile8Bit(file);
8950 if (tape->use_mouse_actions)
8952 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8953 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8954 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8957 tape->pos[i].delay = getFile8Bit(file);
8959 if (tape->file_version == FILE_VERSION_1_0)
8961 // eliminate possible diagonal moves in old tapes
8962 // this is only for backward compatibility
8964 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8965 byte action = tape->pos[i].action[0];
8966 int k, num_moves = 0;
8968 for (k = 0; k < 4; k++)
8970 if (action & joy_dir[k])
8972 tape->pos[i + num_moves].action[0] = joy_dir[k];
8974 tape->pos[i + num_moves].delay = 0;
8983 tape->length += num_moves;
8986 else if (tape->file_version < FILE_VERSION_2_0)
8988 // convert pre-2.0 tapes to new tape format
8990 if (tape->pos[i].delay > 1)
8993 tape->pos[i + 1] = tape->pos[i];
8994 tape->pos[i + 1].delay = 1;
8997 for (j = 0; j < MAX_PLAYERS; j++)
8998 tape->pos[i].action[j] = MV_NONE;
8999 tape->pos[i].delay--;
9006 if (checkEndOfFile(file))
9010 if (i != tape->length)
9011 chunk_size = tape_pos_size * i;
9016 static void LoadTape_SokobanSolution(char *filename)
9019 int move_delay = TILESIZE / level.initial_player_stepsize[0];
9021 if (!(file = openFile(filename, MODE_READ)))
9023 tape.no_valid_file = TRUE;
9028 while (!checkEndOfFile(file))
9030 unsigned char c = getByteFromFile(file);
9032 if (checkEndOfFile(file))
9039 tape.pos[tape.length].action[0] = MV_UP;
9040 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9046 tape.pos[tape.length].action[0] = MV_DOWN;
9047 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9053 tape.pos[tape.length].action[0] = MV_LEFT;
9054 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9060 tape.pos[tape.length].action[0] = MV_RIGHT;
9061 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9069 // ignore white-space characters
9073 tape.no_valid_file = TRUE;
9075 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9083 if (tape.no_valid_file)
9086 tape.length_frames = GetTapeLengthFrames();
9087 tape.length_seconds = GetTapeLengthSeconds();
9090 void LoadTapeFromFilename(char *filename)
9092 char cookie[MAX_LINE_LEN];
9093 char chunk_name[CHUNK_ID_LEN + 1];
9097 // always start with reliable default values
9098 setTapeInfoToDefaults();
9100 if (strSuffix(filename, ".sln"))
9102 LoadTape_SokobanSolution(filename);
9107 if (!(file = openFile(filename, MODE_READ)))
9109 tape.no_valid_file = TRUE;
9114 getFileChunkBE(file, chunk_name, NULL);
9115 if (strEqual(chunk_name, "RND1"))
9117 getFile32BitBE(file); // not used
9119 getFileChunkBE(file, chunk_name, NULL);
9120 if (!strEqual(chunk_name, "TAPE"))
9122 tape.no_valid_file = TRUE;
9124 Warn("unknown format of tape file '%s'", filename);
9131 else // check for pre-2.0 file format with cookie string
9133 strcpy(cookie, chunk_name);
9134 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9136 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9137 cookie[strlen(cookie) - 1] = '\0';
9139 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9141 tape.no_valid_file = TRUE;
9143 Warn("unknown format of tape file '%s'", filename);
9150 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9152 tape.no_valid_file = TRUE;
9154 Warn("unsupported version of tape file '%s'", filename);
9161 // pre-2.0 tape files have no game version, so use file version here
9162 tape.game_version = tape.file_version;
9165 if (tape.file_version < FILE_VERSION_1_2)
9167 // tape files from versions before 1.2.0 without chunk structure
9168 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9169 LoadTape_BODY(file, 2 * tape.length, &tape);
9177 int (*loader)(File *, int, struct TapeInfo *);
9181 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9182 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9183 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9184 { "INFO", -1, LoadTape_INFO },
9185 { "BODY", -1, LoadTape_BODY },
9189 while (getFileChunkBE(file, chunk_name, &chunk_size))
9193 while (chunk_info[i].name != NULL &&
9194 !strEqual(chunk_name, chunk_info[i].name))
9197 if (chunk_info[i].name == NULL)
9199 Warn("unknown chunk '%s' in tape file '%s'",
9200 chunk_name, filename);
9202 ReadUnusedBytesFromFile(file, chunk_size);
9204 else if (chunk_info[i].size != -1 &&
9205 chunk_info[i].size != chunk_size)
9207 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9208 chunk_size, chunk_name, filename);
9210 ReadUnusedBytesFromFile(file, chunk_size);
9214 // call function to load this tape chunk
9215 int chunk_size_expected =
9216 (chunk_info[i].loader)(file, chunk_size, &tape);
9218 // the size of some chunks cannot be checked before reading other
9219 // chunks first (like "HEAD" and "BODY") that contain some header
9220 // information, so check them here
9221 if (chunk_size_expected != chunk_size)
9223 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9224 chunk_size, chunk_name, filename);
9232 tape.length_frames = GetTapeLengthFrames();
9233 tape.length_seconds = GetTapeLengthSeconds();
9236 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9238 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9240 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9241 tape.engine_version);
9245 void LoadTape(int nr)
9247 char *filename = getTapeFilename(nr);
9249 LoadTapeFromFilename(filename);
9252 void LoadSolutionTape(int nr)
9254 char *filename = getSolutionTapeFilename(nr);
9256 LoadTapeFromFilename(filename);
9258 if (TAPE_IS_EMPTY(tape))
9260 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9261 level.native_bd_level->replay != NULL)
9262 CopyNativeTape_BD_to_RND(&level);
9263 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9264 level.native_sp_level->demo.is_available)
9265 CopyNativeTape_SP_to_RND(&level);
9269 void LoadScoreTape(char *score_tape_basename, int nr)
9271 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9273 LoadTapeFromFilename(filename);
9276 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9278 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9280 LoadTapeFromFilename(filename);
9283 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9285 // chunk required for team mode tapes with non-default screen size
9286 return (tape->num_participating_players > 1 &&
9287 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9288 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9291 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9293 putFileVersion(file, tape->file_version);
9294 putFileVersion(file, tape->game_version);
9297 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9300 byte store_participating_players = 0;
9302 // set bits for participating players for compact storage
9303 for (i = 0; i < MAX_PLAYERS; i++)
9304 if (tape->player_participates[i])
9305 store_participating_players |= (1 << i);
9307 putFile32BitBE(file, tape->random_seed);
9308 putFile32BitBE(file, tape->date);
9309 putFile32BitBE(file, tape->length);
9311 putFile8Bit(file, store_participating_players);
9313 putFile8Bit(file, getTapeActionValue(tape));
9315 putFile8Bit(file, tape->property_bits);
9316 putFile8Bit(file, tape->solved);
9318 putFileVersion(file, tape->engine_version);
9321 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9323 putFile8Bit(file, tape->scr_fieldx);
9324 putFile8Bit(file, tape->scr_fieldy);
9327 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9329 int level_identifier_size = strlen(tape->level_identifier) + 1;
9332 putFile16BitBE(file, level_identifier_size);
9334 for (i = 0; i < level_identifier_size; i++)
9335 putFile8Bit(file, tape->level_identifier[i]);
9337 putFile16BitBE(file, tape->level_nr);
9340 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9344 for (i = 0; i < tape->length; i++)
9346 if (tape->use_key_actions)
9348 for (j = 0; j < MAX_PLAYERS; j++)
9349 if (tape->player_participates[j])
9350 putFile8Bit(file, tape->pos[i].action[j]);
9353 if (tape->use_mouse_actions)
9355 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9356 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9357 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9360 putFile8Bit(file, tape->pos[i].delay);
9364 void SaveTapeToFilename(char *filename)
9368 int info_chunk_size;
9369 int body_chunk_size;
9371 if (!(file = fopen(filename, MODE_WRITE)))
9373 Warn("cannot save level recording file '%s'", filename);
9378 tape_pos_size = getTapePosSize(&tape);
9380 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9381 body_chunk_size = tape_pos_size * tape.length;
9383 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9384 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9386 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9387 SaveTape_VERS(file, &tape);
9389 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9390 SaveTape_HEAD(file, &tape);
9392 if (checkSaveTape_SCRN(&tape))
9394 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9395 SaveTape_SCRN(file, &tape);
9398 putFileChunkBE(file, "INFO", info_chunk_size);
9399 SaveTape_INFO(file, &tape);
9401 putFileChunkBE(file, "BODY", body_chunk_size);
9402 SaveTape_BODY(file, &tape);
9406 SetFilePermissions(filename, PERMS_PRIVATE);
9409 static void SaveTapeExt(char *filename)
9413 tape.file_version = FILE_VERSION_ACTUAL;
9414 tape.game_version = GAME_VERSION_ACTUAL;
9416 tape.num_participating_players = 0;
9418 // count number of participating players
9419 for (i = 0; i < MAX_PLAYERS; i++)
9420 if (tape.player_participates[i])
9421 tape.num_participating_players++;
9423 SaveTapeToFilename(filename);
9425 tape.changed = FALSE;
9428 void SaveTape(int nr)
9430 char *filename = getTapeFilename(nr);
9432 InitTapeDirectory(leveldir_current->subdir);
9434 SaveTapeExt(filename);
9437 void SaveScoreTape(int nr)
9439 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9441 // used instead of "leveldir_current->subdir" (for network games)
9442 InitScoreTapeDirectory(levelset.identifier, nr);
9444 SaveTapeExt(filename);
9447 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9448 unsigned int req_state_added)
9450 char *filename = getTapeFilename(nr);
9451 boolean new_tape = !fileExists(filename);
9452 boolean tape_saved = FALSE;
9454 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9459 Request(msg_saved, REQ_CONFIRM | req_state_added);
9467 boolean SaveTapeChecked(int nr)
9469 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9472 boolean SaveTapeChecked_LevelSolved(int nr)
9474 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9475 "Level solved! Tape saved!", REQ_STAY_OPEN);
9478 void DumpTape(struct TapeInfo *tape)
9480 int tape_frame_counter;
9483 if (tape->no_valid_file)
9485 Warn("cannot dump -- no valid tape file found");
9492 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9493 tape->level_nr, tape->file_version, tape->game_version);
9494 Print(" (effective engine version %08d)\n",
9495 tape->engine_version);
9496 Print("Level series identifier: '%s'\n", tape->level_identifier);
9498 Print("Solution tape: %s\n",
9499 tape->solved ? "yes" :
9500 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9502 Print("Special tape properties: ");
9503 if (tape->property_bits == TAPE_PROPERTY_NONE)
9505 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9506 Print("[em_random_bug]");
9507 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9508 Print("[game_speed]");
9509 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9511 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9512 Print("[single_step]");
9513 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9514 Print("[snapshot]");
9515 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9516 Print("[replayed]");
9517 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9518 Print("[tas_keys]");
9519 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9520 Print("[small_graphics]");
9523 int year2 = tape->date / 10000;
9524 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9525 int month_index_raw = (tape->date / 100) % 100;
9526 int month_index = month_index_raw % 12; // prevent invalid index
9527 int month = month_index + 1;
9528 int day = tape->date % 100;
9530 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9534 tape_frame_counter = 0;
9536 for (i = 0; i < tape->length; i++)
9538 if (i >= MAX_TAPE_LEN)
9543 for (j = 0; j < MAX_PLAYERS; j++)
9545 if (tape->player_participates[j])
9547 int action = tape->pos[i].action[j];
9549 Print("%d:%02x ", j, action);
9550 Print("[%c%c%c%c|%c%c] - ",
9551 (action & JOY_LEFT ? '<' : ' '),
9552 (action & JOY_RIGHT ? '>' : ' '),
9553 (action & JOY_UP ? '^' : ' '),
9554 (action & JOY_DOWN ? 'v' : ' '),
9555 (action & JOY_BUTTON_1 ? '1' : ' '),
9556 (action & JOY_BUTTON_2 ? '2' : ' '));
9560 Print("(%03d) ", tape->pos[i].delay);
9561 Print("[%05d]\n", tape_frame_counter);
9563 tape_frame_counter += tape->pos[i].delay;
9569 void DumpTapes(void)
9571 static LevelDirTree *dumptape_leveldir = NULL;
9573 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9574 global.dumptape_leveldir);
9576 if (dumptape_leveldir == NULL)
9577 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9579 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9580 global.dumptape_level_nr > dumptape_leveldir->last_level)
9581 Fail("no such level number: %d", global.dumptape_level_nr);
9583 leveldir_current = dumptape_leveldir;
9585 if (options.mytapes)
9586 LoadTape(global.dumptape_level_nr);
9588 LoadSolutionTape(global.dumptape_level_nr);
9596 // ============================================================================
9597 // score file functions
9598 // ============================================================================
9600 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9604 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9606 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9607 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9608 scores->entry[i].score = 0;
9609 scores->entry[i].time = 0;
9611 scores->entry[i].id = -1;
9612 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9613 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9614 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9615 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9616 strcpy(scores->entry[i].country_code, "??");
9619 scores->num_entries = 0;
9620 scores->last_added = -1;
9621 scores->last_added_local = -1;
9623 scores->updated = FALSE;
9624 scores->uploaded = FALSE;
9625 scores->tape_downloaded = FALSE;
9626 scores->force_last_added = FALSE;
9628 // The following values are intentionally not reset here:
9632 // - continue_playing
9633 // - continue_on_return
9636 static void setScoreInfoToDefaults(void)
9638 setScoreInfoToDefaultsExt(&scores);
9641 static void setServerScoreInfoToDefaults(void)
9643 setScoreInfoToDefaultsExt(&server_scores);
9646 static void LoadScore_OLD(int nr)
9649 char *filename = getScoreFilename(nr);
9650 char cookie[MAX_LINE_LEN];
9651 char line[MAX_LINE_LEN];
9655 if (!(file = fopen(filename, MODE_READ)))
9658 // check file identifier
9659 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9661 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9662 cookie[strlen(cookie) - 1] = '\0';
9664 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9666 Warn("unknown format of score file '%s'", filename);
9673 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9675 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9676 Warn("fscanf() failed; %s", strerror(errno));
9678 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9681 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9682 line[strlen(line) - 1] = '\0';
9684 for (line_ptr = line; *line_ptr; line_ptr++)
9686 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9688 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9689 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9698 static void ConvertScore_OLD(void)
9700 // only convert score to time for levels that rate playing time over score
9701 if (!level.rate_time_over_score)
9704 // convert old score to playing time for score-less levels (like Supaplex)
9705 int time_final_max = 999;
9708 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9710 int score = scores.entry[i].score;
9712 if (score > 0 && score < time_final_max)
9713 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9717 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9719 scores->file_version = getFileVersion(file);
9720 scores->game_version = getFileVersion(file);
9725 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9727 char *level_identifier = NULL;
9728 int level_identifier_size;
9731 level_identifier_size = getFile16BitBE(file);
9733 level_identifier = checked_malloc(level_identifier_size);
9735 for (i = 0; i < level_identifier_size; i++)
9736 level_identifier[i] = getFile8Bit(file);
9738 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9739 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9741 checked_free(level_identifier);
9743 scores->level_nr = getFile16BitBE(file);
9744 scores->num_entries = getFile16BitBE(file);
9746 chunk_size = 2 + level_identifier_size + 2 + 2;
9751 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9755 for (i = 0; i < scores->num_entries; i++)
9757 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9758 scores->entry[i].name[j] = getFile8Bit(file);
9760 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9763 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9768 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9772 for (i = 0; i < scores->num_entries; i++)
9773 scores->entry[i].score = getFile16BitBE(file);
9775 chunk_size = scores->num_entries * 2;
9780 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9784 for (i = 0; i < scores->num_entries; i++)
9785 scores->entry[i].score = getFile32BitBE(file);
9787 chunk_size = scores->num_entries * 4;
9792 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9796 for (i = 0; i < scores->num_entries; i++)
9797 scores->entry[i].time = getFile32BitBE(file);
9799 chunk_size = scores->num_entries * 4;
9804 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9808 for (i = 0; i < scores->num_entries; i++)
9810 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9811 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9813 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9816 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9821 void LoadScore(int nr)
9823 char *filename = getScoreFilename(nr);
9824 char cookie[MAX_LINE_LEN];
9825 char chunk_name[CHUNK_ID_LEN + 1];
9827 boolean old_score_file_format = FALSE;
9830 // always start with reliable default values
9831 setScoreInfoToDefaults();
9833 if (!(file = openFile(filename, MODE_READ)))
9836 getFileChunkBE(file, chunk_name, NULL);
9837 if (strEqual(chunk_name, "RND1"))
9839 getFile32BitBE(file); // not used
9841 getFileChunkBE(file, chunk_name, NULL);
9842 if (!strEqual(chunk_name, "SCOR"))
9844 Warn("unknown format of score file '%s'", filename);
9851 else // check for old file format with cookie string
9853 strcpy(cookie, chunk_name);
9854 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9856 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9857 cookie[strlen(cookie) - 1] = '\0';
9859 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9861 Warn("unknown format of score file '%s'", filename);
9868 old_score_file_format = TRUE;
9871 if (old_score_file_format)
9873 // score files from versions before 4.2.4.0 without chunk structure
9876 // convert score to time, if possible (mainly for Supaplex levels)
9885 int (*loader)(File *, int, struct ScoreInfo *);
9889 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9890 { "INFO", -1, LoadScore_INFO },
9891 { "NAME", -1, LoadScore_NAME },
9892 { "SCOR", -1, LoadScore_SCOR },
9893 { "SC4R", -1, LoadScore_SC4R },
9894 { "TIME", -1, LoadScore_TIME },
9895 { "TAPE", -1, LoadScore_TAPE },
9900 while (getFileChunkBE(file, chunk_name, &chunk_size))
9904 while (chunk_info[i].name != NULL &&
9905 !strEqual(chunk_name, chunk_info[i].name))
9908 if (chunk_info[i].name == NULL)
9910 Warn("unknown chunk '%s' in score file '%s'",
9911 chunk_name, filename);
9913 ReadUnusedBytesFromFile(file, chunk_size);
9915 else if (chunk_info[i].size != -1 &&
9916 chunk_info[i].size != chunk_size)
9918 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9919 chunk_size, chunk_name, filename);
9921 ReadUnusedBytesFromFile(file, chunk_size);
9925 // call function to load this score chunk
9926 int chunk_size_expected =
9927 (chunk_info[i].loader)(file, chunk_size, &scores);
9929 // the size of some chunks cannot be checked before reading other
9930 // chunks first (like "HEAD" and "BODY") that contain some header
9931 // information, so check them here
9932 if (chunk_size_expected != chunk_size)
9934 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9935 chunk_size, chunk_name, filename);
9944 #if ENABLE_HISTORIC_CHUNKS
9945 void SaveScore_OLD(int nr)
9948 char *filename = getScoreFilename(nr);
9951 // used instead of "leveldir_current->subdir" (for network games)
9952 InitScoreDirectory(levelset.identifier);
9954 if (!(file = fopen(filename, MODE_WRITE)))
9956 Warn("cannot save score for level %d", nr);
9961 fprintf(file, "%s\n\n", SCORE_COOKIE);
9963 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9964 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9968 SetFilePermissions(filename, PERMS_PRIVATE);
9972 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9974 putFileVersion(file, scores->file_version);
9975 putFileVersion(file, scores->game_version);
9978 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9980 int level_identifier_size = strlen(scores->level_identifier) + 1;
9983 putFile16BitBE(file, level_identifier_size);
9985 for (i = 0; i < level_identifier_size; i++)
9986 putFile8Bit(file, scores->level_identifier[i]);
9988 putFile16BitBE(file, scores->level_nr);
9989 putFile16BitBE(file, scores->num_entries);
9992 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9996 for (i = 0; i < scores->num_entries; i++)
9998 int name_size = strlen(scores->entry[i].name);
10000 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10001 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10005 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10009 for (i = 0; i < scores->num_entries; i++)
10010 putFile16BitBE(file, scores->entry[i].score);
10013 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10017 for (i = 0; i < scores->num_entries; i++)
10018 putFile32BitBE(file, scores->entry[i].score);
10021 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10025 for (i = 0; i < scores->num_entries; i++)
10026 putFile32BitBE(file, scores->entry[i].time);
10029 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10033 for (i = 0; i < scores->num_entries; i++)
10035 int size = strlen(scores->entry[i].tape_basename);
10037 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10038 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10042 static void SaveScoreToFilename(char *filename)
10045 int info_chunk_size;
10046 int name_chunk_size;
10047 int scor_chunk_size;
10048 int sc4r_chunk_size;
10049 int time_chunk_size;
10050 int tape_chunk_size;
10051 boolean has_large_score_values;
10054 if (!(file = fopen(filename, MODE_WRITE)))
10056 Warn("cannot save score file '%s'", filename);
10061 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10062 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10063 scor_chunk_size = scores.num_entries * 2;
10064 sc4r_chunk_size = scores.num_entries * 4;
10065 time_chunk_size = scores.num_entries * 4;
10066 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10068 has_large_score_values = FALSE;
10069 for (i = 0; i < scores.num_entries; i++)
10070 if (scores.entry[i].score > 0xffff)
10071 has_large_score_values = TRUE;
10073 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10074 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10076 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10077 SaveScore_VERS(file, &scores);
10079 putFileChunkBE(file, "INFO", info_chunk_size);
10080 SaveScore_INFO(file, &scores);
10082 putFileChunkBE(file, "NAME", name_chunk_size);
10083 SaveScore_NAME(file, &scores);
10085 if (has_large_score_values)
10087 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10088 SaveScore_SC4R(file, &scores);
10092 putFileChunkBE(file, "SCOR", scor_chunk_size);
10093 SaveScore_SCOR(file, &scores);
10096 putFileChunkBE(file, "TIME", time_chunk_size);
10097 SaveScore_TIME(file, &scores);
10099 putFileChunkBE(file, "TAPE", tape_chunk_size);
10100 SaveScore_TAPE(file, &scores);
10104 SetFilePermissions(filename, PERMS_PRIVATE);
10107 void SaveScore(int nr)
10109 char *filename = getScoreFilename(nr);
10112 // used instead of "leveldir_current->subdir" (for network games)
10113 InitScoreDirectory(levelset.identifier);
10115 scores.file_version = FILE_VERSION_ACTUAL;
10116 scores.game_version = GAME_VERSION_ACTUAL;
10118 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10119 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10120 scores.level_nr = level_nr;
10122 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10123 if (scores.entry[i].score == 0 &&
10124 scores.entry[i].time == 0 &&
10125 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10128 scores.num_entries = i;
10130 if (scores.num_entries == 0)
10133 SaveScoreToFilename(filename);
10136 static void LoadServerScoreFromCache(int nr)
10138 struct ScoreEntry score_entry;
10147 { &score_entry.score, FALSE, 0 },
10148 { &score_entry.time, FALSE, 0 },
10149 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10150 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10151 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10152 { &score_entry.id, FALSE, 0 },
10153 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10154 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10155 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10156 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10160 char *filename = getScoreCacheFilename(nr);
10161 SetupFileHash *score_hash = loadSetupFileHash(filename);
10164 server_scores.num_entries = 0;
10166 if (score_hash == NULL)
10169 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10171 score_entry = server_scores.entry[i];
10173 for (j = 0; score_mapping[j].value != NULL; j++)
10177 sprintf(token, "%02d.%d", i, j);
10179 char *value = getHashEntry(score_hash, token);
10184 if (score_mapping[j].is_string)
10186 char *score_value = (char *)score_mapping[j].value;
10187 int value_size = score_mapping[j].string_size;
10189 strncpy(score_value, value, value_size);
10190 score_value[value_size] = '\0';
10194 int *score_value = (int *)score_mapping[j].value;
10196 *score_value = atoi(value);
10199 server_scores.num_entries = i + 1;
10202 server_scores.entry[i] = score_entry;
10205 freeSetupFileHash(score_hash);
10208 void LoadServerScore(int nr, boolean download_score)
10210 if (!setup.use_api_server)
10213 // always start with reliable default values
10214 setServerScoreInfoToDefaults();
10216 // 1st step: load server scores from cache file (which may not exist)
10217 // (this should prevent reading it while the thread is writing to it)
10218 LoadServerScoreFromCache(nr);
10220 if (download_score && runtime.use_api_server)
10222 // 2nd step: download server scores from score server to cache file
10223 // (as thread, as it might time out if the server is not reachable)
10224 ApiGetScoreAsThread(nr);
10228 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10230 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10232 // if score tape not uploaded, ask for uploading missing tapes later
10233 if (!setup.has_remaining_tapes)
10234 setup.ask_for_remaining_tapes = TRUE;
10236 setup.provide_uploading_tapes = TRUE;
10237 setup.has_remaining_tapes = TRUE;
10239 SaveSetup_ServerSetup();
10242 void SaveServerScore(int nr, boolean tape_saved)
10244 if (!runtime.use_api_server)
10246 PrepareScoreTapesForUpload(leveldir_current->subdir);
10251 ApiAddScoreAsThread(nr, tape_saved, NULL);
10254 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10255 char *score_tape_filename)
10257 if (!runtime.use_api_server)
10260 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10263 void LoadLocalAndServerScore(int nr, boolean download_score)
10265 int last_added_local = scores.last_added_local;
10266 boolean force_last_added = scores.force_last_added;
10268 // needed if only showing server scores
10269 setScoreInfoToDefaults();
10271 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10274 // restore last added local score entry (before merging server scores)
10275 scores.last_added = scores.last_added_local = last_added_local;
10277 if (setup.use_api_server &&
10278 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10280 // load server scores from cache file and trigger update from server
10281 LoadServerScore(nr, download_score);
10283 // merge local scores with scores from server
10284 MergeServerScore();
10287 if (force_last_added)
10288 scores.force_last_added = force_last_added;
10292 // ============================================================================
10293 // setup file functions
10294 // ============================================================================
10296 #define TOKEN_STR_PLAYER_PREFIX "player_"
10299 static struct TokenInfo global_setup_tokens[] =
10303 &setup.player_name, "player_name"
10307 &setup.multiple_users, "multiple_users"
10311 &setup.sound, "sound"
10315 &setup.sound_loops, "repeating_sound_loops"
10319 &setup.sound_music, "background_music"
10323 &setup.sound_simple, "simple_sound_effects"
10327 &setup.toons, "toons"
10331 &setup.global_animations, "global_animations"
10335 &setup.scroll_delay, "scroll_delay"
10339 &setup.forced_scroll_delay, "forced_scroll_delay"
10343 &setup.scroll_delay_value, "scroll_delay_value"
10347 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10351 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10355 &setup.fade_screens, "fade_screens"
10359 &setup.autorecord, "automatic_tape_recording"
10363 &setup.autorecord_after_replay, "autorecord_after_replay"
10367 &setup.auto_pause_on_start, "auto_pause_on_start"
10371 &setup.show_titlescreen, "show_titlescreen"
10375 &setup.quick_doors, "quick_doors"
10379 &setup.team_mode, "team_mode"
10383 &setup.handicap, "handicap"
10387 &setup.skip_levels, "skip_levels"
10391 &setup.increment_levels, "increment_levels"
10395 &setup.auto_play_next_level, "auto_play_next_level"
10399 &setup.count_score_after_game, "count_score_after_game"
10403 &setup.show_scores_after_game, "show_scores_after_game"
10407 &setup.time_limit, "time_limit"
10411 &setup.fullscreen, "fullscreen"
10415 &setup.window_scaling_percent, "window_scaling_percent"
10419 &setup.window_scaling_quality, "window_scaling_quality"
10423 &setup.screen_rendering_mode, "screen_rendering_mode"
10427 &setup.vsync_mode, "vsync_mode"
10431 &setup.ask_on_escape, "ask_on_escape"
10435 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10439 &setup.ask_on_game_over, "ask_on_game_over"
10443 &setup.ask_on_quit_game, "ask_on_quit_game"
10447 &setup.ask_on_quit_program, "ask_on_quit_program"
10451 &setup.quick_switch, "quick_player_switch"
10455 &setup.input_on_focus, "input_on_focus"
10459 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10463 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10467 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10471 &setup.game_speed_extended, "game_speed_extended"
10475 &setup.game_frame_delay, "game_frame_delay"
10479 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10483 &setup.bd_skip_hatching, "bd_skip_hatching"
10487 &setup.bd_scroll_delay, "bd_scroll_delay"
10491 &setup.bd_smooth_movements, "bd_smooth_movements"
10495 &setup.sp_show_border_elements, "sp_show_border_elements"
10499 &setup.small_game_graphics, "small_game_graphics"
10503 &setup.show_load_save_buttons, "show_load_save_buttons"
10507 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10511 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10515 &setup.graphics_set, "graphics_set"
10519 &setup.sounds_set, "sounds_set"
10523 &setup.music_set, "music_set"
10527 &setup.override_level_graphics, "override_level_graphics"
10531 &setup.override_level_sounds, "override_level_sounds"
10535 &setup.override_level_music, "override_level_music"
10539 &setup.volume_simple, "volume_simple"
10543 &setup.volume_loops, "volume_loops"
10547 &setup.volume_music, "volume_music"
10551 &setup.network_mode, "network_mode"
10555 &setup.network_player_nr, "network_player"
10559 &setup.network_server_hostname, "network_server_hostname"
10563 &setup.touch.control_type, "touch.control_type"
10567 &setup.touch.move_distance, "touch.move_distance"
10571 &setup.touch.drop_distance, "touch.drop_distance"
10575 &setup.touch.transparency, "touch.transparency"
10579 &setup.touch.draw_outlined, "touch.draw_outlined"
10583 &setup.touch.draw_pressed, "touch.draw_pressed"
10587 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10591 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10595 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10599 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10603 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10607 static struct TokenInfo auto_setup_tokens[] =
10611 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10615 static struct TokenInfo server_setup_tokens[] =
10619 &setup.player_uuid, "player_uuid"
10623 &setup.player_version, "player_version"
10627 &setup.use_api_server, TEST_PREFIX "use_api_server"
10631 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10635 &setup.api_server_password, TEST_PREFIX "api_server_password"
10639 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10643 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10647 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10651 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10655 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10659 static struct TokenInfo editor_setup_tokens[] =
10663 &setup.editor.el_classic, "editor.el_classic"
10667 &setup.editor.el_custom, "editor.el_custom"
10671 &setup.editor.el_user_defined, "editor.el_user_defined"
10675 &setup.editor.el_dynamic, "editor.el_dynamic"
10679 &setup.editor.el_headlines, "editor.el_headlines"
10683 &setup.editor.show_element_token, "editor.show_element_token"
10687 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10691 static struct TokenInfo editor_cascade_setup_tokens[] =
10695 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10699 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10703 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10707 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10711 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10715 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10719 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10723 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10727 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10731 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10735 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10739 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10743 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10747 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10751 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10755 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10759 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10763 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10767 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10771 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10775 static struct TokenInfo shortcut_setup_tokens[] =
10779 &setup.shortcut.save_game, "shortcut.save_game"
10783 &setup.shortcut.load_game, "shortcut.load_game"
10787 &setup.shortcut.restart_game, "shortcut.restart_game"
10791 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10795 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10799 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10803 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10807 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10811 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10815 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10819 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10823 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10827 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10831 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10835 &setup.shortcut.tape_record, "shortcut.tape_record"
10839 &setup.shortcut.tape_play, "shortcut.tape_play"
10843 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10847 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10851 &setup.shortcut.sound_music, "shortcut.sound_music"
10855 &setup.shortcut.snap_left, "shortcut.snap_left"
10859 &setup.shortcut.snap_right, "shortcut.snap_right"
10863 &setup.shortcut.snap_up, "shortcut.snap_up"
10867 &setup.shortcut.snap_down, "shortcut.snap_down"
10871 static struct SetupInputInfo setup_input;
10872 static struct TokenInfo player_setup_tokens[] =
10876 &setup_input.use_joystick, ".use_joystick"
10880 &setup_input.joy.device_name, ".joy.device_name"
10884 &setup_input.joy.xleft, ".joy.xleft"
10888 &setup_input.joy.xmiddle, ".joy.xmiddle"
10892 &setup_input.joy.xright, ".joy.xright"
10896 &setup_input.joy.yupper, ".joy.yupper"
10900 &setup_input.joy.ymiddle, ".joy.ymiddle"
10904 &setup_input.joy.ylower, ".joy.ylower"
10908 &setup_input.joy.snap, ".joy.snap_field"
10912 &setup_input.joy.drop, ".joy.place_bomb"
10916 &setup_input.key.left, ".key.move_left"
10920 &setup_input.key.right, ".key.move_right"
10924 &setup_input.key.up, ".key.move_up"
10928 &setup_input.key.down, ".key.move_down"
10932 &setup_input.key.snap, ".key.snap_field"
10936 &setup_input.key.drop, ".key.place_bomb"
10940 static struct TokenInfo system_setup_tokens[] =
10944 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10948 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10952 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10956 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10960 static struct TokenInfo internal_setup_tokens[] =
10964 &setup.internal.program_title, "program_title"
10968 &setup.internal.program_version, "program_version"
10972 &setup.internal.program_author, "program_author"
10976 &setup.internal.program_email, "program_email"
10980 &setup.internal.program_website, "program_website"
10984 &setup.internal.program_copyright, "program_copyright"
10988 &setup.internal.program_company, "program_company"
10992 &setup.internal.program_icon_file, "program_icon_file"
10996 &setup.internal.default_graphics_set, "default_graphics_set"
11000 &setup.internal.default_sounds_set, "default_sounds_set"
11004 &setup.internal.default_music_set, "default_music_set"
11008 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
11012 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
11016 &setup.internal.fallback_music_file, "fallback_music_file"
11020 &setup.internal.default_level_series, "default_level_series"
11024 &setup.internal.default_window_width, "default_window_width"
11028 &setup.internal.default_window_height, "default_window_height"
11032 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
11036 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11040 &setup.internal.create_user_levelset, "create_user_levelset"
11044 &setup.internal.info_screens_from_main, "info_screens_from_main"
11048 &setup.internal.menu_game, "menu_game"
11052 &setup.internal.menu_engines, "menu_engines"
11056 &setup.internal.menu_editor, "menu_editor"
11060 &setup.internal.menu_graphics, "menu_graphics"
11064 &setup.internal.menu_sound, "menu_sound"
11068 &setup.internal.menu_artwork, "menu_artwork"
11072 &setup.internal.menu_input, "menu_input"
11076 &setup.internal.menu_touch, "menu_touch"
11080 &setup.internal.menu_shortcuts, "menu_shortcuts"
11084 &setup.internal.menu_exit, "menu_exit"
11088 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11092 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11096 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11100 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11104 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11108 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11112 &setup.internal.info_title, "info_title"
11116 &setup.internal.info_elements, "info_elements"
11120 &setup.internal.info_music, "info_music"
11124 &setup.internal.info_credits, "info_credits"
11128 &setup.internal.info_program, "info_program"
11132 &setup.internal.info_version, "info_version"
11136 &setup.internal.info_levelset, "info_levelset"
11140 &setup.internal.info_exit, "info_exit"
11144 static struct TokenInfo debug_setup_tokens[] =
11148 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11152 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11156 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11160 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11164 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11168 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11172 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11176 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11180 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11184 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11188 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11192 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11196 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11200 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11204 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11208 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11212 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11216 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11220 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11224 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11228 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11231 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11235 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11239 &setup.debug.xsn_mode, "debug.xsn_mode"
11243 &setup.debug.xsn_percent, "debug.xsn_percent"
11247 static struct TokenInfo options_setup_tokens[] =
11251 &setup.options.verbose, "options.verbose"
11255 &setup.options.debug, "options.debug"
11259 &setup.options.debug_mode, "options.debug_mode"
11263 static void setSetupInfoToDefaults(struct SetupInfo *si)
11267 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11269 si->multiple_users = TRUE;
11272 si->sound_loops = TRUE;
11273 si->sound_music = TRUE;
11274 si->sound_simple = TRUE;
11276 si->global_animations = TRUE;
11277 si->scroll_delay = TRUE;
11278 si->forced_scroll_delay = FALSE;
11279 si->scroll_delay_value = STD_SCROLL_DELAY;
11280 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11281 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11282 si->fade_screens = TRUE;
11283 si->autorecord = TRUE;
11284 si->autorecord_after_replay = TRUE;
11285 si->auto_pause_on_start = FALSE;
11286 si->show_titlescreen = TRUE;
11287 si->quick_doors = FALSE;
11288 si->team_mode = FALSE;
11289 si->handicap = TRUE;
11290 si->skip_levels = TRUE;
11291 si->increment_levels = TRUE;
11292 si->auto_play_next_level = TRUE;
11293 si->count_score_after_game = TRUE;
11294 si->show_scores_after_game = TRUE;
11295 si->time_limit = TRUE;
11296 si->fullscreen = FALSE;
11297 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11298 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11299 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11300 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11301 si->ask_on_escape = TRUE;
11302 si->ask_on_escape_editor = TRUE;
11303 si->ask_on_game_over = TRUE;
11304 si->ask_on_quit_game = TRUE;
11305 si->ask_on_quit_program = TRUE;
11306 si->quick_switch = FALSE;
11307 si->input_on_focus = FALSE;
11308 si->prefer_aga_graphics = TRUE;
11309 si->prefer_lowpass_sounds = FALSE;
11310 si->prefer_extra_panel_items = TRUE;
11311 si->game_speed_extended = FALSE;
11312 si->game_frame_delay = GAME_FRAME_DELAY;
11313 si->bd_skip_uncovering = FALSE;
11314 si->bd_skip_hatching = FALSE;
11315 si->bd_scroll_delay = TRUE;
11316 si->bd_smooth_movements = AUTO;
11317 si->sp_show_border_elements = FALSE;
11318 si->small_game_graphics = FALSE;
11319 si->show_load_save_buttons = FALSE;
11320 si->show_undo_redo_buttons = FALSE;
11321 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11323 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11324 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11325 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11327 si->override_level_graphics = FALSE;
11328 si->override_level_sounds = FALSE;
11329 si->override_level_music = FALSE;
11331 si->volume_simple = 100; // percent
11332 si->volume_loops = 100; // percent
11333 si->volume_music = 100; // percent
11335 si->network_mode = FALSE;
11336 si->network_player_nr = 0; // first player
11337 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11339 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11340 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11341 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11342 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11343 si->touch.draw_outlined = TRUE;
11344 si->touch.draw_pressed = TRUE;
11346 for (i = 0; i < 2; i++)
11348 char *default_grid_button[6][2] =
11354 { "111222", " vv " },
11355 { "111222", " vv " }
11357 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11358 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11359 int min_xsize = MIN(6, grid_xsize);
11360 int min_ysize = MIN(6, grid_ysize);
11361 int startx = grid_xsize - min_xsize;
11362 int starty = grid_ysize - min_ysize;
11365 // virtual buttons grid can only be set to defaults if video is initialized
11366 // (this will be repeated if virtual buttons are not loaded from setup file)
11367 if (video.initialized)
11369 si->touch.grid_xsize[i] = grid_xsize;
11370 si->touch.grid_ysize[i] = grid_ysize;
11374 si->touch.grid_xsize[i] = -1;
11375 si->touch.grid_ysize[i] = -1;
11378 for (x = 0; x < MAX_GRID_XSIZE; x++)
11379 for (y = 0; y < MAX_GRID_YSIZE; y++)
11380 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11382 for (x = 0; x < min_xsize; x++)
11383 for (y = 0; y < min_ysize; y++)
11384 si->touch.grid_button[i][x][starty + y] =
11385 default_grid_button[y][0][x];
11387 for (x = 0; x < min_xsize; x++)
11388 for (y = 0; y < min_ysize; y++)
11389 si->touch.grid_button[i][startx + x][starty + y] =
11390 default_grid_button[y][1][x];
11393 si->touch.grid_initialized = video.initialized;
11395 si->touch.overlay_buttons = FALSE;
11397 si->editor.el_boulderdash = TRUE;
11398 si->editor.el_boulderdash_native = TRUE;
11399 si->editor.el_boulderdash_effects = TRUE;
11400 si->editor.el_emerald_mine = TRUE;
11401 si->editor.el_emerald_mine_club = TRUE;
11402 si->editor.el_more = TRUE;
11403 si->editor.el_sokoban = TRUE;
11404 si->editor.el_supaplex = TRUE;
11405 si->editor.el_diamond_caves = TRUE;
11406 si->editor.el_dx_boulderdash = TRUE;
11408 si->editor.el_mirror_magic = TRUE;
11409 si->editor.el_deflektor = TRUE;
11411 si->editor.el_chars = TRUE;
11412 si->editor.el_steel_chars = TRUE;
11414 si->editor.el_classic = TRUE;
11415 si->editor.el_custom = TRUE;
11417 si->editor.el_user_defined = FALSE;
11418 si->editor.el_dynamic = TRUE;
11420 si->editor.el_headlines = TRUE;
11422 si->editor.show_element_token = FALSE;
11424 si->editor.show_read_only_warning = TRUE;
11426 si->editor.use_template_for_new_levels = TRUE;
11428 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11429 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11430 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11431 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11432 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11434 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11435 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11436 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11437 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11438 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11440 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11441 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11442 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11443 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11444 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11445 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11447 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11448 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11449 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11451 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11452 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11453 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11454 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11456 for (i = 0; i < MAX_PLAYERS; i++)
11458 si->input[i].use_joystick = FALSE;
11459 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11460 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11461 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11462 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11463 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11464 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11465 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11466 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11467 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11468 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11469 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11470 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11471 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11472 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11473 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11476 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11477 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11478 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11479 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11481 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11482 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11483 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11484 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11485 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11486 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11487 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11489 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11491 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11492 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11493 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11495 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11496 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11497 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11499 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11500 si->internal.choose_from_top_leveldir = FALSE;
11501 si->internal.show_scaling_in_title = TRUE;
11502 si->internal.create_user_levelset = TRUE;
11503 si->internal.info_screens_from_main = FALSE;
11505 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11506 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11508 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11509 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11510 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11511 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11512 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11513 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11514 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11515 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11516 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11517 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11519 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11520 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11521 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11522 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11523 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11524 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11525 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11526 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11527 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11528 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11530 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11531 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11533 si->debug.show_frames_per_second = FALSE;
11535 si->debug.xsn_mode = AUTO;
11536 si->debug.xsn_percent = 0;
11538 si->options.verbose = FALSE;
11539 si->options.debug = FALSE;
11540 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11542 #if defined(PLATFORM_ANDROID)
11543 si->fullscreen = TRUE;
11544 si->touch.overlay_buttons = TRUE;
11547 setHideSetupEntry(&setup.debug.xsn_mode);
11550 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11552 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11555 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11557 si->player_uuid = NULL; // (will be set later)
11558 si->player_version = 1; // (will be set later)
11560 si->use_api_server = TRUE;
11561 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11562 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11563 si->ask_for_uploading_tapes = TRUE;
11564 si->ask_for_remaining_tapes = FALSE;
11565 si->provide_uploading_tapes = TRUE;
11566 si->ask_for_using_api_server = TRUE;
11567 si->has_remaining_tapes = FALSE;
11570 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11572 si->editor_cascade.el_bd = TRUE;
11573 si->editor_cascade.el_bd_native = TRUE;
11574 si->editor_cascade.el_bd_effects = FALSE;
11575 si->editor_cascade.el_em = TRUE;
11576 si->editor_cascade.el_emc = TRUE;
11577 si->editor_cascade.el_rnd = TRUE;
11578 si->editor_cascade.el_sb = TRUE;
11579 si->editor_cascade.el_sp = TRUE;
11580 si->editor_cascade.el_dc = TRUE;
11581 si->editor_cascade.el_dx = TRUE;
11583 si->editor_cascade.el_mm = TRUE;
11584 si->editor_cascade.el_df = TRUE;
11586 si->editor_cascade.el_chars = FALSE;
11587 si->editor_cascade.el_steel_chars = FALSE;
11588 si->editor_cascade.el_ce = FALSE;
11589 si->editor_cascade.el_ge = FALSE;
11590 si->editor_cascade.el_es = FALSE;
11591 si->editor_cascade.el_ref = FALSE;
11592 si->editor_cascade.el_user = FALSE;
11593 si->editor_cascade.el_dynamic = FALSE;
11596 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11598 static char *getHideSetupToken(void *setup_value)
11600 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11602 if (setup_value != NULL)
11603 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11605 return hide_setup_token;
11608 void setHideSetupEntry(void *setup_value)
11610 char *hide_setup_token = getHideSetupToken(setup_value);
11612 if (hide_setup_hash == NULL)
11613 hide_setup_hash = newSetupFileHash();
11615 if (setup_value != NULL)
11616 setHashEntry(hide_setup_hash, hide_setup_token, "");
11619 void removeHideSetupEntry(void *setup_value)
11621 char *hide_setup_token = getHideSetupToken(setup_value);
11623 if (setup_value != NULL)
11624 removeHashEntry(hide_setup_hash, hide_setup_token);
11627 boolean hideSetupEntry(void *setup_value)
11629 char *hide_setup_token = getHideSetupToken(setup_value);
11631 return (setup_value != NULL &&
11632 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11635 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11636 struct TokenInfo *token_info,
11637 int token_nr, char *token_text)
11639 char *token_hide_text = getStringCat2(token_text, ".hide");
11640 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11642 // set the value of this setup option in the setup option structure
11643 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11645 // check if this setup option should be hidden in the setup menu
11646 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11647 setHideSetupEntry(token_info[token_nr].value);
11649 free(token_hide_text);
11652 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11653 struct TokenInfo *token_info,
11656 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11657 token_info[token_nr].text);
11660 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11664 if (!setup_file_hash)
11667 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11668 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11670 setup.touch.grid_initialized = TRUE;
11671 for (i = 0; i < 2; i++)
11673 int grid_xsize = setup.touch.grid_xsize[i];
11674 int grid_ysize = setup.touch.grid_ysize[i];
11677 // if virtual buttons are not loaded from setup file, repeat initializing
11678 // virtual buttons grid with default values later when video is initialized
11679 if (grid_xsize == -1 ||
11682 setup.touch.grid_initialized = FALSE;
11687 for (y = 0; y < grid_ysize; y++)
11689 char token_string[MAX_LINE_LEN];
11691 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11693 char *value_string = getHashEntry(setup_file_hash, token_string);
11695 if (value_string == NULL)
11698 for (x = 0; x < grid_xsize; x++)
11700 char c = value_string[x];
11702 setup.touch.grid_button[i][x][y] =
11703 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11708 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11709 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11711 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11712 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11714 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11718 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11720 setup_input = setup.input[pnr];
11721 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11723 char full_token[100];
11725 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11726 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11729 setup.input[pnr] = setup_input;
11732 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11733 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11735 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11736 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11738 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11739 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11741 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11742 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11744 setHideRelatedSetupEntries();
11747 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11751 if (!setup_file_hash)
11754 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11755 setSetupInfo(auto_setup_tokens, i,
11756 getHashEntry(setup_file_hash,
11757 auto_setup_tokens[i].text));
11760 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11764 if (!setup_file_hash)
11767 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11768 setSetupInfo(server_setup_tokens, i,
11769 getHashEntry(setup_file_hash,
11770 server_setup_tokens[i].text));
11773 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11777 if (!setup_file_hash)
11780 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11781 setSetupInfo(editor_cascade_setup_tokens, i,
11782 getHashEntry(setup_file_hash,
11783 editor_cascade_setup_tokens[i].text));
11786 void LoadUserNames(void)
11788 int last_user_nr = user.nr;
11791 if (global.user_names != NULL)
11793 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11794 checked_free(global.user_names[i]);
11796 checked_free(global.user_names);
11799 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11801 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11805 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11807 if (setup_file_hash)
11809 char *player_name = getHashEntry(setup_file_hash, "player_name");
11811 global.user_names[i] = getFixedUserName(player_name);
11813 freeSetupFileHash(setup_file_hash);
11816 if (global.user_names[i] == NULL)
11817 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11820 user.nr = last_user_nr;
11823 void LoadSetupFromFilename(char *filename)
11825 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11827 if (setup_file_hash)
11829 decodeSetupFileHash_Default(setup_file_hash);
11831 freeSetupFileHash(setup_file_hash);
11835 Debug("setup", "using default setup values");
11839 static void LoadSetup_SpecialPostProcessing(void)
11841 char *player_name_new;
11843 // needed to work around problems with fixed length strings
11844 player_name_new = getFixedUserName(setup.player_name);
11845 free(setup.player_name);
11846 setup.player_name = player_name_new;
11848 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11849 if (setup.scroll_delay == FALSE)
11851 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11852 setup.scroll_delay = TRUE; // now always "on"
11855 // make sure that scroll delay value stays inside valid range
11856 setup.scroll_delay_value =
11857 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11860 void LoadSetup_Default(void)
11864 // always start with reliable default values
11865 setSetupInfoToDefaults(&setup);
11867 // try to load setup values from default setup file
11868 filename = getDefaultSetupFilename();
11870 if (fileExists(filename))
11871 LoadSetupFromFilename(filename);
11873 // try to load setup values from platform setup file
11874 filename = getPlatformSetupFilename();
11876 if (fileExists(filename))
11877 LoadSetupFromFilename(filename);
11879 // try to load setup values from user setup file
11880 filename = getSetupFilename();
11882 LoadSetupFromFilename(filename);
11884 LoadSetup_SpecialPostProcessing();
11887 void LoadSetup_AutoSetup(void)
11889 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11890 SetupFileHash *setup_file_hash = NULL;
11892 // always start with reliable default values
11893 setSetupInfoToDefaults_AutoSetup(&setup);
11895 setup_file_hash = loadSetupFileHash(filename);
11897 if (setup_file_hash)
11899 decodeSetupFileHash_AutoSetup(setup_file_hash);
11901 freeSetupFileHash(setup_file_hash);
11907 void LoadSetup_ServerSetup(void)
11909 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11910 SetupFileHash *setup_file_hash = NULL;
11912 // always start with reliable default values
11913 setSetupInfoToDefaults_ServerSetup(&setup);
11915 setup_file_hash = loadSetupFileHash(filename);
11917 if (setup_file_hash)
11919 decodeSetupFileHash_ServerSetup(setup_file_hash);
11921 freeSetupFileHash(setup_file_hash);
11926 if (setup.player_uuid == NULL)
11928 // player UUID does not yet exist in setup file
11929 setup.player_uuid = getStringCopy(getUUID());
11930 setup.player_version = 2;
11932 SaveSetup_ServerSetup();
11936 void LoadSetup_EditorCascade(void)
11938 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11939 SetupFileHash *setup_file_hash = NULL;
11941 // always start with reliable default values
11942 setSetupInfoToDefaults_EditorCascade(&setup);
11944 setup_file_hash = loadSetupFileHash(filename);
11946 if (setup_file_hash)
11948 decodeSetupFileHash_EditorCascade(setup_file_hash);
11950 freeSetupFileHash(setup_file_hash);
11956 void LoadSetup(void)
11958 LoadSetup_Default();
11959 LoadSetup_AutoSetup();
11960 LoadSetup_ServerSetup();
11961 LoadSetup_EditorCascade();
11964 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11965 char *mapping_line)
11967 char mapping_guid[MAX_LINE_LEN];
11968 char *mapping_start, *mapping_end;
11970 // get GUID from game controller mapping line: copy complete line
11971 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11972 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11974 // get GUID from game controller mapping line: cut after GUID part
11975 mapping_start = strchr(mapping_guid, ',');
11976 if (mapping_start != NULL)
11977 *mapping_start = '\0';
11979 // cut newline from game controller mapping line
11980 mapping_end = strchr(mapping_line, '\n');
11981 if (mapping_end != NULL)
11982 *mapping_end = '\0';
11984 // add mapping entry to game controller mappings hash
11985 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11988 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11993 if (!(file = fopen(filename, MODE_READ)))
11995 Warn("cannot read game controller mappings file '%s'", filename);
12000 while (!feof(file))
12002 char line[MAX_LINE_LEN];
12004 if (!fgets(line, MAX_LINE_LEN, file))
12007 addGameControllerMappingToHash(mappings_hash, line);
12013 void SaveSetup_Default(void)
12015 char *filename = getSetupFilename();
12019 InitUserDataDirectory();
12021 if (!(file = fopen(filename, MODE_WRITE)))
12023 Warn("cannot write setup file '%s'", filename);
12028 fprintFileHeader(file, SETUP_FILENAME);
12030 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12032 // just to make things nicer :)
12033 if (global_setup_tokens[i].value == &setup.multiple_users ||
12034 global_setup_tokens[i].value == &setup.sound ||
12035 global_setup_tokens[i].value == &setup.graphics_set ||
12036 global_setup_tokens[i].value == &setup.volume_simple ||
12037 global_setup_tokens[i].value == &setup.network_mode ||
12038 global_setup_tokens[i].value == &setup.touch.control_type ||
12039 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12040 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12041 fprintf(file, "\n");
12043 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12046 for (i = 0; i < 2; i++)
12048 int grid_xsize = setup.touch.grid_xsize[i];
12049 int grid_ysize = setup.touch.grid_ysize[i];
12052 fprintf(file, "\n");
12054 for (y = 0; y < grid_ysize; y++)
12056 char token_string[MAX_LINE_LEN];
12057 char value_string[MAX_LINE_LEN];
12059 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12061 for (x = 0; x < grid_xsize; x++)
12063 char c = setup.touch.grid_button[i][x][y];
12065 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12068 value_string[grid_xsize] = '\0';
12070 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12074 fprintf(file, "\n");
12075 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12076 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12078 fprintf(file, "\n");
12079 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12080 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12082 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12086 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12087 fprintf(file, "\n");
12089 setup_input = setup.input[pnr];
12090 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12091 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12094 fprintf(file, "\n");
12095 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12096 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12098 // (internal setup values not saved to user setup file)
12100 fprintf(file, "\n");
12101 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12102 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12103 setup.debug.xsn_mode != AUTO)
12104 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12106 fprintf(file, "\n");
12107 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12108 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12112 SetFilePermissions(filename, PERMS_PRIVATE);
12115 void SaveSetup_AutoSetup(void)
12117 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12121 InitUserDataDirectory();
12123 if (!(file = fopen(filename, MODE_WRITE)))
12125 Warn("cannot write auto setup file '%s'", filename);
12132 fprintFileHeader(file, AUTOSETUP_FILENAME);
12134 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12135 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12139 SetFilePermissions(filename, PERMS_PRIVATE);
12144 void SaveSetup_ServerSetup(void)
12146 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12150 InitUserDataDirectory();
12152 if (!(file = fopen(filename, MODE_WRITE)))
12154 Warn("cannot write server setup file '%s'", filename);
12161 fprintFileHeader(file, SERVERSETUP_FILENAME);
12163 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12165 // just to make things nicer :)
12166 if (server_setup_tokens[i].value == &setup.use_api_server)
12167 fprintf(file, "\n");
12169 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12174 SetFilePermissions(filename, PERMS_PRIVATE);
12179 void SaveSetup_EditorCascade(void)
12181 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12185 InitUserDataDirectory();
12187 if (!(file = fopen(filename, MODE_WRITE)))
12189 Warn("cannot write editor cascade state file '%s'", filename);
12196 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12198 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12199 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12203 SetFilePermissions(filename, PERMS_PRIVATE);
12208 void SaveSetup(void)
12210 SaveSetup_Default();
12211 SaveSetup_AutoSetup();
12212 SaveSetup_ServerSetup();
12213 SaveSetup_EditorCascade();
12216 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12221 if (!(file = fopen(filename, MODE_WRITE)))
12223 Warn("cannot write game controller mappings file '%s'", filename);
12228 BEGIN_HASH_ITERATION(mappings_hash, itr)
12230 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12232 END_HASH_ITERATION(mappings_hash, itr)
12237 void SaveSetup_AddGameControllerMapping(char *mapping)
12239 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12240 SetupFileHash *mappings_hash = newSetupFileHash();
12242 InitUserDataDirectory();
12244 // load existing personal game controller mappings
12245 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12247 // add new mapping to personal game controller mappings
12248 addGameControllerMappingToHash(mappings_hash, mapping);
12250 // save updated personal game controller mappings
12251 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12253 freeSetupFileHash(mappings_hash);
12257 void LoadCustomElementDescriptions(void)
12259 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12260 SetupFileHash *setup_file_hash;
12263 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12265 if (element_info[i].custom_description != NULL)
12267 free(element_info[i].custom_description);
12268 element_info[i].custom_description = NULL;
12272 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12275 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12277 char *token = getStringCat2(element_info[i].token_name, ".name");
12278 char *value = getHashEntry(setup_file_hash, token);
12281 element_info[i].custom_description = getStringCopy(value);
12286 freeSetupFileHash(setup_file_hash);
12289 static int getElementFromToken(char *token)
12291 char *value = getHashEntry(element_token_hash, token);
12294 return atoi(value);
12296 Warn("unknown element token '%s'", token);
12298 return EL_UNDEFINED;
12301 void FreeGlobalAnimEventInfo(void)
12303 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12305 if (gaei->event_list == NULL)
12310 for (i = 0; i < gaei->num_event_lists; i++)
12312 checked_free(gaei->event_list[i]->event_value);
12313 checked_free(gaei->event_list[i]);
12316 checked_free(gaei->event_list);
12318 gaei->event_list = NULL;
12319 gaei->num_event_lists = 0;
12322 static int AddGlobalAnimEventList(void)
12324 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12325 int list_pos = gaei->num_event_lists++;
12327 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12328 sizeof(struct GlobalAnimEventListInfo *));
12330 gaei->event_list[list_pos] =
12331 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12333 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12335 gaeli->event_value = NULL;
12336 gaeli->num_event_values = 0;
12341 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12343 // do not add empty global animation events
12344 if (event_value == ANIM_EVENT_NONE)
12347 // if list position is undefined, create new list
12348 if (list_pos == ANIM_EVENT_UNDEFINED)
12349 list_pos = AddGlobalAnimEventList();
12351 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12352 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12353 int value_pos = gaeli->num_event_values++;
12355 gaeli->event_value = checked_realloc(gaeli->event_value,
12356 gaeli->num_event_values * sizeof(int *));
12358 gaeli->event_value[value_pos] = event_value;
12363 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12365 if (list_pos == ANIM_EVENT_UNDEFINED)
12366 return ANIM_EVENT_NONE;
12368 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12369 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12371 return gaeli->event_value[value_pos];
12374 int GetGlobalAnimEventValueCount(int list_pos)
12376 if (list_pos == ANIM_EVENT_UNDEFINED)
12379 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12380 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12382 return gaeli->num_event_values;
12385 // This function checks if a string <s> of the format "string1, string2, ..."
12386 // exactly contains a string <s_contained>.
12388 static boolean string_has_parameter(char *s, char *s_contained)
12392 if (s == NULL || s_contained == NULL)
12395 if (strlen(s_contained) > strlen(s))
12398 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12400 char next_char = s[strlen(s_contained)];
12402 // check if next character is delimiter or whitespace
12403 if (next_char == ',' || next_char == '\0' ||
12404 next_char == ' ' || next_char == '\t')
12408 // check if string contains another parameter string after a comma
12409 substring = strchr(s, ',');
12410 if (substring == NULL) // string does not contain a comma
12413 // advance string pointer to next character after the comma
12416 // skip potential whitespaces after the comma
12417 while (*substring == ' ' || *substring == '\t')
12420 return string_has_parameter(substring, s_contained);
12423 static int get_anim_parameter_value_ce(char *s)
12426 char *pattern_1 = "ce_change:custom_";
12427 char *pattern_2 = ".page_";
12428 int pattern_1_len = strlen(pattern_1);
12429 char *matching_char = strstr(s_ptr, pattern_1);
12430 int result = ANIM_EVENT_NONE;
12432 if (matching_char == NULL)
12433 return ANIM_EVENT_NONE;
12435 result = ANIM_EVENT_CE_CHANGE;
12437 s_ptr = matching_char + pattern_1_len;
12439 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12440 if (*s_ptr >= '0' && *s_ptr <= '9')
12442 int gic_ce_nr = (*s_ptr++ - '0');
12444 if (*s_ptr >= '0' && *s_ptr <= '9')
12446 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12448 if (*s_ptr >= '0' && *s_ptr <= '9')
12449 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12452 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12453 return ANIM_EVENT_NONE;
12455 // custom element stored as 0 to 255
12458 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12462 // invalid custom element number specified
12464 return ANIM_EVENT_NONE;
12467 // check for change page number ("page_X" or "page_XX") (optional)
12468 if (strPrefix(s_ptr, pattern_2))
12470 s_ptr += strlen(pattern_2);
12472 if (*s_ptr >= '0' && *s_ptr <= '9')
12474 int gic_page_nr = (*s_ptr++ - '0');
12476 if (*s_ptr >= '0' && *s_ptr <= '9')
12477 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12479 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12480 return ANIM_EVENT_NONE;
12482 // change page stored as 1 to 32 (0 means "all change pages")
12484 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12488 // invalid animation part number specified
12490 return ANIM_EVENT_NONE;
12494 // discard result if next character is neither delimiter nor whitespace
12495 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12496 *s_ptr == ' ' || *s_ptr == '\t'))
12497 return ANIM_EVENT_NONE;
12502 static int get_anim_parameter_value(char *s)
12504 int event_value[] =
12512 char *pattern_1[] =
12520 char *pattern_2 = ".part_";
12521 char *matching_char = NULL;
12523 int pattern_1_len = 0;
12524 int result = ANIM_EVENT_NONE;
12527 result = get_anim_parameter_value_ce(s);
12529 if (result != ANIM_EVENT_NONE)
12532 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12534 matching_char = strstr(s_ptr, pattern_1[i]);
12535 pattern_1_len = strlen(pattern_1[i]);
12536 result = event_value[i];
12538 if (matching_char != NULL)
12542 if (matching_char == NULL)
12543 return ANIM_EVENT_NONE;
12545 s_ptr = matching_char + pattern_1_len;
12547 // check for main animation number ("anim_X" or "anim_XX")
12548 if (*s_ptr >= '0' && *s_ptr <= '9')
12550 int gic_anim_nr = (*s_ptr++ - '0');
12552 if (*s_ptr >= '0' && *s_ptr <= '9')
12553 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12555 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12556 return ANIM_EVENT_NONE;
12558 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12562 // invalid main animation number specified
12564 return ANIM_EVENT_NONE;
12567 // check for animation part number ("part_X" or "part_XX") (optional)
12568 if (strPrefix(s_ptr, pattern_2))
12570 s_ptr += strlen(pattern_2);
12572 if (*s_ptr >= '0' && *s_ptr <= '9')
12574 int gic_part_nr = (*s_ptr++ - '0');
12576 if (*s_ptr >= '0' && *s_ptr <= '9')
12577 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12579 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12580 return ANIM_EVENT_NONE;
12582 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12586 // invalid animation part number specified
12588 return ANIM_EVENT_NONE;
12592 // discard result if next character is neither delimiter nor whitespace
12593 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12594 *s_ptr == ' ' || *s_ptr == '\t'))
12595 return ANIM_EVENT_NONE;
12600 static int get_anim_parameter_values(char *s)
12602 int list_pos = ANIM_EVENT_UNDEFINED;
12603 int event_value = ANIM_EVENT_DEFAULT;
12605 if (string_has_parameter(s, "any"))
12606 event_value |= ANIM_EVENT_ANY;
12608 if (string_has_parameter(s, "click:self") ||
12609 string_has_parameter(s, "click") ||
12610 string_has_parameter(s, "self"))
12611 event_value |= ANIM_EVENT_SELF;
12613 if (string_has_parameter(s, "unclick:any"))
12614 event_value |= ANIM_EVENT_UNCLICK_ANY;
12616 // if animation event found, add it to global animation event list
12617 if (event_value != ANIM_EVENT_NONE)
12618 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12622 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12623 event_value = get_anim_parameter_value(s);
12625 // if animation event found, add it to global animation event list
12626 if (event_value != ANIM_EVENT_NONE)
12627 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12629 // continue with next part of the string, starting with next comma
12630 s = strchr(s + 1, ',');
12636 static int get_anim_action_parameter_value(char *token)
12638 // check most common default case first to massively speed things up
12639 if (strEqual(token, ARG_UNDEFINED))
12640 return ANIM_EVENT_ACTION_NONE;
12642 int result = getImageIDFromToken(token);
12646 char *gfx_token = getStringCat2("gfx.", token);
12648 result = getImageIDFromToken(gfx_token);
12650 checked_free(gfx_token);
12655 Key key = getKeyFromX11KeyName(token);
12657 if (key != KSYM_UNDEFINED)
12658 result = -(int)key;
12665 result = get_hash_from_string(token); // unsigned int => int
12666 result = ABS(result); // may be negative now
12667 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12669 setHashEntry(anim_url_hash, int2str(result, 0), token);
12674 result = ANIM_EVENT_ACTION_NONE;
12679 int get_parameter_value(char *value_raw, char *suffix, int type)
12681 char *value = getStringToLower(value_raw);
12682 int result = 0; // probably a save default value
12684 if (strEqual(suffix, ".direction"))
12686 result = (strEqual(value, "left") ? MV_LEFT :
12687 strEqual(value, "right") ? MV_RIGHT :
12688 strEqual(value, "up") ? MV_UP :
12689 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12691 else if (strEqual(suffix, ".position"))
12693 result = (strEqual(value, "left") ? POS_LEFT :
12694 strEqual(value, "right") ? POS_RIGHT :
12695 strEqual(value, "top") ? POS_TOP :
12696 strEqual(value, "upper") ? POS_UPPER :
12697 strEqual(value, "middle") ? POS_MIDDLE :
12698 strEqual(value, "lower") ? POS_LOWER :
12699 strEqual(value, "bottom") ? POS_BOTTOM :
12700 strEqual(value, "any") ? POS_ANY :
12701 strEqual(value, "ce") ? POS_CE :
12702 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12703 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12705 else if (strEqual(suffix, ".align"))
12707 result = (strEqual(value, "left") ? ALIGN_LEFT :
12708 strEqual(value, "right") ? ALIGN_RIGHT :
12709 strEqual(value, "center") ? ALIGN_CENTER :
12710 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12712 else if (strEqual(suffix, ".valign"))
12714 result = (strEqual(value, "top") ? VALIGN_TOP :
12715 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12716 strEqual(value, "middle") ? VALIGN_MIDDLE :
12717 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12719 else if (strEqual(suffix, ".anim_mode"))
12721 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12722 string_has_parameter(value, "loop") ? ANIM_LOOP :
12723 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12724 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12725 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12726 string_has_parameter(value, "random") ? ANIM_RANDOM :
12727 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12728 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12729 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12730 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12731 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12732 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12733 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12734 string_has_parameter(value, "all") ? ANIM_ALL :
12735 string_has_parameter(value, "tiled") ? ANIM_TILED :
12736 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12739 if (string_has_parameter(value, "once"))
12740 result |= ANIM_ONCE;
12742 if (string_has_parameter(value, "reverse"))
12743 result |= ANIM_REVERSE;
12745 if (string_has_parameter(value, "opaque_player"))
12746 result |= ANIM_OPAQUE_PLAYER;
12748 if (string_has_parameter(value, "static_panel"))
12749 result |= ANIM_STATIC_PANEL;
12751 else if (strEqual(suffix, ".init_event") ||
12752 strEqual(suffix, ".anim_event"))
12754 result = get_anim_parameter_values(value);
12756 else if (strEqual(suffix, ".init_delay_action") ||
12757 strEqual(suffix, ".anim_delay_action") ||
12758 strEqual(suffix, ".post_delay_action") ||
12759 strEqual(suffix, ".init_event_action") ||
12760 strEqual(suffix, ".anim_event_action"))
12762 result = get_anim_action_parameter_value(value_raw);
12764 else if (strEqual(suffix, ".class"))
12766 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12767 get_hash_from_string(value));
12769 else if (strEqual(suffix, ".style"))
12771 result = STYLE_DEFAULT;
12773 if (string_has_parameter(value, "accurate_borders"))
12774 result |= STYLE_ACCURATE_BORDERS;
12776 if (string_has_parameter(value, "inner_corners"))
12777 result |= STYLE_INNER_CORNERS;
12779 if (string_has_parameter(value, "reverse"))
12780 result |= STYLE_REVERSE;
12782 if (string_has_parameter(value, "leftmost_position"))
12783 result |= STYLE_LEFTMOST_POSITION;
12785 if (string_has_parameter(value, "block_clicks"))
12786 result |= STYLE_BLOCK;
12788 if (string_has_parameter(value, "passthrough_clicks"))
12789 result |= STYLE_PASSTHROUGH;
12791 if (string_has_parameter(value, "multiple_actions"))
12792 result |= STYLE_MULTIPLE_ACTIONS;
12794 if (string_has_parameter(value, "consume_ce_event"))
12795 result |= STYLE_CONSUME_CE_EVENT;
12797 else if (strEqual(suffix, ".fade_mode"))
12799 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12800 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12801 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12802 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12803 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12804 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12805 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12806 FADE_MODE_DEFAULT);
12808 else if (strEqual(suffix, ".auto_delay_unit"))
12810 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12811 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12812 AUTO_DELAY_UNIT_DEFAULT);
12814 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12816 result = gfx.get_font_from_token_function(value);
12818 else // generic parameter of type integer or boolean
12820 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12821 type == TYPE_INTEGER ? get_integer_from_string(value) :
12822 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12823 ARG_UNDEFINED_VALUE);
12831 static int get_token_parameter_value(char *token, char *value_raw)
12835 if (token == NULL || value_raw == NULL)
12836 return ARG_UNDEFINED_VALUE;
12838 suffix = strrchr(token, '.');
12839 if (suffix == NULL)
12842 if (strEqual(suffix, ".element"))
12843 return getElementFromToken(value_raw);
12845 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12846 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12849 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12850 boolean ignore_defaults)
12854 for (i = 0; image_config_vars[i].token != NULL; i++)
12856 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12858 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12859 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12863 *image_config_vars[i].value =
12864 get_token_parameter_value(image_config_vars[i].token, value);
12868 void InitMenuDesignSettings_Static(void)
12870 // always start with reliable default values from static default config
12871 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12874 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12878 // the following initializes hierarchical values from static configuration
12880 // special case: initialize "ARG_DEFAULT" values in static default config
12881 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12882 titlescreen_initial_first_default.fade_mode =
12883 title_initial_first_default.fade_mode;
12884 titlescreen_initial_first_default.fade_delay =
12885 title_initial_first_default.fade_delay;
12886 titlescreen_initial_first_default.post_delay =
12887 title_initial_first_default.post_delay;
12888 titlescreen_initial_first_default.auto_delay =
12889 title_initial_first_default.auto_delay;
12890 titlescreen_initial_first_default.auto_delay_unit =
12891 title_initial_first_default.auto_delay_unit;
12892 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12893 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12894 titlescreen_first_default.post_delay = title_first_default.post_delay;
12895 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12896 titlescreen_first_default.auto_delay_unit =
12897 title_first_default.auto_delay_unit;
12898 titlemessage_initial_first_default.fade_mode =
12899 title_initial_first_default.fade_mode;
12900 titlemessage_initial_first_default.fade_delay =
12901 title_initial_first_default.fade_delay;
12902 titlemessage_initial_first_default.post_delay =
12903 title_initial_first_default.post_delay;
12904 titlemessage_initial_first_default.auto_delay =
12905 title_initial_first_default.auto_delay;
12906 titlemessage_initial_first_default.auto_delay_unit =
12907 title_initial_first_default.auto_delay_unit;
12908 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12909 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12910 titlemessage_first_default.post_delay = title_first_default.post_delay;
12911 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12912 titlemessage_first_default.auto_delay_unit =
12913 title_first_default.auto_delay_unit;
12915 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12916 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12917 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12918 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12919 titlescreen_initial_default.auto_delay_unit =
12920 title_initial_default.auto_delay_unit;
12921 titlescreen_default.fade_mode = title_default.fade_mode;
12922 titlescreen_default.fade_delay = title_default.fade_delay;
12923 titlescreen_default.post_delay = title_default.post_delay;
12924 titlescreen_default.auto_delay = title_default.auto_delay;
12925 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12926 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12927 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12928 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12929 titlemessage_initial_default.auto_delay_unit =
12930 title_initial_default.auto_delay_unit;
12931 titlemessage_default.fade_mode = title_default.fade_mode;
12932 titlemessage_default.fade_delay = title_default.fade_delay;
12933 titlemessage_default.post_delay = title_default.post_delay;
12934 titlemessage_default.auto_delay = title_default.auto_delay;
12935 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12937 // special case: initialize "ARG_DEFAULT" values in static default config
12938 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12939 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12941 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12942 titlescreen_first[i] = titlescreen_first_default;
12943 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12944 titlemessage_first[i] = titlemessage_first_default;
12946 titlescreen_initial[i] = titlescreen_initial_default;
12947 titlescreen[i] = titlescreen_default;
12948 titlemessage_initial[i] = titlemessage_initial_default;
12949 titlemessage[i] = titlemessage_default;
12952 // special case: initialize "ARG_DEFAULT" values in static default config
12953 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12954 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12956 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12959 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12960 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12961 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12964 // special case: initialize "ARG_DEFAULT" values in static default config
12965 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12966 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12968 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12969 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12970 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12972 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12975 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12979 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12983 struct XY *dst, *src;
12985 game_buttons_xy[] =
12987 { &game.button.save, &game.button.stop },
12988 { &game.button.pause2, &game.button.pause },
12989 { &game.button.load, &game.button.play },
12990 { &game.button.undo, &game.button.stop },
12991 { &game.button.redo, &game.button.play },
12997 // special case: initialize later added SETUP list size from LEVELS value
12998 if (menu.list_size[GAME_MODE_SETUP] == -1)
12999 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13001 // set default position for snapshot buttons to stop/pause/play buttons
13002 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13003 if ((*game_buttons_xy[i].dst).x == -1 &&
13004 (*game_buttons_xy[i].dst).y == -1)
13005 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13007 // --------------------------------------------------------------------------
13008 // dynamic viewports (including playfield margins, borders and alignments)
13009 // --------------------------------------------------------------------------
13011 // dynamic viewports currently only supported for landscape mode
13012 int display_width = MAX(video.display_width, video.display_height);
13013 int display_height = MIN(video.display_width, video.display_height);
13015 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13017 struct RectWithBorder *vp_window = &viewport.window[i];
13018 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13019 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
13020 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
13021 boolean dynamic_window_width = (vp_window->min_width != -1);
13022 boolean dynamic_window_height = (vp_window->min_height != -1);
13023 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
13024 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13026 // adjust window size if min/max width/height is specified
13028 if (vp_window->min_width != -1)
13030 int window_width = display_width;
13032 // when using static window height, use aspect ratio of display
13033 if (vp_window->min_height == -1)
13034 window_width = vp_window->height * display_width / display_height;
13036 vp_window->width = MAX(vp_window->min_width, window_width);
13039 if (vp_window->min_height != -1)
13041 int window_height = display_height;
13043 // when using static window width, use aspect ratio of display
13044 if (vp_window->min_width == -1)
13045 window_height = vp_window->width * display_height / display_width;
13047 vp_window->height = MAX(vp_window->min_height, window_height);
13050 if (vp_window->max_width != -1)
13051 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13053 if (vp_window->max_height != -1)
13054 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13056 int playfield_width = vp_window->width;
13057 int playfield_height = vp_window->height;
13059 // adjust playfield size and position according to specified margins
13061 playfield_width -= vp_playfield->margin_left;
13062 playfield_width -= vp_playfield->margin_right;
13064 playfield_height -= vp_playfield->margin_top;
13065 playfield_height -= vp_playfield->margin_bottom;
13067 // adjust playfield size if min/max width/height is specified
13069 if (vp_playfield->min_width != -1)
13070 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13072 if (vp_playfield->min_height != -1)
13073 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13075 if (vp_playfield->max_width != -1)
13076 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13078 if (vp_playfield->max_height != -1)
13079 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13081 // adjust playfield position according to specified alignment
13083 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13084 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13085 else if (vp_playfield->align == ALIGN_CENTER)
13086 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13087 else if (vp_playfield->align == ALIGN_RIGHT)
13088 vp_playfield->x += playfield_width - vp_playfield->width;
13090 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13091 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13092 else if (vp_playfield->valign == VALIGN_MIDDLE)
13093 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13094 else if (vp_playfield->valign == VALIGN_BOTTOM)
13095 vp_playfield->y += playfield_height - vp_playfield->height;
13097 vp_playfield->x += vp_playfield->margin_left;
13098 vp_playfield->y += vp_playfield->margin_top;
13100 // adjust individual playfield borders if only default border is specified
13102 if (vp_playfield->border_left == -1)
13103 vp_playfield->border_left = vp_playfield->border_size;
13104 if (vp_playfield->border_right == -1)
13105 vp_playfield->border_right = vp_playfield->border_size;
13106 if (vp_playfield->border_top == -1)
13107 vp_playfield->border_top = vp_playfield->border_size;
13108 if (vp_playfield->border_bottom == -1)
13109 vp_playfield->border_bottom = vp_playfield->border_size;
13111 // set dynamic playfield borders if borders are specified as undefined
13112 // (but only if window size was dynamic and playfield size was static)
13114 if (dynamic_window_width && !dynamic_playfield_width)
13116 if (vp_playfield->border_left == -1)
13118 vp_playfield->border_left = (vp_playfield->x -
13119 vp_playfield->margin_left);
13120 vp_playfield->x -= vp_playfield->border_left;
13121 vp_playfield->width += vp_playfield->border_left;
13124 if (vp_playfield->border_right == -1)
13126 vp_playfield->border_right = (vp_window->width -
13128 vp_playfield->width -
13129 vp_playfield->margin_right);
13130 vp_playfield->width += vp_playfield->border_right;
13134 if (dynamic_window_height && !dynamic_playfield_height)
13136 if (vp_playfield->border_top == -1)
13138 vp_playfield->border_top = (vp_playfield->y -
13139 vp_playfield->margin_top);
13140 vp_playfield->y -= vp_playfield->border_top;
13141 vp_playfield->height += vp_playfield->border_top;
13144 if (vp_playfield->border_bottom == -1)
13146 vp_playfield->border_bottom = (vp_window->height -
13148 vp_playfield->height -
13149 vp_playfield->margin_bottom);
13150 vp_playfield->height += vp_playfield->border_bottom;
13154 // adjust playfield size to be a multiple of a defined alignment tile size
13156 int align_size = vp_playfield->align_size;
13157 int playfield_xtiles = vp_playfield->width / align_size;
13158 int playfield_ytiles = vp_playfield->height / align_size;
13159 int playfield_width_corrected = playfield_xtiles * align_size;
13160 int playfield_height_corrected = playfield_ytiles * align_size;
13161 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13162 i == GFX_SPECIAL_ARG_EDITOR);
13164 if (is_playfield_mode &&
13165 dynamic_playfield_width &&
13166 vp_playfield->width != playfield_width_corrected)
13168 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13170 vp_playfield->width = playfield_width_corrected;
13172 if (vp_playfield->align == ALIGN_LEFT)
13174 vp_playfield->border_left += playfield_xdiff;
13176 else if (vp_playfield->align == ALIGN_RIGHT)
13178 vp_playfield->border_right += playfield_xdiff;
13180 else if (vp_playfield->align == ALIGN_CENTER)
13182 int border_left_diff = playfield_xdiff / 2;
13183 int border_right_diff = playfield_xdiff - border_left_diff;
13185 vp_playfield->border_left += border_left_diff;
13186 vp_playfield->border_right += border_right_diff;
13190 if (is_playfield_mode &&
13191 dynamic_playfield_height &&
13192 vp_playfield->height != playfield_height_corrected)
13194 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13196 vp_playfield->height = playfield_height_corrected;
13198 if (vp_playfield->valign == VALIGN_TOP)
13200 vp_playfield->border_top += playfield_ydiff;
13202 else if (vp_playfield->align == VALIGN_BOTTOM)
13204 vp_playfield->border_right += playfield_ydiff;
13206 else if (vp_playfield->align == VALIGN_MIDDLE)
13208 int border_top_diff = playfield_ydiff / 2;
13209 int border_bottom_diff = playfield_ydiff - border_top_diff;
13211 vp_playfield->border_top += border_top_diff;
13212 vp_playfield->border_bottom += border_bottom_diff;
13216 // adjust door positions according to specified alignment
13218 for (j = 0; j < 2; j++)
13220 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13222 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13223 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13224 else if (vp_door->align == ALIGN_CENTER)
13225 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13226 else if (vp_door->align == ALIGN_RIGHT)
13227 vp_door->x += vp_window->width - vp_door->width;
13229 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13230 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13231 else if (vp_door->valign == VALIGN_MIDDLE)
13232 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13233 else if (vp_door->valign == VALIGN_BOTTOM)
13234 vp_door->y += vp_window->height - vp_door->height;
13239 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13243 struct XYTileSize *dst, *src;
13246 editor_buttons_xy[] =
13249 &editor.button.element_left, &editor.palette.element_left,
13250 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13253 &editor.button.element_middle, &editor.palette.element_middle,
13254 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13257 &editor.button.element_right, &editor.palette.element_right,
13258 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13265 // set default position for element buttons to element graphics
13266 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13268 if ((*editor_buttons_xy[i].dst).x == -1 &&
13269 (*editor_buttons_xy[i].dst).y == -1)
13271 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13273 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13275 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13279 // adjust editor palette rows and columns if specified to be dynamic
13281 if (editor.palette.cols == -1)
13283 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13284 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13285 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13287 editor.palette.cols = (vp_width - sc_width) / bt_width;
13289 if (editor.palette.x == -1)
13291 int palette_width = editor.palette.cols * bt_width + sc_width;
13293 editor.palette.x = (vp_width - palette_width) / 2;
13297 if (editor.palette.rows == -1)
13299 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13300 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13301 int tx_height = getFontHeight(FONT_TEXT_2);
13303 editor.palette.rows = (vp_height - tx_height) / bt_height;
13305 if (editor.palette.y == -1)
13307 int palette_height = editor.palette.rows * bt_height + tx_height;
13309 editor.palette.y = (vp_height - palette_height) / 2;
13314 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13315 boolean initialize)
13317 // special case: check if network and preview player positions are redefined,
13318 // to compare this later against the main menu level preview being redefined
13319 struct TokenIntPtrInfo menu_config_players[] =
13321 { "main.network_players.x", &menu.main.network_players.redefined },
13322 { "main.network_players.y", &menu.main.network_players.redefined },
13323 { "main.preview_players.x", &menu.main.preview_players.redefined },
13324 { "main.preview_players.y", &menu.main.preview_players.redefined },
13325 { "preview.x", &preview.redefined },
13326 { "preview.y", &preview.redefined }
13332 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13333 *menu_config_players[i].value = FALSE;
13337 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13338 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13339 *menu_config_players[i].value = TRUE;
13343 static void InitMenuDesignSettings_PreviewPlayers(void)
13345 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13348 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13350 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13353 static void LoadMenuDesignSettingsFromFilename(char *filename)
13355 static struct TitleFadingInfo tfi;
13356 static struct TitleMessageInfo tmi;
13357 static struct TokenInfo title_tokens[] =
13359 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13360 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13361 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13362 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13363 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13367 static struct TokenInfo titlemessage_tokens[] =
13369 { TYPE_INTEGER, &tmi.x, ".x" },
13370 { TYPE_INTEGER, &tmi.y, ".y" },
13371 { TYPE_INTEGER, &tmi.width, ".width" },
13372 { TYPE_INTEGER, &tmi.height, ".height" },
13373 { TYPE_INTEGER, &tmi.chars, ".chars" },
13374 { TYPE_INTEGER, &tmi.lines, ".lines" },
13375 { TYPE_INTEGER, &tmi.align, ".align" },
13376 { TYPE_INTEGER, &tmi.valign, ".valign" },
13377 { TYPE_INTEGER, &tmi.font, ".font" },
13378 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13379 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13380 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13381 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13382 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13383 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13384 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13385 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13386 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13392 struct TitleFadingInfo *info;
13397 // initialize first titles from "enter screen" definitions, if defined
13398 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13399 { &title_first_default, "menu.enter_screen.TITLE" },
13401 // initialize title screens from "next screen" definitions, if defined
13402 { &title_initial_default, "menu.next_screen.TITLE" },
13403 { &title_default, "menu.next_screen.TITLE" },
13409 struct TitleMessageInfo *array;
13412 titlemessage_arrays[] =
13414 // initialize first titles from "enter screen" definitions, if defined
13415 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13416 { titlescreen_first, "menu.enter_screen.TITLE" },
13417 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13418 { titlemessage_first, "menu.enter_screen.TITLE" },
13420 // initialize titles from "next screen" definitions, if defined
13421 { titlescreen_initial, "menu.next_screen.TITLE" },
13422 { titlescreen, "menu.next_screen.TITLE" },
13423 { titlemessage_initial, "menu.next_screen.TITLE" },
13424 { titlemessage, "menu.next_screen.TITLE" },
13426 // overwrite titles with title definitions, if defined
13427 { titlescreen_initial_first, "[title_initial]" },
13428 { titlescreen_first, "[title]" },
13429 { titlemessage_initial_first, "[title_initial]" },
13430 { titlemessage_first, "[title]" },
13432 { titlescreen_initial, "[title_initial]" },
13433 { titlescreen, "[title]" },
13434 { titlemessage_initial, "[title_initial]" },
13435 { titlemessage, "[title]" },
13437 // overwrite titles with title screen/message definitions, if defined
13438 { titlescreen_initial_first, "[titlescreen_initial]" },
13439 { titlescreen_first, "[titlescreen]" },
13440 { titlemessage_initial_first, "[titlemessage_initial]" },
13441 { titlemessage_first, "[titlemessage]" },
13443 { titlescreen_initial, "[titlescreen_initial]" },
13444 { titlescreen, "[titlescreen]" },
13445 { titlemessage_initial, "[titlemessage_initial]" },
13446 { titlemessage, "[titlemessage]" },
13450 SetupFileHash *setup_file_hash;
13453 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13456 // the following initializes hierarchical values from dynamic configuration
13458 // special case: initialize with default values that may be overwritten
13459 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13460 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13462 struct TokenIntPtrInfo menu_config[] =
13464 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13465 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13466 { "menu.list_size", &menu.list_size[i] }
13469 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13471 char *token = menu_config[j].token;
13472 char *value = getHashEntry(setup_file_hash, token);
13475 *menu_config[j].value = get_integer_from_string(value);
13479 // special case: initialize with default values that may be overwritten
13480 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13481 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13483 struct TokenIntPtrInfo menu_config[] =
13485 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13486 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13487 { "menu.list_size.INFO", &menu.list_size_info[i] },
13488 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13489 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13492 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13494 char *token = menu_config[j].token;
13495 char *value = getHashEntry(setup_file_hash, token);
13498 *menu_config[j].value = get_integer_from_string(value);
13502 // special case: initialize with default values that may be overwritten
13503 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13504 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13506 struct TokenIntPtrInfo menu_config[] =
13508 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13509 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13512 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13514 char *token = menu_config[j].token;
13515 char *value = getHashEntry(setup_file_hash, token);
13518 *menu_config[j].value = get_integer_from_string(value);
13522 // special case: initialize with default values that may be overwritten
13523 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13524 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13526 struct TokenIntPtrInfo menu_config[] =
13528 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13529 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13530 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13531 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13532 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13533 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13534 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13535 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13536 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13537 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13540 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13542 char *token = menu_config[j].token;
13543 char *value = getHashEntry(setup_file_hash, token);
13546 *menu_config[j].value = get_integer_from_string(value);
13550 // special case: initialize with default values that may be overwritten
13551 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13552 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13554 struct TokenIntPtrInfo menu_config[] =
13556 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13557 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13558 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13559 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13560 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13561 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13562 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13563 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13564 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13567 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13569 char *token = menu_config[j].token;
13570 char *value = getHashEntry(setup_file_hash, token);
13573 *menu_config[j].value = get_token_parameter_value(token, value);
13577 // special case: initialize with default values that may be overwritten
13578 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13579 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13583 char *token_prefix;
13584 struct RectWithBorder *struct_ptr;
13588 { "viewport.window", &viewport.window[i] },
13589 { "viewport.playfield", &viewport.playfield[i] },
13590 { "viewport.door_1", &viewport.door_1[i] },
13591 { "viewport.door_2", &viewport.door_2[i] }
13594 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13596 struct TokenIntPtrInfo vp_config[] =
13598 { ".x", &vp_struct[j].struct_ptr->x },
13599 { ".y", &vp_struct[j].struct_ptr->y },
13600 { ".width", &vp_struct[j].struct_ptr->width },
13601 { ".height", &vp_struct[j].struct_ptr->height },
13602 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13603 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13604 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13605 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13606 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13607 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13608 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13609 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13610 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13611 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13612 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13613 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13614 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13615 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13616 { ".align", &vp_struct[j].struct_ptr->align },
13617 { ".valign", &vp_struct[j].struct_ptr->valign }
13620 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13622 char *token = getStringCat2(vp_struct[j].token_prefix,
13623 vp_config[k].token);
13624 char *value = getHashEntry(setup_file_hash, token);
13627 *vp_config[k].value = get_token_parameter_value(token, value);
13634 // special case: initialize with default values that may be overwritten
13635 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13636 for (i = 0; title_info[i].info != NULL; i++)
13638 struct TitleFadingInfo *info = title_info[i].info;
13639 char *base_token = title_info[i].text;
13641 for (j = 0; title_tokens[j].type != -1; j++)
13643 char *token = getStringCat2(base_token, title_tokens[j].text);
13644 char *value = getHashEntry(setup_file_hash, token);
13648 int parameter_value = get_token_parameter_value(token, value);
13652 *(int *)title_tokens[j].value = (int)parameter_value;
13661 // special case: initialize with default values that may be overwritten
13662 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13663 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13665 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13666 char *base_token = titlemessage_arrays[i].text;
13668 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13670 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13671 char *value = getHashEntry(setup_file_hash, token);
13675 int parameter_value = get_token_parameter_value(token, value);
13677 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13681 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13682 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13684 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13694 // read (and overwrite with) values that may be specified in config file
13695 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13697 // special case: check if network and preview player positions are redefined
13698 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13700 freeSetupFileHash(setup_file_hash);
13703 void LoadMenuDesignSettings(void)
13705 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13707 InitMenuDesignSettings_Static();
13708 InitMenuDesignSettings_SpecialPreProcessing();
13709 InitMenuDesignSettings_PreviewPlayers();
13711 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13713 // first look for special settings configured in level series config
13714 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13716 if (fileExists(filename_base))
13717 LoadMenuDesignSettingsFromFilename(filename_base);
13720 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13722 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13723 LoadMenuDesignSettingsFromFilename(filename_local);
13725 InitMenuDesignSettings_SpecialPostProcessing();
13728 void LoadMenuDesignSettings_AfterGraphics(void)
13730 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13733 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13734 boolean ignore_defaults)
13738 for (i = 0; sound_config_vars[i].token != NULL; i++)
13740 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13742 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13743 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13747 *sound_config_vars[i].value =
13748 get_token_parameter_value(sound_config_vars[i].token, value);
13752 void InitSoundSettings_Static(void)
13754 // always start with reliable default values from static default config
13755 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13758 static void LoadSoundSettingsFromFilename(char *filename)
13760 SetupFileHash *setup_file_hash;
13762 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13765 // read (and overwrite with) values that may be specified in config file
13766 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13768 freeSetupFileHash(setup_file_hash);
13771 void LoadSoundSettings(void)
13773 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13775 InitSoundSettings_Static();
13777 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13779 // first look for special settings configured in level series config
13780 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13782 if (fileExists(filename_base))
13783 LoadSoundSettingsFromFilename(filename_base);
13786 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13788 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13789 LoadSoundSettingsFromFilename(filename_local);
13792 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13794 char *filename = getEditorSetupFilename();
13795 SetupFileList *setup_file_list, *list;
13796 SetupFileHash *element_hash;
13797 int num_unknown_tokens = 0;
13800 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13803 element_hash = newSetupFileHash();
13805 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13806 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13808 // determined size may be larger than needed (due to unknown elements)
13810 for (list = setup_file_list; list != NULL; list = list->next)
13813 // add space for up to 3 more elements for padding that may be needed
13814 *num_elements += 3;
13816 // free memory for old list of elements, if needed
13817 checked_free(*elements);
13819 // allocate memory for new list of elements
13820 *elements = checked_malloc(*num_elements * sizeof(int));
13823 for (list = setup_file_list; list != NULL; list = list->next)
13825 char *value = getHashEntry(element_hash, list->token);
13827 if (value == NULL) // try to find obsolete token mapping
13829 char *mapped_token = get_mapped_token(list->token);
13831 if (mapped_token != NULL)
13833 value = getHashEntry(element_hash, mapped_token);
13835 free(mapped_token);
13841 (*elements)[(*num_elements)++] = atoi(value);
13845 if (num_unknown_tokens == 0)
13848 Warn("unknown token(s) found in config file:");
13849 Warn("- config file: '%s'", filename);
13851 num_unknown_tokens++;
13854 Warn("- token: '%s'", list->token);
13858 if (num_unknown_tokens > 0)
13861 while (*num_elements % 4) // pad with empty elements, if needed
13862 (*elements)[(*num_elements)++] = EL_EMPTY;
13864 freeSetupFileList(setup_file_list);
13865 freeSetupFileHash(element_hash);
13868 for (i = 0; i < *num_elements; i++)
13869 Debug("editor", "element '%s' [%d]\n",
13870 element_info[(*elements)[i]].token_name, (*elements)[i]);
13874 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13877 SetupFileHash *setup_file_hash = NULL;
13878 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13879 char *filename_music, *filename_prefix, *filename_info;
13885 token_to_value_ptr[] =
13887 { "title_header", &tmp_music_file_info.title_header },
13888 { "artist_header", &tmp_music_file_info.artist_header },
13889 { "album_header", &tmp_music_file_info.album_header },
13890 { "year_header", &tmp_music_file_info.year_header },
13891 { "played_header", &tmp_music_file_info.played_header },
13893 { "title", &tmp_music_file_info.title },
13894 { "artist", &tmp_music_file_info.artist },
13895 { "album", &tmp_music_file_info.album },
13896 { "year", &tmp_music_file_info.year },
13897 { "played", &tmp_music_file_info.played },
13903 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13904 getCustomMusicFilename(basename));
13906 if (filename_music == NULL)
13909 // ---------- try to replace file extension ----------
13911 filename_prefix = getStringCopy(filename_music);
13912 if (strrchr(filename_prefix, '.') != NULL)
13913 *strrchr(filename_prefix, '.') = '\0';
13914 filename_info = getStringCat2(filename_prefix, ".txt");
13916 if (fileExists(filename_info))
13917 setup_file_hash = loadSetupFileHash(filename_info);
13919 free(filename_prefix);
13920 free(filename_info);
13922 if (setup_file_hash == NULL)
13924 // ---------- try to add file extension ----------
13926 filename_prefix = getStringCopy(filename_music);
13927 filename_info = getStringCat2(filename_prefix, ".txt");
13929 if (fileExists(filename_info))
13930 setup_file_hash = loadSetupFileHash(filename_info);
13932 free(filename_prefix);
13933 free(filename_info);
13936 if (setup_file_hash == NULL)
13939 // ---------- music file info found ----------
13941 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13943 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13945 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13947 *token_to_value_ptr[i].value_ptr =
13948 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13951 tmp_music_file_info.basename = getStringCopy(basename);
13952 tmp_music_file_info.music = music;
13953 tmp_music_file_info.is_sound = is_sound;
13955 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13956 *new_music_file_info = tmp_music_file_info;
13958 return new_music_file_info;
13961 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13963 return get_music_file_info_ext(basename, music, FALSE);
13966 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13968 return get_music_file_info_ext(basename, sound, TRUE);
13971 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13972 char *basename, boolean is_sound)
13974 for (; list != NULL; list = list->next)
13975 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13981 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13983 return music_info_listed_ext(list, basename, FALSE);
13986 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13988 return music_info_listed_ext(list, basename, TRUE);
13991 void LoadMusicInfo(void)
13993 int num_music_noconf = getMusicListSize_NoConf();
13994 int num_music = getMusicListSize();
13995 int num_sounds = getSoundListSize();
13996 struct FileInfo *music, *sound;
13997 struct MusicFileInfo *next, **new;
14001 while (music_file_info != NULL)
14003 next = music_file_info->next;
14005 checked_free(music_file_info->basename);
14007 checked_free(music_file_info->title_header);
14008 checked_free(music_file_info->artist_header);
14009 checked_free(music_file_info->album_header);
14010 checked_free(music_file_info->year_header);
14011 checked_free(music_file_info->played_header);
14013 checked_free(music_file_info->title);
14014 checked_free(music_file_info->artist);
14015 checked_free(music_file_info->album);
14016 checked_free(music_file_info->year);
14017 checked_free(music_file_info->played);
14019 free(music_file_info);
14021 music_file_info = next;
14024 new = &music_file_info;
14026 // get (configured or unconfigured) music file info for all levels
14027 for (i = leveldir_current->first_level;
14028 i <= leveldir_current->last_level; i++)
14032 if (levelset.music[i] != MUS_UNDEFINED)
14034 // get music file info for configured level music
14035 music_nr = levelset.music[i];
14037 else if (num_music_noconf > 0)
14039 // get music file info for unconfigured level music
14040 int level_pos = i - leveldir_current->first_level;
14042 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14049 char *basename = getMusicInfoEntryFilename(music_nr);
14051 if (basename == NULL)
14054 if (!music_info_listed(music_file_info, basename))
14056 *new = get_music_file_info(basename, music_nr);
14059 new = &(*new)->next;
14063 // get music file info for all remaining configured music files
14064 for (i = 0; i < num_music; i++)
14066 music = getMusicListEntry(i);
14068 if (music->filename == NULL)
14071 if (strEqual(music->filename, UNDEFINED_FILENAME))
14074 // a configured file may be not recognized as music
14075 if (!FileIsMusic(music->filename))
14078 if (!music_info_listed(music_file_info, music->filename))
14080 *new = get_music_file_info(music->filename, i);
14083 new = &(*new)->next;
14087 // get sound file info for all configured sound files
14088 for (i = 0; i < num_sounds; i++)
14090 sound = getSoundListEntry(i);
14092 if (sound->filename == NULL)
14095 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14098 // a configured file may be not recognized as sound
14099 if (!FileIsSound(sound->filename))
14102 if (!sound_info_listed(music_file_info, sound->filename))
14104 *new = get_sound_file_info(sound->filename, i);
14106 new = &(*new)->next;
14110 // add pointers to previous list nodes
14112 struct MusicFileInfo *node = music_file_info;
14114 while (node != NULL)
14117 node->next->prev = node;
14123 static void add_helpanim_entry(int element, int action, int direction,
14124 int delay, int *num_list_entries)
14126 struct HelpAnimInfo *new_list_entry;
14127 (*num_list_entries)++;
14130 checked_realloc(helpanim_info,
14131 *num_list_entries * sizeof(struct HelpAnimInfo));
14132 new_list_entry = &helpanim_info[*num_list_entries - 1];
14134 new_list_entry->element = element;
14135 new_list_entry->action = action;
14136 new_list_entry->direction = direction;
14137 new_list_entry->delay = delay;
14140 static void print_unknown_token(char *filename, char *token, int token_nr)
14145 Warn("unknown token(s) found in config file:");
14146 Warn("- config file: '%s'", filename);
14149 Warn("- token: '%s'", token);
14152 static void print_unknown_token_end(int token_nr)
14158 void LoadHelpAnimInfo(void)
14160 char *filename = getHelpAnimFilename();
14161 SetupFileList *setup_file_list = NULL, *list;
14162 SetupFileHash *element_hash, *action_hash, *direction_hash;
14163 int num_list_entries = 0;
14164 int num_unknown_tokens = 0;
14167 if (fileExists(filename))
14168 setup_file_list = loadSetupFileList(filename);
14170 if (setup_file_list == NULL)
14172 // use reliable default values from static configuration
14173 SetupFileList *insert_ptr;
14175 insert_ptr = setup_file_list =
14176 newSetupFileList(helpanim_config[0].token,
14177 helpanim_config[0].value);
14179 for (i = 1; helpanim_config[i].token; i++)
14180 insert_ptr = addListEntry(insert_ptr,
14181 helpanim_config[i].token,
14182 helpanim_config[i].value);
14185 element_hash = newSetupFileHash();
14186 action_hash = newSetupFileHash();
14187 direction_hash = newSetupFileHash();
14189 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14190 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14192 for (i = 0; i < NUM_ACTIONS; i++)
14193 setHashEntry(action_hash, element_action_info[i].suffix,
14194 i_to_a(element_action_info[i].value));
14196 // do not store direction index (bit) here, but direction value!
14197 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14198 setHashEntry(direction_hash, element_direction_info[i].suffix,
14199 i_to_a(1 << element_direction_info[i].value));
14201 for (list = setup_file_list; list != NULL; list = list->next)
14203 char *element_token, *action_token, *direction_token;
14204 char *element_value, *action_value, *direction_value;
14205 int delay = atoi(list->value);
14207 if (strEqual(list->token, "end"))
14209 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14214 /* first try to break element into element/action/direction parts;
14215 if this does not work, also accept combined "element[.act][.dir]"
14216 elements (like "dynamite.active"), which are unique elements */
14218 if (strchr(list->token, '.') == NULL) // token contains no '.'
14220 element_value = getHashEntry(element_hash, list->token);
14221 if (element_value != NULL) // element found
14222 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14223 &num_list_entries);
14226 // no further suffixes found -- this is not an element
14227 print_unknown_token(filename, list->token, num_unknown_tokens++);
14233 // token has format "<prefix>.<something>"
14235 action_token = strchr(list->token, '.'); // suffix may be action ...
14236 direction_token = action_token; // ... or direction
14238 element_token = getStringCopy(list->token);
14239 *strchr(element_token, '.') = '\0';
14241 element_value = getHashEntry(element_hash, element_token);
14243 if (element_value == NULL) // this is no element
14245 element_value = getHashEntry(element_hash, list->token);
14246 if (element_value != NULL) // combined element found
14247 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14248 &num_list_entries);
14250 print_unknown_token(filename, list->token, num_unknown_tokens++);
14252 free(element_token);
14257 action_value = getHashEntry(action_hash, action_token);
14259 if (action_value != NULL) // action found
14261 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14262 &num_list_entries);
14264 free(element_token);
14269 direction_value = getHashEntry(direction_hash, direction_token);
14271 if (direction_value != NULL) // direction found
14273 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14274 &num_list_entries);
14276 free(element_token);
14281 if (strchr(action_token + 1, '.') == NULL)
14283 // no further suffixes found -- this is not an action nor direction
14285 element_value = getHashEntry(element_hash, list->token);
14286 if (element_value != NULL) // combined element found
14287 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14288 &num_list_entries);
14290 print_unknown_token(filename, list->token, num_unknown_tokens++);
14292 free(element_token);
14297 // token has format "<prefix>.<suffix>.<something>"
14299 direction_token = strchr(action_token + 1, '.');
14301 action_token = getStringCopy(action_token);
14302 *strchr(action_token + 1, '.') = '\0';
14304 action_value = getHashEntry(action_hash, action_token);
14306 if (action_value == NULL) // this is no action
14308 element_value = getHashEntry(element_hash, list->token);
14309 if (element_value != NULL) // combined element found
14310 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14311 &num_list_entries);
14313 print_unknown_token(filename, list->token, num_unknown_tokens++);
14315 free(element_token);
14316 free(action_token);
14321 direction_value = getHashEntry(direction_hash, direction_token);
14323 if (direction_value != NULL) // direction found
14325 add_helpanim_entry(atoi(element_value), atoi(action_value),
14326 atoi(direction_value), delay, &num_list_entries);
14328 free(element_token);
14329 free(action_token);
14334 // this is no direction
14336 element_value = getHashEntry(element_hash, list->token);
14337 if (element_value != NULL) // combined element found
14338 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14339 &num_list_entries);
14341 print_unknown_token(filename, list->token, num_unknown_tokens++);
14343 free(element_token);
14344 free(action_token);
14347 print_unknown_token_end(num_unknown_tokens);
14349 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14350 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14352 freeSetupFileList(setup_file_list);
14353 freeSetupFileHash(element_hash);
14354 freeSetupFileHash(action_hash);
14355 freeSetupFileHash(direction_hash);
14358 for (i = 0; i < num_list_entries; i++)
14359 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14360 EL_NAME(helpanim_info[i].element),
14361 helpanim_info[i].element,
14362 helpanim_info[i].action,
14363 helpanim_info[i].direction,
14364 helpanim_info[i].delay);
14368 void LoadHelpTextInfo(void)
14370 char *filename = getHelpTextFilename();
14373 if (helptext_info != NULL)
14375 freeSetupFileHash(helptext_info);
14376 helptext_info = NULL;
14379 if (fileExists(filename))
14380 helptext_info = loadSetupFileHash(filename);
14382 if (helptext_info == NULL)
14384 // use reliable default values from static configuration
14385 helptext_info = newSetupFileHash();
14387 for (i = 0; helptext_config[i].token; i++)
14388 setHashEntry(helptext_info,
14389 helptext_config[i].token,
14390 helptext_config[i].value);
14394 BEGIN_HASH_ITERATION(helptext_info, itr)
14396 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14397 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14399 END_HASH_ITERATION(hash, itr)
14404 // ----------------------------------------------------------------------------
14406 // ----------------------------------------------------------------------------
14408 #define MAX_NUM_CONVERT_LEVELS 1000
14410 void ConvertLevels(void)
14412 static LevelDirTree *convert_leveldir = NULL;
14413 static int convert_level_nr = -1;
14414 static int num_levels_handled = 0;
14415 static int num_levels_converted = 0;
14416 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14419 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14420 global.convert_leveldir);
14422 if (convert_leveldir == NULL)
14423 Fail("no such level identifier: '%s'", global.convert_leveldir);
14425 leveldir_current = convert_leveldir;
14427 if (global.convert_level_nr != -1)
14429 convert_leveldir->first_level = global.convert_level_nr;
14430 convert_leveldir->last_level = global.convert_level_nr;
14433 convert_level_nr = convert_leveldir->first_level;
14435 PrintLine("=", 79);
14436 Print("Converting levels\n");
14437 PrintLine("-", 79);
14438 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14439 Print("Level series name: '%s'\n", convert_leveldir->name);
14440 Print("Level series author: '%s'\n", convert_leveldir->author);
14441 Print("Number of levels: %d\n", convert_leveldir->levels);
14442 PrintLine("=", 79);
14445 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14446 levels_failed[i] = FALSE;
14448 while (convert_level_nr <= convert_leveldir->last_level)
14450 char *level_filename;
14453 level_nr = convert_level_nr++;
14455 Print("Level %03d: ", level_nr);
14457 LoadLevel(level_nr);
14458 if (level.no_level_file || level.no_valid_file)
14460 Print("(no level)\n");
14464 Print("converting level ... ");
14467 // special case: conversion of some EMC levels as requested by ACME
14468 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14471 level_filename = getDefaultLevelFilename(level_nr);
14472 new_level = !fileExists(level_filename);
14476 SaveLevel(level_nr);
14478 num_levels_converted++;
14480 Print("converted.\n");
14484 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14485 levels_failed[level_nr] = TRUE;
14487 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14490 num_levels_handled++;
14494 PrintLine("=", 79);
14495 Print("Number of levels handled: %d\n", num_levels_handled);
14496 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14497 (num_levels_handled ?
14498 num_levels_converted * 100 / num_levels_handled : 0));
14499 PrintLine("-", 79);
14500 Print("Summary (for automatic parsing by scripts):\n");
14501 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14502 convert_leveldir->identifier, num_levels_converted,
14503 num_levels_handled,
14504 (num_levels_handled ?
14505 num_levels_converted * 100 / num_levels_handled : 0));
14507 if (num_levels_handled != num_levels_converted)
14509 Print(", FAILED:");
14510 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14511 if (levels_failed[i])
14516 PrintLine("=", 79);
14518 CloseAllAndExit(0);
14522 // ----------------------------------------------------------------------------
14523 // create and save images for use in level sketches (raw BMP format)
14524 // ----------------------------------------------------------------------------
14526 void CreateLevelSketchImages(void)
14532 InitElementPropertiesGfxElement();
14534 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14535 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14537 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14539 int element = getMappedElement(i);
14540 char basename1[16];
14541 char basename2[16];
14545 sprintf(basename1, "%04d.bmp", i);
14546 sprintf(basename2, "%04ds.bmp", i);
14548 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14549 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14551 DrawSizedElement(0, 0, element, TILESIZE);
14552 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14554 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14555 Fail("cannot save level sketch image file '%s'", filename1);
14557 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14558 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14560 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14561 Fail("cannot save level sketch image file '%s'", filename2);
14566 // create corresponding SQL statements (for normal and small images)
14569 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14570 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14573 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14574 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14576 // optional: create content for forum level sketch demonstration post
14578 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14581 FreeBitmap(bitmap1);
14582 FreeBitmap(bitmap2);
14585 fprintf(stderr, "\n");
14587 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14589 CloseAllAndExit(0);
14593 // ----------------------------------------------------------------------------
14594 // create and save images for element collecting animations (raw BMP format)
14595 // ----------------------------------------------------------------------------
14597 static boolean createCollectImage(int element)
14599 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14602 void CreateCollectElementImages(void)
14606 int anim_frames = num_steps - 1;
14607 int tile_size = TILESIZE;
14608 int anim_width = tile_size * anim_frames;
14609 int anim_height = tile_size;
14610 int num_collect_images = 0;
14611 int pos_collect_images = 0;
14613 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14614 if (createCollectImage(i))
14615 num_collect_images++;
14617 Info("Creating %d element collecting animation images ...",
14618 num_collect_images);
14620 int dst_width = anim_width * 2;
14621 int dst_height = anim_height * num_collect_images / 2;
14622 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14623 char *basename_bmp = "RocksCollect.bmp";
14624 char *basename_png = "RocksCollect.png";
14625 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14626 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14627 int len_filename_bmp = strlen(filename_bmp);
14628 int len_filename_png = strlen(filename_png);
14629 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14630 char cmd_convert[max_command_len];
14632 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14636 // force using RGBA surface for destination bitmap
14637 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14638 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14640 dst_bitmap->surface =
14641 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14643 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14645 if (!createCollectImage(i))
14648 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14649 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14650 int graphic = el2img(i);
14651 char *token_name = element_info[i].token_name;
14652 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14653 Bitmap *src_bitmap;
14656 Info("- creating collecting image for '%s' ...", token_name);
14658 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14660 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14661 tile_size, tile_size, 0, 0);
14663 // force using RGBA surface for temporary bitmap (using transparent black)
14664 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14665 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14667 tmp_bitmap->surface =
14668 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14670 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14672 for (j = 0; j < anim_frames; j++)
14674 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14675 int frame_size = frame_size_final * num_steps;
14676 int offset = (tile_size - frame_size_final) / 2;
14677 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14679 while (frame_size > frame_size_final)
14683 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14685 FreeBitmap(frame_bitmap);
14687 frame_bitmap = half_bitmap;
14690 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14691 frame_size_final, frame_size_final,
14692 dst_x + j * tile_size + offset, dst_y + offset);
14694 FreeBitmap(frame_bitmap);
14697 tmp_bitmap->surface_masked = NULL;
14699 FreeBitmap(tmp_bitmap);
14701 pos_collect_images++;
14704 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14705 Fail("cannot save element collecting image file '%s'", filename_bmp);
14707 FreeBitmap(dst_bitmap);
14709 Info("Converting image file from BMP to PNG ...");
14711 if (system(cmd_convert) != 0)
14712 Fail("converting image file failed");
14714 unlink(filename_bmp);
14718 CloseAllAndExit(0);
14722 // ----------------------------------------------------------------------------
14723 // create and save images for custom and group elements (raw BMP format)
14724 // ----------------------------------------------------------------------------
14726 void CreateCustomElementImages(char *directory)
14728 char *src_basename = "RocksCE-template.ilbm";
14729 char *dst_basename = "RocksCE.bmp";
14730 char *src_filename = getPath2(directory, src_basename);
14731 char *dst_filename = getPath2(directory, dst_basename);
14732 Bitmap *src_bitmap;
14734 int yoffset_ce = 0;
14735 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14738 InitVideoDefaults();
14740 ReCreateBitmap(&backbuffer, video.width, video.height);
14742 src_bitmap = LoadImage(src_filename);
14744 bitmap = CreateBitmap(TILEX * 16 * 2,
14745 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14748 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14755 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14756 TILEX * x, TILEY * y + yoffset_ce);
14758 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14760 TILEX * x + TILEX * 16,
14761 TILEY * y + yoffset_ce);
14763 for (j = 2; j >= 0; j--)
14767 BlitBitmap(src_bitmap, bitmap,
14768 TILEX + c * 7, 0, 6, 10,
14769 TILEX * x + 6 + j * 7,
14770 TILEY * y + 11 + yoffset_ce);
14772 BlitBitmap(src_bitmap, bitmap,
14773 TILEX + c * 8, TILEY, 6, 10,
14774 TILEX * 16 + TILEX * x + 6 + j * 8,
14775 TILEY * y + 10 + yoffset_ce);
14781 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14788 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14789 TILEX * x, TILEY * y + yoffset_ge);
14791 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14793 TILEX * x + TILEX * 16,
14794 TILEY * y + yoffset_ge);
14796 for (j = 1; j >= 0; j--)
14800 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14801 TILEX * x + 6 + j * 10,
14802 TILEY * y + 11 + yoffset_ge);
14804 BlitBitmap(src_bitmap, bitmap,
14805 TILEX + c * 8, TILEY + 12, 6, 10,
14806 TILEX * 16 + TILEX * x + 10 + j * 8,
14807 TILEY * y + 10 + yoffset_ge);
14813 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14814 Fail("cannot save CE graphics file '%s'", dst_filename);
14816 FreeBitmap(bitmap);
14818 CloseAllAndExit(0);