1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
659 EL_BD_MAGIC_WALL, -1,
660 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
661 &li.bd_magic_wall_diamond_to, EL_BD_ROCK_FALLING
664 EL_BD_MAGIC_WALL, -1,
665 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
666 &li.bd_magic_wall_rock_to, EL_BD_DIAMOND_FALLING
669 EL_BD_MAGIC_WALL, -1,
670 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
671 &li.bd_magic_wall_mega_rock_to, EL_BD_NITRO_PACK_FALLING
674 EL_BD_MAGIC_WALL, -1,
675 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
676 &li.bd_magic_wall_nut_to, EL_BD_NUT_FALLING
679 EL_BD_MAGIC_WALL, -1,
680 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
681 &li.bd_magic_wall_nitro_pack_to, EL_BD_MEGA_ROCK_FALLING
684 EL_BD_MAGIC_WALL, -1,
685 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
686 &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
689 EL_BD_MAGIC_WALL, -1,
690 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
691 &li.bd_magic_wall_flying_rock_to, EL_BD_FLYING_DIAMOND_FLYING
696 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
697 &li.bd_clock_extra_time, 30
701 EL_BD_VOODOO_DOLL, -1,
702 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
703 &li.bd_voodoo_collects_diamonds, FALSE
706 EL_BD_VOODOO_DOLL, -1,
707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
708 &li.bd_voodoo_hurt_kills_player, FALSE
711 EL_BD_VOODOO_DOLL, -1,
712 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
713 &li.bd_voodoo_dies_by_rock, FALSE
716 EL_BD_VOODOO_DOLL, -1,
717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
718 &li.bd_voodoo_vanish_by_explosion, TRUE
721 EL_BD_VOODOO_DOLL, -1,
722 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
723 &li.bd_voodoo_penalty_time, 30
728 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
729 &li.bd_slime_is_predictable, TRUE
733 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
734 &li.bd_slime_permeability_rate, 100
738 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
739 &li.bd_slime_permeability_bits_c64, 0
743 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
744 &li.bd_slime_random_seed_c64, -1
748 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
749 &li.bd_slime_eats_element_1, EL_BD_DIAMOND
753 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
754 &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
758 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
759 &li.bd_slime_eats_element_2, EL_BD_ROCK
763 TYPE_ELEMENT, CONF_VALUE_16_BIT(4),
764 &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
768 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
769 &li.bd_slime_eats_element_3, EL_BD_NUT
773 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
774 &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
779 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
780 &li.bd_acid_eats_element, EL_BD_SAND
784 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
785 &li.bd_acid_spread_rate, 3
789 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
790 &li.bd_acid_turns_to_element, EL_BD_EXPLODING_3
795 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
796 &li.bd_biter_move_delay, 0
800 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
801 &li.bd_biter_eats_element, EL_BD_DIAMOND
806 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
807 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
811 EL_BD_EXPANDABLE_WALL_ANY, -1,
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.bd_change_expanding_wall, FALSE
817 EL_BD_REPLICATOR, -1,
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
819 &li.bd_replicators_active, TRUE
822 EL_BD_REPLICATOR, -1,
823 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
824 &li.bd_replicator_create_delay, 4
828 EL_BD_CONVEYOR_LEFT, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
830 &li.bd_conveyor_belts_active, TRUE
833 EL_BD_CONVEYOR_LEFT, -1,
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
835 &li.bd_conveyor_belts_changed, FALSE
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
841 &li.bd_water_cannot_flow_down, FALSE
846 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
847 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
851 EL_BD_PNEUMATIC_HAMMER, -1,
852 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
853 &li.bd_hammer_walls_break_delay, 5
856 EL_BD_PNEUMATIC_HAMMER, -1,
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
858 &li.bd_hammer_walls_reappear, FALSE
861 EL_BD_PNEUMATIC_HAMMER, -1,
862 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
863 &li.bd_hammer_walls_reappear_delay, 100
866 // (the following values are related to various game elements)
870 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
871 &li.score[SC_EMERALD], 10
876 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
877 &li.score[SC_DIAMOND], 10
882 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
883 &li.score[SC_BUG], 10
888 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
889 &li.score[SC_SPACESHIP], 10
894 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
895 &li.score[SC_PACMAN], 10
900 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
901 &li.score[SC_NUT], 10
906 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
907 &li.score[SC_DYNAMITE], 10
912 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
913 &li.score[SC_KEY], 10
918 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
919 &li.score[SC_PEARL], 10
924 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
925 &li.score[SC_CRYSTAL], 10
928 // (amoeba values used by R'n'D game engine only)
931 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
932 &li.amoeba_content, EL_DIAMOND
936 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
941 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
942 &li.grow_into_diggable, TRUE
944 // (amoeba values used by BD game engine only)
947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
948 &li.bd_amoeba_wait_for_hatching, FALSE
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
953 &li.bd_amoeba_start_immediately, TRUE
957 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
958 &li.bd_amoeba_2_explode_by_amoeba, TRUE
962 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
963 &li.bd_amoeba_threshold_too_big, 200
967 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
968 &li.bd_amoeba_slow_growth_time, 200
972 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
973 &li.bd_amoeba_slow_growth_rate, 3
977 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
978 &li.bd_amoeba_fast_growth_rate, 25
982 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
983 &li.bd_amoeba_content_too_big, EL_BD_ROCK
987 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
988 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
993 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
994 &li.bd_amoeba_2_threshold_too_big, 200
998 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
999 &li.bd_amoeba_2_slow_growth_time, 200
1003 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1004 &li.bd_amoeba_2_slow_growth_rate, 3
1008 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1009 &li.bd_amoeba_2_fast_growth_rate, 25
1013 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1014 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1018 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1019 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1023 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1024 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1028 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1029 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1034 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1035 &li.yamyam_content, EL_ROCK, NULL,
1036 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1041 &li.score[SC_YAMYAM], 10
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1047 &li.score[SC_ROBOT], 10
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.time_magic_wall, 10
1068 EL_GAME_OF_LIFE, -1,
1069 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1070 &li.game_of_life[0], 2
1073 EL_GAME_OF_LIFE, -1,
1074 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1075 &li.game_of_life[1], 3
1078 EL_GAME_OF_LIFE, -1,
1079 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1080 &li.game_of_life[2], 3
1083 EL_GAME_OF_LIFE, -1,
1084 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1085 &li.game_of_life[3], 3
1088 EL_GAME_OF_LIFE, -1,
1089 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1090 &li.use_life_bugs, FALSE
1095 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1100 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1105 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1110 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1115 EL_TIMEGATE_SWITCH, -1,
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 &li.time_timegate, 10
1121 EL_LIGHT_SWITCH_ACTIVE, -1,
1122 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1127 EL_SHIELD_NORMAL, -1,
1128 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1129 &li.shield_normal_time, 10
1132 EL_SHIELD_NORMAL, -1,
1133 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1134 &li.score[SC_SHIELD], 10
1138 EL_SHIELD_DEADLY, -1,
1139 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1140 &li.shield_deadly_time, 10
1143 EL_SHIELD_DEADLY, -1,
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1145 &li.score[SC_SHIELD], 10
1150 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1155 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1156 &li.extra_time_score, 10
1160 EL_TIME_ORB_FULL, -1,
1161 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1162 &li.time_orb_time, 10
1165 EL_TIME_ORB_FULL, -1,
1166 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1167 &li.use_time_orb_bug, FALSE
1172 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1173 &li.use_spring_bug, FALSE
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1179 &li.android_move_time, 10
1183 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1184 &li.android_clone_time, 10
1187 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1188 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1189 &li.android_clone_element[0], EL_EMPTY, NULL,
1190 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1194 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1195 &li.android_clone_element[0], EL_EMPTY, NULL,
1196 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1201 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1202 &li.lenses_score, 10
1206 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1211 EL_EMC_MAGNIFIER, -1,
1212 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1213 &li.magnify_score, 10
1216 EL_EMC_MAGNIFIER, -1,
1217 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1218 &li.magnify_time, 10
1222 EL_EMC_MAGIC_BALL, -1,
1223 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1227 EL_EMC_MAGIC_BALL, -1,
1228 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1229 &li.ball_random, FALSE
1232 EL_EMC_MAGIC_BALL, -1,
1233 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1234 &li.ball_active_initial, FALSE
1237 EL_EMC_MAGIC_BALL, -1,
1238 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1239 &li.ball_content, EL_EMPTY, NULL,
1240 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1244 EL_SOKOBAN_FIELD_EMPTY, -1,
1245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1246 &li.sb_fields_needed, TRUE
1250 EL_SOKOBAN_OBJECT, -1,
1251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1252 &li.sb_objects_needed, TRUE
1257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1258 &li.mm_laser_red, FALSE
1262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1263 &li.mm_laser_green, FALSE
1267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1268 &li.mm_laser_blue, TRUE
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1274 &li.df_laser_red, TRUE
1278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1279 &li.df_laser_green, TRUE
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1284 &li.df_laser_blue, FALSE
1288 EL_MM_FUSE_ACTIVE, -1,
1289 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1290 &li.mm_time_fuse, 25
1294 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1295 &li.mm_time_bomb, 75
1299 EL_MM_GRAY_BALL, -1,
1300 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1301 &li.mm_time_ball, 75
1304 EL_MM_GRAY_BALL, -1,
1305 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1306 &li.mm_ball_choice_mode, ANIM_RANDOM
1309 EL_MM_GRAY_BALL, -1,
1310 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1311 &li.mm_ball_content, EL_EMPTY, NULL,
1312 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1315 EL_MM_GRAY_BALL, -1,
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1317 &li.rotate_mm_ball_content, TRUE
1320 EL_MM_GRAY_BALL, -1,
1321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1322 &li.explode_mm_ball, FALSE
1326 EL_MM_STEEL_BLOCK, -1,
1327 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1328 &li.mm_time_block, 75
1331 EL_MM_LIGHTBALL, -1,
1332 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1333 &li.score[SC_ELEM_BONUS], 10
1343 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1347 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1348 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1352 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1353 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1359 &xx_envelope.autowrap, FALSE
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1364 &xx_envelope.centered, FALSE
1369 TYPE_STRING, CONF_VALUE_BYTES(1),
1370 &xx_envelope.text, -1, NULL,
1371 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1372 &xx_default_string_empty[0]
1382 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1386 TYPE_STRING, CONF_VALUE_BYTES(1),
1387 &xx_ei.description[0], -1,
1388 &yy_ei.description[0],
1389 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1390 &xx_default_description[0]
1395 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1396 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1397 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1399 #if ENABLE_RESERVED_CODE
1400 // (reserved for later use)
1403 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1404 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1405 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1412 &xx_ei.use_gfx_element, FALSE,
1413 &yy_ei.use_gfx_element
1417 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1418 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1419 &yy_ei.gfx_element_initial
1424 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1425 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1426 &yy_ei.access_direction
1431 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1432 &xx_ei.collect_score_initial, 10,
1433 &yy_ei.collect_score_initial
1437 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1438 &xx_ei.collect_count_initial, 1,
1439 &yy_ei.collect_count_initial
1444 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1445 &xx_ei.ce_value_fixed_initial, 0,
1446 &yy_ei.ce_value_fixed_initial
1450 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1451 &xx_ei.ce_value_random_initial, 0,
1452 &yy_ei.ce_value_random_initial
1456 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1457 &xx_ei.use_last_ce_value, FALSE,
1458 &yy_ei.use_last_ce_value
1463 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1464 &xx_ei.push_delay_fixed, 8,
1465 &yy_ei.push_delay_fixed
1469 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1470 &xx_ei.push_delay_random, 8,
1471 &yy_ei.push_delay_random
1475 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1476 &xx_ei.drop_delay_fixed, 0,
1477 &yy_ei.drop_delay_fixed
1481 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1482 &xx_ei.drop_delay_random, 0,
1483 &yy_ei.drop_delay_random
1487 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1488 &xx_ei.move_delay_fixed, 0,
1489 &yy_ei.move_delay_fixed
1493 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1494 &xx_ei.move_delay_random, 0,
1495 &yy_ei.move_delay_random
1499 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1500 &xx_ei.step_delay_fixed, 0,
1501 &yy_ei.step_delay_fixed
1505 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1506 &xx_ei.step_delay_random, 0,
1507 &yy_ei.step_delay_random
1512 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1513 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1518 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1519 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1520 &yy_ei.move_direction_initial
1524 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1525 &xx_ei.move_stepsize, TILEX / 8,
1526 &yy_ei.move_stepsize
1531 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1532 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1533 &yy_ei.move_enter_element
1537 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1538 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1539 &yy_ei.move_leave_element
1543 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1544 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1545 &yy_ei.move_leave_type
1550 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1551 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1552 &yy_ei.slippery_type
1557 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1558 &xx_ei.explosion_type, EXPLODES_3X3,
1559 &yy_ei.explosion_type
1563 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1564 &xx_ei.explosion_delay, 16,
1565 &yy_ei.explosion_delay
1569 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1570 &xx_ei.ignition_delay, 8,
1571 &yy_ei.ignition_delay
1576 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1577 &xx_ei.content, EL_EMPTY_SPACE,
1579 &xx_num_contents, 1, 1
1582 // ---------- "num_change_pages" must be the last entry ---------------------
1585 -1, SAVE_CONF_ALWAYS,
1586 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1587 &xx_ei.num_change_pages, 1,
1588 &yy_ei.num_change_pages
1599 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1601 // ---------- "current_change_page" must be the first entry -----------------
1604 -1, SAVE_CONF_ALWAYS,
1605 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1606 &xx_current_change_page, -1
1609 // ---------- (the remaining entries can be in any order) -------------------
1613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1614 &xx_change.can_change, FALSE
1619 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1620 &xx_event_bits[0], 0
1624 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1625 &xx_event_bits[1], 0
1630 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1631 &xx_change.trigger_player, CH_PLAYER_ANY
1635 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1636 &xx_change.trigger_side, CH_SIDE_ANY
1640 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1641 &xx_change.trigger_page, CH_PAGE_ANY
1646 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1647 &xx_change.target_element, EL_EMPTY_SPACE
1652 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1653 &xx_change.delay_fixed, 0
1657 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1658 &xx_change.delay_random, 0
1662 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1663 &xx_change.delay_frames, FRAMES_PER_SECOND
1668 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1669 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1674 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1675 &xx_change.explode, FALSE
1679 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1680 &xx_change.use_target_content, FALSE
1684 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1685 &xx_change.only_if_complete, FALSE
1689 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1690 &xx_change.use_random_replace, FALSE
1694 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1695 &xx_change.random_percentage, 100
1699 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1700 &xx_change.replace_when, CP_WHEN_EMPTY
1705 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1706 &xx_change.has_action, FALSE
1710 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1711 &xx_change.action_type, CA_NO_ACTION
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1716 &xx_change.action_mode, CA_MODE_UNDEFINED
1720 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1721 &xx_change.action_arg, CA_ARG_UNDEFINED
1726 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1727 &xx_change.action_element, EL_EMPTY_SPACE
1732 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1733 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1734 &xx_num_contents, 1, 1
1744 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1748 TYPE_STRING, CONF_VALUE_BYTES(1),
1749 &xx_ei.description[0], -1, NULL,
1750 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1751 &xx_default_description[0]
1756 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1757 &xx_ei.use_gfx_element, FALSE
1761 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1762 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1767 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1768 &xx_group.choice_mode, ANIM_RANDOM
1773 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1774 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1775 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1785 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1789 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1790 &xx_ei.use_gfx_element, FALSE
1794 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1795 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1805 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1809 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1810 &li.block_snap_field, TRUE
1814 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1815 &li.continuous_snapping, TRUE
1819 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1820 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1824 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1825 &li.use_start_element[0], FALSE
1829 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1830 &li.start_element[0], EL_PLAYER_1
1834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1835 &li.use_artwork_element[0], FALSE
1839 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1840 &li.artwork_element[0], EL_PLAYER_1
1844 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1845 &li.use_explosion_element[0], FALSE
1849 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1850 &li.explosion_element[0], EL_PLAYER_1
1865 filetype_id_list[] =
1867 { LEVEL_FILE_TYPE_RND, "RND" },
1868 { LEVEL_FILE_TYPE_BD, "BD" },
1869 { LEVEL_FILE_TYPE_EM, "EM" },
1870 { LEVEL_FILE_TYPE_SP, "SP" },
1871 { LEVEL_FILE_TYPE_DX, "DX" },
1872 { LEVEL_FILE_TYPE_SB, "SB" },
1873 { LEVEL_FILE_TYPE_DC, "DC" },
1874 { LEVEL_FILE_TYPE_MM, "MM" },
1875 { LEVEL_FILE_TYPE_MM, "DF" },
1880 // ============================================================================
1881 // level file functions
1882 // ============================================================================
1884 static boolean check_special_flags(char *flag)
1886 if (strEqual(options.special_flags, flag) ||
1887 strEqual(leveldir_current->special_flags, flag))
1893 static struct DateInfo getCurrentDate(void)
1895 time_t epoch_seconds = time(NULL);
1896 struct tm *now = localtime(&epoch_seconds);
1897 struct DateInfo date;
1899 date.year = now->tm_year + 1900;
1900 date.month = now->tm_mon + 1;
1901 date.day = now->tm_mday;
1903 date.src = DATE_SRC_CLOCK;
1908 static void resetEventFlags(struct ElementChangeInfo *change)
1912 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1913 change->has_event[i] = FALSE;
1916 static void resetEventBits(void)
1920 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1921 xx_event_bits[i] = 0;
1924 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1928 /* important: only change event flag if corresponding event bit is set
1929 (this is because all xx_event_bits[] values are loaded separately,
1930 and all xx_event_bits[] values are set back to zero before loading
1931 another value xx_event_bits[x] (each value representing 32 flags)) */
1933 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1934 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1935 change->has_event[i] = TRUE;
1938 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1942 /* in contrast to the above function setEventFlagsFromEventBits(), it
1943 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1944 depending on the corresponding change->has_event[i] values here, as
1945 all xx_event_bits[] values are reset in resetEventBits() before */
1947 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1948 if (change->has_event[i])
1949 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1952 static char *getDefaultElementDescription(struct ElementInfo *ei)
1954 static char description[MAX_ELEMENT_NAME_LEN + 1];
1955 char *default_description = (ei->custom_description != NULL ?
1956 ei->custom_description :
1957 ei->editor_description);
1960 // always start with reliable default values
1961 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1962 description[i] = '\0';
1964 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1965 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1967 return &description[0];
1970 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1972 char *default_description = getDefaultElementDescription(ei);
1975 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1976 ei->description[i] = default_description[i];
1979 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1983 for (i = 0; conf[i].data_type != -1; i++)
1985 int default_value = conf[i].default_value;
1986 int data_type = conf[i].data_type;
1987 int conf_type = conf[i].conf_type;
1988 int byte_mask = conf_type & CONF_MASK_BYTES;
1990 if (byte_mask == CONF_MASK_MULTI_BYTES)
1992 int default_num_entities = conf[i].default_num_entities;
1993 int max_num_entities = conf[i].max_num_entities;
1995 *(int *)(conf[i].num_entities) = default_num_entities;
1997 if (data_type == TYPE_STRING)
1999 char *default_string = conf[i].default_string;
2000 char *string = (char *)(conf[i].value);
2002 strncpy(string, default_string, max_num_entities);
2004 else if (data_type == TYPE_ELEMENT_LIST)
2006 int *element_array = (int *)(conf[i].value);
2009 for (j = 0; j < max_num_entities; j++)
2010 element_array[j] = default_value;
2012 else if (data_type == TYPE_CONTENT_LIST)
2014 struct Content *content = (struct Content *)(conf[i].value);
2017 for (c = 0; c < max_num_entities; c++)
2018 for (y = 0; y < 3; y++)
2019 for (x = 0; x < 3; x++)
2020 content[c].e[x][y] = default_value;
2023 else // constant size configuration data (1, 2 or 4 bytes)
2025 if (data_type == TYPE_BOOLEAN)
2026 *(boolean *)(conf[i].value) = default_value;
2028 *(int *) (conf[i].value) = default_value;
2033 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2037 for (i = 0; conf[i].data_type != -1; i++)
2039 int data_type = conf[i].data_type;
2040 int conf_type = conf[i].conf_type;
2041 int byte_mask = conf_type & CONF_MASK_BYTES;
2043 if (byte_mask == CONF_MASK_MULTI_BYTES)
2045 int max_num_entities = conf[i].max_num_entities;
2047 if (data_type == TYPE_STRING)
2049 char *string = (char *)(conf[i].value);
2050 char *string_copy = (char *)(conf[i].value_copy);
2052 strncpy(string_copy, string, max_num_entities);
2054 else if (data_type == TYPE_ELEMENT_LIST)
2056 int *element_array = (int *)(conf[i].value);
2057 int *element_array_copy = (int *)(conf[i].value_copy);
2060 for (j = 0; j < max_num_entities; j++)
2061 element_array_copy[j] = element_array[j];
2063 else if (data_type == TYPE_CONTENT_LIST)
2065 struct Content *content = (struct Content *)(conf[i].value);
2066 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2069 for (c = 0; c < max_num_entities; c++)
2070 for (y = 0; y < 3; y++)
2071 for (x = 0; x < 3; x++)
2072 content_copy[c].e[x][y] = content[c].e[x][y];
2075 else // constant size configuration data (1, 2 or 4 bytes)
2077 if (data_type == TYPE_BOOLEAN)
2078 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2080 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2085 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2089 xx_ei = *ei_from; // copy element data into temporary buffer
2090 yy_ei = *ei_to; // copy element data into temporary buffer
2092 copyConfigFromConfigList(chunk_config_CUSX_base);
2097 // ---------- reinitialize and copy change pages ----------
2099 ei_to->num_change_pages = ei_from->num_change_pages;
2100 ei_to->current_change_page = ei_from->current_change_page;
2102 setElementChangePages(ei_to, ei_to->num_change_pages);
2104 for (i = 0; i < ei_to->num_change_pages; i++)
2105 ei_to->change_page[i] = ei_from->change_page[i];
2107 // ---------- copy group element info ----------
2108 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2109 *ei_to->group = *ei_from->group;
2111 // mark this custom element as modified
2112 ei_to->modified_settings = TRUE;
2115 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2117 int change_page_size = sizeof(struct ElementChangeInfo);
2119 ei->num_change_pages = MAX(1, change_pages);
2122 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2124 if (ei->current_change_page >= ei->num_change_pages)
2125 ei->current_change_page = ei->num_change_pages - 1;
2127 ei->change = &ei->change_page[ei->current_change_page];
2130 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2132 xx_change = *change; // copy change data into temporary buffer
2134 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2136 *change = xx_change;
2138 resetEventFlags(change);
2140 change->direct_action = 0;
2141 change->other_action = 0;
2143 change->pre_change_function = NULL;
2144 change->change_function = NULL;
2145 change->post_change_function = NULL;
2148 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2152 li = *level; // copy level data into temporary buffer
2153 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2154 *level = li; // copy temporary buffer back to level data
2156 setLevelInfoToDefaults_BD();
2157 setLevelInfoToDefaults_EM();
2158 setLevelInfoToDefaults_SP();
2159 setLevelInfoToDefaults_MM();
2161 level->native_bd_level = &native_bd_level;
2162 level->native_em_level = &native_em_level;
2163 level->native_sp_level = &native_sp_level;
2164 level->native_mm_level = &native_mm_level;
2166 level->file_version = FILE_VERSION_ACTUAL;
2167 level->game_version = GAME_VERSION_ACTUAL;
2169 level->creation_date = getCurrentDate();
2171 level->encoding_16bit_field = TRUE;
2172 level->encoding_16bit_yamyam = TRUE;
2173 level->encoding_16bit_amoeba = TRUE;
2175 // clear level name and level author string buffers
2176 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2177 level->name[i] = '\0';
2178 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2179 level->author[i] = '\0';
2181 // set level name and level author to default values
2182 strcpy(level->name, NAMELESS_LEVEL_NAME);
2183 strcpy(level->author, ANONYMOUS_NAME);
2185 // set level playfield to playable default level with player and exit
2186 for (x = 0; x < MAX_LEV_FIELDX; x++)
2187 for (y = 0; y < MAX_LEV_FIELDY; y++)
2188 level->field[x][y] = EL_SAND;
2190 level->field[0][0] = EL_PLAYER_1;
2191 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2193 BorderElement = EL_STEELWALL;
2195 // detect custom elements when loading them
2196 level->file_has_custom_elements = FALSE;
2198 // set all bug compatibility flags to "false" => do not emulate this bug
2199 level->use_action_after_change_bug = FALSE;
2201 if (leveldir_current)
2203 // try to determine better author name than 'anonymous'
2204 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2206 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2207 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2211 switch (LEVELCLASS(leveldir_current))
2213 case LEVELCLASS_TUTORIAL:
2214 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2217 case LEVELCLASS_CONTRIB:
2218 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2219 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2222 case LEVELCLASS_PRIVATE:
2223 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2224 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2228 // keep default value
2235 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2237 static boolean clipboard_elements_initialized = FALSE;
2240 InitElementPropertiesStatic();
2242 li = *level; // copy level data into temporary buffer
2243 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2244 *level = li; // copy temporary buffer back to level data
2246 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2249 struct ElementInfo *ei = &element_info[element];
2251 if (element == EL_MM_GRAY_BALL)
2253 struct LevelInfo_MM *level_mm = level->native_mm_level;
2256 for (j = 0; j < level->num_mm_ball_contents; j++)
2257 level->mm_ball_content[j] =
2258 map_element_MM_to_RND(level_mm->ball_content[j]);
2261 // never initialize clipboard elements after the very first time
2262 // (to be able to use clipboard elements between several levels)
2263 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2266 if (IS_ENVELOPE(element))
2268 int envelope_nr = element - EL_ENVELOPE_1;
2270 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2272 level->envelope[envelope_nr] = xx_envelope;
2275 if (IS_CUSTOM_ELEMENT(element) ||
2276 IS_GROUP_ELEMENT(element) ||
2277 IS_INTERNAL_ELEMENT(element))
2279 xx_ei = *ei; // copy element data into temporary buffer
2281 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2286 setElementChangePages(ei, 1);
2287 setElementChangeInfoToDefaults(ei->change);
2289 if (IS_CUSTOM_ELEMENT(element) ||
2290 IS_GROUP_ELEMENT(element))
2292 setElementDescriptionToDefault(ei);
2294 ei->modified_settings = FALSE;
2297 if (IS_CUSTOM_ELEMENT(element) ||
2298 IS_INTERNAL_ELEMENT(element))
2300 // internal values used in level editor
2302 ei->access_type = 0;
2303 ei->access_layer = 0;
2304 ei->access_protected = 0;
2305 ei->walk_to_action = 0;
2306 ei->smash_targets = 0;
2309 ei->can_explode_by_fire = FALSE;
2310 ei->can_explode_smashed = FALSE;
2311 ei->can_explode_impact = FALSE;
2313 ei->current_change_page = 0;
2316 if (IS_GROUP_ELEMENT(element) ||
2317 IS_INTERNAL_ELEMENT(element))
2319 struct ElementGroupInfo *group;
2321 // initialize memory for list of elements in group
2322 if (ei->group == NULL)
2323 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2327 xx_group = *group; // copy group data into temporary buffer
2329 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2334 if (IS_EMPTY_ELEMENT(element) ||
2335 IS_INTERNAL_ELEMENT(element))
2337 xx_ei = *ei; // copy element data into temporary buffer
2339 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2345 clipboard_elements_initialized = TRUE;
2348 static void setLevelInfoToDefaults(struct LevelInfo *level,
2349 boolean level_info_only,
2350 boolean reset_file_status)
2352 setLevelInfoToDefaults_Level(level);
2354 if (!level_info_only)
2355 setLevelInfoToDefaults_Elements(level);
2357 if (reset_file_status)
2359 level->no_valid_file = FALSE;
2360 level->no_level_file = FALSE;
2363 level->changed = FALSE;
2366 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2368 level_file_info->nr = 0;
2369 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2370 level_file_info->packed = FALSE;
2372 setString(&level_file_info->basename, NULL);
2373 setString(&level_file_info->filename, NULL);
2376 int getMappedElement_SB(int, boolean);
2378 static void ActivateLevelTemplate(void)
2382 if (check_special_flags("load_xsb_to_ces"))
2384 // fill smaller playfields with padding "beyond border wall" elements
2385 if (level.fieldx < level_template.fieldx ||
2386 level.fieldy < level_template.fieldy)
2388 short field[level.fieldx][level.fieldy];
2389 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2390 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2391 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2392 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2394 // copy old playfield (which is smaller than the visible area)
2395 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2396 field[x][y] = level.field[x][y];
2398 // fill new, larger playfield with "beyond border wall" elements
2399 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2400 level.field[x][y] = getMappedElement_SB('_', TRUE);
2402 // copy the old playfield to the middle of the new playfield
2403 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2404 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2406 level.fieldx = new_fieldx;
2407 level.fieldy = new_fieldy;
2411 // Currently there is no special action needed to activate the template
2412 // data, because 'element_info' property settings overwrite the original
2413 // level data, while all other variables do not change.
2415 // Exception: 'from_level_template' elements in the original level playfield
2416 // are overwritten with the corresponding elements at the same position in
2417 // playfield from the level template.
2419 for (x = 0; x < level.fieldx; x++)
2420 for (y = 0; y < level.fieldy; y++)
2421 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2422 level.field[x][y] = level_template.field[x][y];
2424 if (check_special_flags("load_xsb_to_ces"))
2426 struct LevelInfo level_backup = level;
2428 // overwrite all individual level settings from template level settings
2429 level = level_template;
2431 // restore level file info
2432 level.file_info = level_backup.file_info;
2434 // restore playfield size
2435 level.fieldx = level_backup.fieldx;
2436 level.fieldy = level_backup.fieldy;
2438 // restore playfield content
2439 for (x = 0; x < level.fieldx; x++)
2440 for (y = 0; y < level.fieldy; y++)
2441 level.field[x][y] = level_backup.field[x][y];
2443 // restore name and author from individual level
2444 strcpy(level.name, level_backup.name);
2445 strcpy(level.author, level_backup.author);
2447 // restore flag "use_custom_template"
2448 level.use_custom_template = level_backup.use_custom_template;
2452 static boolean checkForPackageFromBasename_BD(char *basename)
2454 // check for native BD level file extensions
2455 if (!strSuffixLower(basename, ".bd") &&
2456 !strSuffixLower(basename, ".bdr") &&
2457 !strSuffixLower(basename, ".brc") &&
2458 !strSuffixLower(basename, ".gds"))
2461 // check for standard single-level BD files (like "001.bd")
2462 if (strSuffixLower(basename, ".bd") &&
2463 strlen(basename) == 6 &&
2464 basename[0] >= '0' && basename[0] <= '9' &&
2465 basename[1] >= '0' && basename[1] <= '9' &&
2466 basename[2] >= '0' && basename[2] <= '9')
2469 // this is a level package in native BD file format
2473 static char *getLevelFilenameFromBasename(char *basename)
2475 static char *filename = NULL;
2477 checked_free(filename);
2479 filename = getPath2(getCurrentLevelDir(), basename);
2484 static int getFileTypeFromBasename(char *basename)
2486 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2488 static char *filename = NULL;
2489 struct stat file_status;
2491 // ---------- try to determine file type from filename ----------
2493 // check for typical filename of a Supaplex level package file
2494 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2495 return LEVEL_FILE_TYPE_SP;
2497 // check for typical filename of a Diamond Caves II level package file
2498 if (strSuffixLower(basename, ".dc") ||
2499 strSuffixLower(basename, ".dc2"))
2500 return LEVEL_FILE_TYPE_DC;
2502 // check for typical filename of a Sokoban level package file
2503 if (strSuffixLower(basename, ".xsb") &&
2504 strchr(basename, '%') == NULL)
2505 return LEVEL_FILE_TYPE_SB;
2507 // check for typical filename of a Boulder Dash (GDash) level package file
2508 if (checkForPackageFromBasename_BD(basename))
2509 return LEVEL_FILE_TYPE_BD;
2511 // ---------- try to determine file type from filesize ----------
2513 checked_free(filename);
2514 filename = getPath2(getCurrentLevelDir(), basename);
2516 if (stat(filename, &file_status) == 0)
2518 // check for typical filesize of a Supaplex level package file
2519 if (file_status.st_size == 170496)
2520 return LEVEL_FILE_TYPE_SP;
2523 return LEVEL_FILE_TYPE_UNKNOWN;
2526 static int getFileTypeFromMagicBytes(char *filename, int type)
2530 if ((file = openFile(filename, MODE_READ)))
2532 char chunk_name[CHUNK_ID_LEN + 1];
2534 getFileChunkBE(file, chunk_name, NULL);
2536 if (strEqual(chunk_name, "MMII") ||
2537 strEqual(chunk_name, "MIRR"))
2538 type = LEVEL_FILE_TYPE_MM;
2546 static boolean checkForPackageFromBasename(char *basename)
2548 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2549 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2551 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2554 static char *getSingleLevelBasenameExt(int nr, char *extension)
2556 static char basename[MAX_FILENAME_LEN];
2559 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2561 sprintf(basename, "%03d.%s", nr, extension);
2566 static char *getSingleLevelBasename(int nr)
2568 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2571 static char *getPackedLevelBasename(int type)
2573 static char basename[MAX_FILENAME_LEN];
2574 char *directory = getCurrentLevelDir();
2576 DirectoryEntry *dir_entry;
2578 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2580 if ((dir = openDirectory(directory)) == NULL)
2582 Warn("cannot read current level directory '%s'", directory);
2587 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2589 char *entry_basename = dir_entry->basename;
2590 int entry_type = getFileTypeFromBasename(entry_basename);
2592 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2594 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2597 strcpy(basename, entry_basename);
2604 closeDirectory(dir);
2609 static char *getSingleLevelFilename(int nr)
2611 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2614 #if ENABLE_UNUSED_CODE
2615 static char *getPackedLevelFilename(int type)
2617 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2621 char *getDefaultLevelFilename(int nr)
2623 return getSingleLevelFilename(nr);
2626 #if ENABLE_UNUSED_CODE
2627 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2631 lfi->packed = FALSE;
2633 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2634 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2638 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2639 int type, char *format, ...)
2641 static char basename[MAX_FILENAME_LEN];
2644 va_start(ap, format);
2645 vsprintf(basename, format, ap);
2649 lfi->packed = FALSE;
2651 setString(&lfi->basename, basename);
2652 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2655 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2661 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2662 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2665 static int getFiletypeFromID(char *filetype_id)
2667 char *filetype_id_lower;
2668 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2671 if (filetype_id == NULL)
2672 return LEVEL_FILE_TYPE_UNKNOWN;
2674 filetype_id_lower = getStringToLower(filetype_id);
2676 for (i = 0; filetype_id_list[i].id != NULL; i++)
2678 char *id_lower = getStringToLower(filetype_id_list[i].id);
2680 if (strEqual(filetype_id_lower, id_lower))
2681 filetype = filetype_id_list[i].filetype;
2685 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2689 free(filetype_id_lower);
2694 char *getLocalLevelTemplateFilename(void)
2696 return getDefaultLevelFilename(-1);
2699 char *getGlobalLevelTemplateFilename(void)
2701 // global variable "leveldir_current" must be modified in the loop below
2702 LevelDirTree *leveldir_current_last = leveldir_current;
2703 char *filename = NULL;
2705 // check for template level in path from current to topmost tree node
2707 while (leveldir_current != NULL)
2709 filename = getDefaultLevelFilename(-1);
2711 if (fileExists(filename))
2714 leveldir_current = leveldir_current->node_parent;
2717 // restore global variable "leveldir_current" modified in above loop
2718 leveldir_current = leveldir_current_last;
2723 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2727 // special case: level number is negative => check for level template file
2730 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2731 getSingleLevelBasename(-1));
2733 // replace local level template filename with global template filename
2734 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2736 // no fallback if template file not existing
2740 // special case: check for file name/pattern specified in "levelinfo.conf"
2741 if (leveldir_current->level_filename != NULL)
2743 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2745 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2746 leveldir_current->level_filename, nr);
2748 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2750 if (fileExists(lfi->filename))
2753 else if (leveldir_current->level_filetype != NULL)
2755 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2757 // check for specified native level file with standard file name
2758 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2759 "%03d.%s", nr, LEVELFILE_EXTENSION);
2760 if (fileExists(lfi->filename))
2764 // check for native Rocks'n'Diamonds level file
2765 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2766 "%03d.%s", nr, LEVELFILE_EXTENSION);
2767 if (fileExists(lfi->filename))
2770 // check for native Boulder Dash level file
2771 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2772 if (fileExists(lfi->filename))
2775 // check for Emerald Mine level file (V1)
2776 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2777 'a' + (nr / 10) % 26, '0' + nr % 10);
2778 if (fileExists(lfi->filename))
2780 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2781 'A' + (nr / 10) % 26, '0' + nr % 10);
2782 if (fileExists(lfi->filename))
2785 // check for Emerald Mine level file (V2 to V5)
2786 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2787 if (fileExists(lfi->filename))
2790 // check for Emerald Mine level file (V6 / single mode)
2791 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2792 if (fileExists(lfi->filename))
2794 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2795 if (fileExists(lfi->filename))
2798 // check for Emerald Mine level file (V6 / teamwork mode)
2799 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2800 if (fileExists(lfi->filename))
2802 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2803 if (fileExists(lfi->filename))
2806 // check for various packed level file formats
2807 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2808 if (fileExists(lfi->filename))
2811 // no known level file found -- use default values (and fail later)
2812 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2813 "%03d.%s", nr, LEVELFILE_EXTENSION);
2816 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2818 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2819 lfi->type = getFileTypeFromBasename(lfi->basename);
2821 if (lfi->type == LEVEL_FILE_TYPE_RND)
2822 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2825 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2827 // always start with reliable default values
2828 setFileInfoToDefaults(level_file_info);
2830 level_file_info->nr = nr; // set requested level number
2832 determineLevelFileInfo_Filename(level_file_info);
2833 determineLevelFileInfo_Filetype(level_file_info);
2836 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2837 struct LevelFileInfo *lfi_to)
2839 lfi_to->nr = lfi_from->nr;
2840 lfi_to->type = lfi_from->type;
2841 lfi_to->packed = lfi_from->packed;
2843 setString(&lfi_to->basename, lfi_from->basename);
2844 setString(&lfi_to->filename, lfi_from->filename);
2847 // ----------------------------------------------------------------------------
2848 // functions for loading R'n'D level
2849 // ----------------------------------------------------------------------------
2851 int getMappedElement(int element)
2853 // remap some (historic, now obsolete) elements
2857 case EL_PLAYER_OBSOLETE:
2858 element = EL_PLAYER_1;
2861 case EL_KEY_OBSOLETE:
2865 case EL_EM_KEY_1_FILE_OBSOLETE:
2866 element = EL_EM_KEY_1;
2869 case EL_EM_KEY_2_FILE_OBSOLETE:
2870 element = EL_EM_KEY_2;
2873 case EL_EM_KEY_3_FILE_OBSOLETE:
2874 element = EL_EM_KEY_3;
2877 case EL_EM_KEY_4_FILE_OBSOLETE:
2878 element = EL_EM_KEY_4;
2881 case EL_ENVELOPE_OBSOLETE:
2882 element = EL_ENVELOPE_1;
2890 if (element >= NUM_FILE_ELEMENTS)
2892 Warn("invalid level element %d", element);
2894 element = EL_UNKNOWN;
2902 static int getMappedElementByVersion(int element, int game_version)
2904 // remap some elements due to certain game version
2906 if (game_version <= VERSION_IDENT(2,2,0,0))
2908 // map game font elements
2909 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2910 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2911 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2912 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2915 if (game_version < VERSION_IDENT(3,0,0,0))
2917 // map Supaplex gravity tube elements
2918 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2919 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2920 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2921 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2928 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2930 level->file_version = getFileVersion(file);
2931 level->game_version = getFileVersion(file);
2936 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2938 level->creation_date.year = getFile16BitBE(file);
2939 level->creation_date.month = getFile8Bit(file);
2940 level->creation_date.day = getFile8Bit(file);
2942 level->creation_date.src = DATE_SRC_LEVELFILE;
2947 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2949 int initial_player_stepsize;
2950 int initial_player_gravity;
2953 level->fieldx = getFile8Bit(file);
2954 level->fieldy = getFile8Bit(file);
2956 level->time = getFile16BitBE(file);
2957 level->gems_needed = getFile16BitBE(file);
2959 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2960 level->name[i] = getFile8Bit(file);
2961 level->name[MAX_LEVEL_NAME_LEN] = 0;
2963 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2964 level->score[i] = getFile8Bit(file);
2966 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2967 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2968 for (y = 0; y < 3; y++)
2969 for (x = 0; x < 3; x++)
2970 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2972 level->amoeba_speed = getFile8Bit(file);
2973 level->time_magic_wall = getFile8Bit(file);
2974 level->time_wheel = getFile8Bit(file);
2975 level->amoeba_content = getMappedElement(getFile8Bit(file));
2977 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2980 for (i = 0; i < MAX_PLAYERS; i++)
2981 level->initial_player_stepsize[i] = initial_player_stepsize;
2983 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2985 for (i = 0; i < MAX_PLAYERS; i++)
2986 level->initial_player_gravity[i] = initial_player_gravity;
2988 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2989 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2991 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2993 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2994 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2995 level->can_move_into_acid_bits = getFile32BitBE(file);
2996 level->dont_collide_with_bits = getFile8Bit(file);
2998 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2999 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3001 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3002 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3003 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3005 level->game_engine_type = getFile8Bit(file);
3007 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3012 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3016 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3017 level->name[i] = getFile8Bit(file);
3018 level->name[MAX_LEVEL_NAME_LEN] = 0;
3023 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3027 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3028 level->author[i] = getFile8Bit(file);
3029 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3034 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3037 int chunk_size_expected = level->fieldx * level->fieldy;
3039 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3040 stored with 16-bit encoding (and should be twice as big then).
3041 Even worse, playfield data was stored 16-bit when only yamyam content
3042 contained 16-bit elements and vice versa. */
3044 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3045 chunk_size_expected *= 2;
3047 if (chunk_size_expected != chunk_size)
3049 ReadUnusedBytesFromFile(file, chunk_size);
3050 return chunk_size_expected;
3053 for (y = 0; y < level->fieldy; y++)
3054 for (x = 0; x < level->fieldx; x++)
3055 level->field[x][y] =
3056 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3061 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3064 int header_size = 4;
3065 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3066 int chunk_size_expected = header_size + content_size;
3068 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3069 stored with 16-bit encoding (and should be twice as big then).
3070 Even worse, playfield data was stored 16-bit when only yamyam content
3071 contained 16-bit elements and vice versa. */
3073 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3074 chunk_size_expected += content_size;
3076 if (chunk_size_expected != chunk_size)
3078 ReadUnusedBytesFromFile(file, chunk_size);
3079 return chunk_size_expected;
3083 level->num_yamyam_contents = getFile8Bit(file);
3087 // correct invalid number of content fields -- should never happen
3088 if (level->num_yamyam_contents < 1 ||
3089 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3090 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3092 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3093 for (y = 0; y < 3; y++)
3094 for (x = 0; x < 3; x++)
3095 level->yamyam_content[i].e[x][y] =
3096 getMappedElement(level->encoding_16bit_field ?
3097 getFile16BitBE(file) : getFile8Bit(file));
3101 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3106 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3108 element = getMappedElement(getFile16BitBE(file));
3109 num_contents = getFile8Bit(file);
3111 getFile8Bit(file); // content x size (unused)
3112 getFile8Bit(file); // content y size (unused)
3114 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3116 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3117 for (y = 0; y < 3; y++)
3118 for (x = 0; x < 3; x++)
3119 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3121 // correct invalid number of content fields -- should never happen
3122 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3123 num_contents = STD_ELEMENT_CONTENTS;
3125 if (element == EL_YAMYAM)
3127 level->num_yamyam_contents = num_contents;
3129 for (i = 0; i < num_contents; i++)
3130 for (y = 0; y < 3; y++)
3131 for (x = 0; x < 3; x++)
3132 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3134 else if (element == EL_BD_AMOEBA)
3136 level->amoeba_content = content_array[0][0][0];
3140 Warn("cannot load content for element '%d'", element);
3146 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3152 int chunk_size_expected;
3154 element = getMappedElement(getFile16BitBE(file));
3155 if (!IS_ENVELOPE(element))
3156 element = EL_ENVELOPE_1;
3158 envelope_nr = element - EL_ENVELOPE_1;
3160 envelope_len = getFile16BitBE(file);
3162 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3163 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3165 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3167 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3168 if (chunk_size_expected != chunk_size)
3170 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3171 return chunk_size_expected;
3174 for (i = 0; i < envelope_len; i++)
3175 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3180 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3182 int num_changed_custom_elements = getFile16BitBE(file);
3183 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3186 if (chunk_size_expected != chunk_size)
3188 ReadUnusedBytesFromFile(file, chunk_size - 2);
3189 return chunk_size_expected;
3192 for (i = 0; i < num_changed_custom_elements; i++)
3194 int element = getMappedElement(getFile16BitBE(file));
3195 int properties = getFile32BitBE(file);
3197 if (IS_CUSTOM_ELEMENT(element))
3198 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3200 Warn("invalid custom element number %d", element);
3202 // older game versions that wrote level files with CUS1 chunks used
3203 // different default push delay values (not yet stored in level file)
3204 element_info[element].push_delay_fixed = 2;
3205 element_info[element].push_delay_random = 8;
3208 level->file_has_custom_elements = TRUE;
3213 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3215 int num_changed_custom_elements = getFile16BitBE(file);
3216 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3219 if (chunk_size_expected != chunk_size)
3221 ReadUnusedBytesFromFile(file, chunk_size - 2);
3222 return chunk_size_expected;
3225 for (i = 0; i < num_changed_custom_elements; i++)
3227 int element = getMappedElement(getFile16BitBE(file));
3228 int custom_target_element = getMappedElement(getFile16BitBE(file));
3230 if (IS_CUSTOM_ELEMENT(element))
3231 element_info[element].change->target_element = custom_target_element;
3233 Warn("invalid custom element number %d", element);
3236 level->file_has_custom_elements = TRUE;
3241 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3243 int num_changed_custom_elements = getFile16BitBE(file);
3244 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3247 if (chunk_size_expected != chunk_size)
3249 ReadUnusedBytesFromFile(file, chunk_size - 2);
3250 return chunk_size_expected;
3253 for (i = 0; i < num_changed_custom_elements; i++)
3255 int element = getMappedElement(getFile16BitBE(file));
3256 struct ElementInfo *ei = &element_info[element];
3257 unsigned int event_bits;
3259 if (!IS_CUSTOM_ELEMENT(element))
3261 Warn("invalid custom element number %d", element);
3263 element = EL_INTERNAL_DUMMY;
3266 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3267 ei->description[j] = getFile8Bit(file);
3268 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3270 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3272 // some free bytes for future properties and padding
3273 ReadUnusedBytesFromFile(file, 7);
3275 ei->use_gfx_element = getFile8Bit(file);
3276 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3278 ei->collect_score_initial = getFile8Bit(file);
3279 ei->collect_count_initial = getFile8Bit(file);
3281 ei->push_delay_fixed = getFile16BitBE(file);
3282 ei->push_delay_random = getFile16BitBE(file);
3283 ei->move_delay_fixed = getFile16BitBE(file);
3284 ei->move_delay_random = getFile16BitBE(file);
3286 ei->move_pattern = getFile16BitBE(file);
3287 ei->move_direction_initial = getFile8Bit(file);
3288 ei->move_stepsize = getFile8Bit(file);
3290 for (y = 0; y < 3; y++)
3291 for (x = 0; x < 3; x++)
3292 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3294 // bits 0 - 31 of "has_event[]"
3295 event_bits = getFile32BitBE(file);
3296 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3297 if (event_bits & (1u << j))
3298 ei->change->has_event[j] = TRUE;
3300 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3302 ei->change->delay_fixed = getFile16BitBE(file);
3303 ei->change->delay_random = getFile16BitBE(file);
3304 ei->change->delay_frames = getFile16BitBE(file);
3306 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3308 ei->change->explode = getFile8Bit(file);
3309 ei->change->use_target_content = getFile8Bit(file);
3310 ei->change->only_if_complete = getFile8Bit(file);
3311 ei->change->use_random_replace = getFile8Bit(file);
3313 ei->change->random_percentage = getFile8Bit(file);
3314 ei->change->replace_when = getFile8Bit(file);
3316 for (y = 0; y < 3; y++)
3317 for (x = 0; x < 3; x++)
3318 ei->change->target_content.e[x][y] =
3319 getMappedElement(getFile16BitBE(file));
3321 ei->slippery_type = getFile8Bit(file);
3323 // some free bytes for future properties and padding
3324 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3326 // mark that this custom element has been modified
3327 ei->modified_settings = TRUE;
3330 level->file_has_custom_elements = TRUE;
3335 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3337 struct ElementInfo *ei;
3338 int chunk_size_expected;
3342 // ---------- custom element base property values (96 bytes) ----------------
3344 element = getMappedElement(getFile16BitBE(file));
3346 if (!IS_CUSTOM_ELEMENT(element))
3348 Warn("invalid custom element number %d", element);
3350 ReadUnusedBytesFromFile(file, chunk_size - 2);
3355 ei = &element_info[element];
3357 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3358 ei->description[i] = getFile8Bit(file);
3359 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3361 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3363 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3365 ei->num_change_pages = getFile8Bit(file);
3367 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3368 if (chunk_size_expected != chunk_size)
3370 ReadUnusedBytesFromFile(file, chunk_size - 43);
3371 return chunk_size_expected;
3374 ei->ce_value_fixed_initial = getFile16BitBE(file);
3375 ei->ce_value_random_initial = getFile16BitBE(file);
3376 ei->use_last_ce_value = getFile8Bit(file);
3378 ei->use_gfx_element = getFile8Bit(file);
3379 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3381 ei->collect_score_initial = getFile8Bit(file);
3382 ei->collect_count_initial = getFile8Bit(file);
3384 ei->drop_delay_fixed = getFile8Bit(file);
3385 ei->push_delay_fixed = getFile8Bit(file);
3386 ei->drop_delay_random = getFile8Bit(file);
3387 ei->push_delay_random = getFile8Bit(file);
3388 ei->move_delay_fixed = getFile16BitBE(file);
3389 ei->move_delay_random = getFile16BitBE(file);
3391 // bits 0 - 15 of "move_pattern" ...
3392 ei->move_pattern = getFile16BitBE(file);
3393 ei->move_direction_initial = getFile8Bit(file);
3394 ei->move_stepsize = getFile8Bit(file);
3396 ei->slippery_type = getFile8Bit(file);
3398 for (y = 0; y < 3; y++)
3399 for (x = 0; x < 3; x++)
3400 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3402 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3403 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3404 ei->move_leave_type = getFile8Bit(file);
3406 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3407 ei->move_pattern |= (getFile16BitBE(file) << 16);
3409 ei->access_direction = getFile8Bit(file);
3411 ei->explosion_delay = getFile8Bit(file);
3412 ei->ignition_delay = getFile8Bit(file);
3413 ei->explosion_type = getFile8Bit(file);
3415 // some free bytes for future custom property values and padding
3416 ReadUnusedBytesFromFile(file, 1);
3418 // ---------- change page property values (48 bytes) ------------------------
3420 setElementChangePages(ei, ei->num_change_pages);
3422 for (i = 0; i < ei->num_change_pages; i++)
3424 struct ElementChangeInfo *change = &ei->change_page[i];
3425 unsigned int event_bits;
3427 // always start with reliable default values
3428 setElementChangeInfoToDefaults(change);
3430 // bits 0 - 31 of "has_event[]" ...
3431 event_bits = getFile32BitBE(file);
3432 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3433 if (event_bits & (1u << j))
3434 change->has_event[j] = TRUE;
3436 change->target_element = getMappedElement(getFile16BitBE(file));
3438 change->delay_fixed = getFile16BitBE(file);
3439 change->delay_random = getFile16BitBE(file);
3440 change->delay_frames = getFile16BitBE(file);
3442 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3444 change->explode = getFile8Bit(file);
3445 change->use_target_content = getFile8Bit(file);
3446 change->only_if_complete = getFile8Bit(file);
3447 change->use_random_replace = getFile8Bit(file);
3449 change->random_percentage = getFile8Bit(file);
3450 change->replace_when = getFile8Bit(file);
3452 for (y = 0; y < 3; y++)
3453 for (x = 0; x < 3; x++)
3454 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3456 change->can_change = getFile8Bit(file);
3458 change->trigger_side = getFile8Bit(file);
3460 change->trigger_player = getFile8Bit(file);
3461 change->trigger_page = getFile8Bit(file);
3463 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3464 CH_PAGE_ANY : (1 << change->trigger_page));
3466 change->has_action = getFile8Bit(file);
3467 change->action_type = getFile8Bit(file);
3468 change->action_mode = getFile8Bit(file);
3469 change->action_arg = getFile16BitBE(file);
3471 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3472 event_bits = getFile8Bit(file);
3473 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3474 if (event_bits & (1u << (j - 32)))
3475 change->has_event[j] = TRUE;
3478 // mark this custom element as modified
3479 ei->modified_settings = TRUE;
3481 level->file_has_custom_elements = TRUE;
3486 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3488 struct ElementInfo *ei;
3489 struct ElementGroupInfo *group;
3493 element = getMappedElement(getFile16BitBE(file));
3495 if (!IS_GROUP_ELEMENT(element))
3497 Warn("invalid group element number %d", element);
3499 ReadUnusedBytesFromFile(file, chunk_size - 2);
3504 ei = &element_info[element];
3506 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3507 ei->description[i] = getFile8Bit(file);
3508 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3510 group = element_info[element].group;
3512 group->num_elements = getFile8Bit(file);
3514 ei->use_gfx_element = getFile8Bit(file);
3515 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3517 group->choice_mode = getFile8Bit(file);
3519 // some free bytes for future values and padding
3520 ReadUnusedBytesFromFile(file, 3);
3522 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3523 group->element[i] = getMappedElement(getFile16BitBE(file));
3525 // mark this group element as modified
3526 element_info[element].modified_settings = TRUE;
3528 level->file_has_custom_elements = TRUE;
3533 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3534 int element, int real_element)
3536 int micro_chunk_size = 0;
3537 int conf_type = getFile8Bit(file);
3538 int byte_mask = conf_type & CONF_MASK_BYTES;
3539 boolean element_found = FALSE;
3542 micro_chunk_size += 1;
3544 if (byte_mask == CONF_MASK_MULTI_BYTES)
3546 int num_bytes = getFile16BitBE(file);
3547 byte *buffer = checked_malloc(num_bytes);
3549 ReadBytesFromFile(file, buffer, num_bytes);
3551 for (i = 0; conf[i].data_type != -1; i++)
3553 if (conf[i].element == element &&
3554 conf[i].conf_type == conf_type)
3556 int data_type = conf[i].data_type;
3557 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3558 int max_num_entities = conf[i].max_num_entities;
3560 if (num_entities > max_num_entities)
3562 Warn("truncating number of entities for element %d from %d to %d",
3563 element, num_entities, max_num_entities);
3565 num_entities = max_num_entities;
3568 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3569 data_type == TYPE_CONTENT_LIST))
3571 // for element and content lists, zero entities are not allowed
3572 Warn("found empty list of entities for element %d", element);
3574 // do not set "num_entities" here to prevent reading behind buffer
3576 *(int *)(conf[i].num_entities) = 1; // at least one is required
3580 *(int *)(conf[i].num_entities) = num_entities;
3583 element_found = TRUE;
3585 if (data_type == TYPE_STRING)
3587 char *string = (char *)(conf[i].value);
3590 for (j = 0; j < max_num_entities; j++)
3591 string[j] = (j < num_entities ? buffer[j] : '\0');
3593 else if (data_type == TYPE_ELEMENT_LIST)
3595 int *element_array = (int *)(conf[i].value);
3598 for (j = 0; j < num_entities; j++)
3600 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3602 else if (data_type == TYPE_CONTENT_LIST)
3604 struct Content *content= (struct Content *)(conf[i].value);
3607 for (c = 0; c < num_entities; c++)
3608 for (y = 0; y < 3; y++)
3609 for (x = 0; x < 3; x++)
3610 content[c].e[x][y] =
3611 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3614 element_found = FALSE;
3620 checked_free(buffer);
3622 micro_chunk_size += 2 + num_bytes;
3624 else // constant size configuration data (1, 2 or 4 bytes)
3626 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3627 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3628 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3630 for (i = 0; conf[i].data_type != -1; i++)
3632 if (conf[i].element == element &&
3633 conf[i].conf_type == conf_type)
3635 int data_type = conf[i].data_type;
3637 if (data_type == TYPE_ELEMENT)
3638 value = getMappedElement(value);
3640 if (data_type == TYPE_BOOLEAN)
3641 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3643 *(int *) (conf[i].value) = value;
3645 element_found = TRUE;
3651 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3656 char *error_conf_chunk_bytes =
3657 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3658 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3659 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3660 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3661 int error_element = real_element;
3663 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3664 error_conf_chunk_bytes, error_conf_chunk_token,
3665 error_element, EL_NAME(error_element));
3668 return micro_chunk_size;
3671 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3673 int real_chunk_size = 0;
3675 li = *level; // copy level data into temporary buffer
3677 while (!checkEndOfFile(file))
3679 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3681 if (real_chunk_size >= chunk_size)
3685 *level = li; // copy temporary buffer back to level data
3687 return real_chunk_size;
3690 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3692 int real_chunk_size = 0;
3694 li = *level; // copy level data into temporary buffer
3696 while (!checkEndOfFile(file))
3698 int element = getMappedElement(getFile16BitBE(file));
3700 real_chunk_size += 2;
3701 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
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_ELEM(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_ELEM,
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_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3736 int element = getMappedElement(getFile16BitBE(file));
3737 int envelope_nr = element - EL_ENVELOPE_1;
3738 int real_chunk_size = 2;
3740 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3742 while (!checkEndOfFile(file))
3744 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3747 if (real_chunk_size >= chunk_size)
3751 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3753 return real_chunk_size;
3756 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3758 int element = getMappedElement(getFile16BitBE(file));
3759 int real_chunk_size = 2;
3760 struct ElementInfo *ei = &element_info[element];
3763 xx_ei = *ei; // copy element data into temporary buffer
3765 xx_ei.num_change_pages = -1;
3767 while (!checkEndOfFile(file))
3769 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3771 if (xx_ei.num_change_pages != -1)
3774 if (real_chunk_size >= chunk_size)
3780 if (ei->num_change_pages == -1)
3782 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3785 ei->num_change_pages = 1;
3787 setElementChangePages(ei, 1);
3788 setElementChangeInfoToDefaults(ei->change);
3790 return real_chunk_size;
3793 // initialize number of change pages stored for this custom element
3794 setElementChangePages(ei, ei->num_change_pages);
3795 for (i = 0; i < ei->num_change_pages; i++)
3796 setElementChangeInfoToDefaults(&ei->change_page[i]);
3798 // start with reading properties for the first change page
3799 xx_current_change_page = 0;
3801 while (!checkEndOfFile(file))
3803 // level file might contain invalid change page number
3804 if (xx_current_change_page >= ei->num_change_pages)
3807 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3809 xx_change = *change; // copy change data into temporary buffer
3811 resetEventBits(); // reset bits; change page might have changed
3813 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3816 *change = xx_change;
3818 setEventFlagsFromEventBits(change);
3820 if (real_chunk_size >= chunk_size)
3824 level->file_has_custom_elements = TRUE;
3826 return real_chunk_size;
3829 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3831 int element = getMappedElement(getFile16BitBE(file));
3832 int real_chunk_size = 2;
3833 struct ElementInfo *ei = &element_info[element];
3834 struct ElementGroupInfo *group = ei->group;
3839 xx_ei = *ei; // copy element data into temporary buffer
3840 xx_group = *group; // copy group data into temporary buffer
3842 while (!checkEndOfFile(file))
3844 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3847 if (real_chunk_size >= chunk_size)
3854 level->file_has_custom_elements = TRUE;
3856 return real_chunk_size;
3859 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3861 int element = getMappedElement(getFile16BitBE(file));
3862 int real_chunk_size = 2;
3863 struct ElementInfo *ei = &element_info[element];
3865 xx_ei = *ei; // copy element data into temporary buffer
3867 while (!checkEndOfFile(file))
3869 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3872 if (real_chunk_size >= chunk_size)
3878 level->file_has_custom_elements = TRUE;
3880 return real_chunk_size;
3883 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3884 struct LevelFileInfo *level_file_info,
3885 boolean level_info_only)
3887 char *filename = level_file_info->filename;
3888 char cookie[MAX_LINE_LEN];
3889 char chunk_name[CHUNK_ID_LEN + 1];
3893 if (!(file = openFile(filename, MODE_READ)))
3895 level->no_valid_file = TRUE;
3896 level->no_level_file = TRUE;
3898 if (level_info_only)
3901 Warn("cannot read level '%s' -- using empty level", filename);
3903 if (!setup.editor.use_template_for_new_levels)
3906 // if level file not found, try to initialize level data from template
3907 filename = getGlobalLevelTemplateFilename();
3909 if (!(file = openFile(filename, MODE_READ)))
3912 // default: for empty levels, use level template for custom elements
3913 level->use_custom_template = TRUE;
3915 level->no_valid_file = FALSE;
3918 getFileChunkBE(file, chunk_name, NULL);
3919 if (strEqual(chunk_name, "RND1"))
3921 getFile32BitBE(file); // not used
3923 getFileChunkBE(file, chunk_name, NULL);
3924 if (!strEqual(chunk_name, "CAVE"))
3926 level->no_valid_file = TRUE;
3928 Warn("unknown format of level file '%s'", filename);
3935 else // check for pre-2.0 file format with cookie string
3937 strcpy(cookie, chunk_name);
3938 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3940 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3941 cookie[strlen(cookie) - 1] = '\0';
3943 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3945 level->no_valid_file = TRUE;
3947 Warn("unknown format of level file '%s'", filename);
3954 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3956 level->no_valid_file = TRUE;
3958 Warn("unsupported version of level file '%s'", filename);
3965 // pre-2.0 level files have no game version, so use file version here
3966 level->game_version = level->file_version;
3969 if (level->file_version < FILE_VERSION_1_2)
3971 // level files from versions before 1.2.0 without chunk structure
3972 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3973 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3981 int (*loader)(File *, int, struct LevelInfo *);
3985 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3986 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3987 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3988 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3989 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3990 { "INFO", -1, LoadLevel_INFO },
3991 { "BODY", -1, LoadLevel_BODY },
3992 { "CONT", -1, LoadLevel_CONT },
3993 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3994 { "CNT3", -1, LoadLevel_CNT3 },
3995 { "CUS1", -1, LoadLevel_CUS1 },
3996 { "CUS2", -1, LoadLevel_CUS2 },
3997 { "CUS3", -1, LoadLevel_CUS3 },
3998 { "CUS4", -1, LoadLevel_CUS4 },
3999 { "GRP1", -1, LoadLevel_GRP1 },
4000 { "CONF", -1, LoadLevel_CONF },
4001 { "ELEM", -1, LoadLevel_ELEM },
4002 { "NOTE", -1, LoadLevel_NOTE },
4003 { "CUSX", -1, LoadLevel_CUSX },
4004 { "GRPX", -1, LoadLevel_GRPX },
4005 { "EMPX", -1, LoadLevel_EMPX },
4010 while (getFileChunkBE(file, chunk_name, &chunk_size))
4014 while (chunk_info[i].name != NULL &&
4015 !strEqual(chunk_name, chunk_info[i].name))
4018 if (chunk_info[i].name == NULL)
4020 Warn("unknown chunk '%s' in level file '%s'",
4021 chunk_name, filename);
4023 ReadUnusedBytesFromFile(file, chunk_size);
4025 else if (chunk_info[i].size != -1 &&
4026 chunk_info[i].size != chunk_size)
4028 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4029 chunk_size, chunk_name, filename);
4031 ReadUnusedBytesFromFile(file, chunk_size);
4035 // call function to load this level chunk
4036 int chunk_size_expected =
4037 (chunk_info[i].loader)(file, chunk_size, level);
4039 if (chunk_size_expected < 0)
4041 Warn("error reading chunk '%s' in level file '%s'",
4042 chunk_name, filename);
4047 // the size of some chunks cannot be checked before reading other
4048 // chunks first (like "HEAD" and "BODY") that contain some header
4049 // information, so check them here
4050 if (chunk_size_expected != chunk_size)
4052 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4053 chunk_size, chunk_name, filename);
4065 // ----------------------------------------------------------------------------
4066 // functions for loading BD level
4067 // ----------------------------------------------------------------------------
4069 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4071 struct LevelInfo_BD *level_bd = level->native_bd_level;
4072 GdCave *cave = NULL; // will be changed below
4073 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4074 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4077 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4079 // cave and map newly allocated when set to defaults above
4080 cave = level_bd->cave;
4083 cave->intermission = level->bd_intermission;
4086 cave->level_time[0] = level->time;
4087 cave->level_diamonds[0] = level->gems_needed;
4090 cave->scheduling = level->bd_scheduling_type;
4091 cave->pal_timing = level->bd_pal_timing;
4092 cave->level_speed[0] = level->bd_cycle_delay_ms;
4093 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4094 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4095 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4098 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4099 cave->diamond_value = level->score[SC_EMERALD];
4100 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4102 // compatibility settings
4103 cave->lineshift = level->bd_line_shifting_borders;
4104 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4105 cave->short_explosions = level->bd_short_explosions;
4106 cave->gravity_affects_all = level->bd_gravity_affects_all;
4108 // player properties
4109 cave->diagonal_movements = level->bd_diagonal_movements;
4110 cave->active_is_first_found = level->bd_topmost_player_active;
4111 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4112 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4113 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4114 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4116 // element properties
4117 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4118 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4119 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4120 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4121 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4122 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4123 cave->level_magic_wall_time[0] = level->time_magic_wall;
4124 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4125 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4127 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4128 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4129 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4130 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4131 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4132 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4133 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4135 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4136 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4137 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4138 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4139 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4140 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4141 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4142 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4143 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4144 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4145 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4147 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4148 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4149 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4150 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4151 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4152 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4154 cave->slime_predictable = level->bd_slime_is_predictable;
4155 cave->slime_correct_random = level->bd_slime_correct_random;
4156 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4157 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4158 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4159 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4160 cave->slime_eats_1 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4161 cave->slime_converts_1 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4162 cave->slime_eats_2 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4163 cave->slime_converts_2 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4164 cave->slime_eats_3 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4165 cave->slime_converts_3 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4167 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4168 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4169 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4171 cave->biter_delay_frame = level->bd_biter_move_delay;
4172 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4174 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4176 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4178 cave->replicators_active = level->bd_replicators_active;
4179 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4181 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4182 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4184 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4186 cave->nut_turns_to_when_crushed = map_element_RND_to_BD_cave(level->bd_nut_content);
4188 cave->pneumatic_hammer_frame = level->bd_hammer_walls_break_delay;
4189 cave->hammered_walls_reappear = level->bd_hammer_walls_reappear;
4190 cave->hammered_wall_reappear_frame = level->bd_hammer_walls_reappear_delay;
4193 strncpy(cave->name, level->name, sizeof(GdString));
4194 cave->name[sizeof(GdString) - 1] = '\0';
4196 // playfield elements
4197 for (x = 0; x < cave->w; x++)
4198 for (y = 0; y < cave->h; y++)
4199 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4202 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4204 struct LevelInfo_BD *level_bd = level->native_bd_level;
4205 GdCave *cave = level_bd->cave;
4206 int bd_level_nr = level_bd->level_nr;
4209 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4210 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4213 level->bd_intermission = cave->intermission;
4216 level->time = cave->level_time[bd_level_nr];
4217 level->gems_needed = cave->level_diamonds[bd_level_nr];
4220 level->bd_scheduling_type = cave->scheduling;
4221 level->bd_pal_timing = cave->pal_timing;
4222 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4223 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4224 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4225 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4228 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4229 level->score[SC_EMERALD] = cave->diamond_value;
4230 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4232 // compatibility settings
4233 level->bd_line_shifting_borders = cave->lineshift;
4234 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4235 level->bd_short_explosions = cave->short_explosions;
4236 level->bd_gravity_affects_all = cave->gravity_affects_all;
4238 // player properties
4239 level->bd_diagonal_movements = cave->diagonal_movements;
4240 level->bd_topmost_player_active = cave->active_is_first_found;
4241 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4242 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4243 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4244 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4246 // element properties
4247 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4248 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4249 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4250 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4251 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4252 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4253 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4254 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4255 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4257 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4258 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4259 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4260 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4261 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4262 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4263 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4265 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4266 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4267 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4268 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4269 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4270 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4271 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4272 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4273 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4274 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4275 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4277 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4278 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4279 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4280 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4281 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4282 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4284 level->bd_slime_is_predictable = cave->slime_predictable;
4285 level->bd_slime_correct_random = cave->slime_correct_random;
4286 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4287 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4288 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4289 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4290 level->bd_slime_eats_element_1 = map_element_BD_to_RND_cave(cave->slime_eats_1);
4291 level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4292 level->bd_slime_eats_element_2 = map_element_BD_to_RND_cave(cave->slime_eats_2);
4293 level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4294 level->bd_slime_eats_element_3 = map_element_BD_to_RND_cave(cave->slime_eats_3);
4295 level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4297 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4298 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4299 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4301 level->bd_biter_move_delay = cave->biter_delay_frame;
4302 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4304 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4306 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4308 level->bd_replicators_active = cave->replicators_active;
4309 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4311 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4312 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4314 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4316 level->bd_nut_content = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4318 level->bd_hammer_walls_break_delay = cave->pneumatic_hammer_frame;
4319 level->bd_hammer_walls_reappear = cave->hammered_walls_reappear;
4320 level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4323 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4325 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4326 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4328 // playfield elements
4329 for (x = 0; x < level->fieldx; x++)
4330 for (y = 0; y < level->fieldy; y++)
4331 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4333 checked_free(cave_name);
4336 static void setTapeInfoToDefaults(void);
4338 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4340 struct LevelInfo_BD *level_bd = level->native_bd_level;
4341 GdCave *cave = level_bd->cave;
4342 GdReplay *replay = level_bd->replay;
4348 // always start with reliable default values
4349 setTapeInfoToDefaults();
4351 tape.level_nr = level_nr; // (currently not used)
4352 tape.random_seed = replay->seed;
4354 TapeSetDateFromIsoDateString(replay->date);
4357 tape.pos[tape.counter].delay = 0;
4359 tape.bd_replay = TRUE;
4361 // all time calculations only used to display approximate tape time
4362 int cave_speed = cave->speed;
4363 int milliseconds_game = 0;
4364 int milliseconds_elapsed = 20;
4366 for (i = 0; i < replay->movements->len; i++)
4368 int replay_action = replay->movements->data[i];
4369 int tape_action = map_action_BD_to_RND(replay_action);
4370 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4371 boolean success = 0;
4375 success = TapeAddAction(action);
4377 milliseconds_game += milliseconds_elapsed;
4379 if (milliseconds_game >= cave_speed)
4381 milliseconds_game -= cave_speed;
4388 tape.pos[tape.counter].delay = 0;
4389 tape.pos[tape.counter].action[0] = 0;
4393 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4399 TapeHaltRecording();
4403 // ----------------------------------------------------------------------------
4404 // functions for loading EM level
4405 // ----------------------------------------------------------------------------
4407 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4409 static int ball_xy[8][2] =
4420 struct LevelInfo_EM *level_em = level->native_em_level;
4421 struct CAVE *cav = level_em->cav;
4424 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4425 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4427 cav->time_seconds = level->time;
4428 cav->gems_needed = level->gems_needed;
4430 cav->emerald_score = level->score[SC_EMERALD];
4431 cav->diamond_score = level->score[SC_DIAMOND];
4432 cav->alien_score = level->score[SC_ROBOT];
4433 cav->tank_score = level->score[SC_SPACESHIP];
4434 cav->bug_score = level->score[SC_BUG];
4435 cav->eater_score = level->score[SC_YAMYAM];
4436 cav->nut_score = level->score[SC_NUT];
4437 cav->dynamite_score = level->score[SC_DYNAMITE];
4438 cav->key_score = level->score[SC_KEY];
4439 cav->exit_score = level->score[SC_TIME_BONUS];
4441 cav->num_eater_arrays = level->num_yamyam_contents;
4443 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4444 for (y = 0; y < 3; y++)
4445 for (x = 0; x < 3; x++)
4446 cav->eater_array[i][y * 3 + x] =
4447 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4449 cav->amoeba_time = level->amoeba_speed;
4450 cav->wonderwall_time = level->time_magic_wall;
4451 cav->wheel_time = level->time_wheel;
4453 cav->android_move_time = level->android_move_time;
4454 cav->android_clone_time = level->android_clone_time;
4455 cav->ball_random = level->ball_random;
4456 cav->ball_active = level->ball_active_initial;
4457 cav->ball_time = level->ball_time;
4458 cav->num_ball_arrays = level->num_ball_contents;
4460 cav->lenses_score = level->lenses_score;
4461 cav->magnify_score = level->magnify_score;
4462 cav->slurp_score = level->slurp_score;
4464 cav->lenses_time = level->lenses_time;
4465 cav->magnify_time = level->magnify_time;
4467 cav->wind_time = 9999;
4468 cav->wind_direction =
4469 map_direction_RND_to_EM(level->wind_direction_initial);
4471 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4472 for (j = 0; j < 8; j++)
4473 cav->ball_array[i][j] =
4474 map_element_RND_to_EM_cave(level->ball_content[i].
4475 e[ball_xy[j][0]][ball_xy[j][1]]);
4477 map_android_clone_elements_RND_to_EM(level);
4479 // first fill the complete playfield with the empty space element
4480 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4481 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4482 cav->cave[x][y] = Cblank;
4484 // then copy the real level contents from level file into the playfield
4485 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4487 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4489 if (level->field[x][y] == EL_AMOEBA_DEAD)
4490 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4492 cav->cave[x][y] = new_element;
4495 for (i = 0; i < MAX_PLAYERS; i++)
4497 cav->player_x[i] = -1;
4498 cav->player_y[i] = -1;
4501 // initialize player positions and delete players from the playfield
4502 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4504 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4506 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4508 cav->player_x[player_nr] = x;
4509 cav->player_y[player_nr] = y;
4511 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4516 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4518 static int ball_xy[8][2] =
4529 struct LevelInfo_EM *level_em = level->native_em_level;
4530 struct CAVE *cav = level_em->cav;
4533 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4534 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4536 level->time = cav->time_seconds;
4537 level->gems_needed = cav->gems_needed;
4539 sprintf(level->name, "Level %d", level->file_info.nr);
4541 level->score[SC_EMERALD] = cav->emerald_score;
4542 level->score[SC_DIAMOND] = cav->diamond_score;
4543 level->score[SC_ROBOT] = cav->alien_score;
4544 level->score[SC_SPACESHIP] = cav->tank_score;
4545 level->score[SC_BUG] = cav->bug_score;
4546 level->score[SC_YAMYAM] = cav->eater_score;
4547 level->score[SC_NUT] = cav->nut_score;
4548 level->score[SC_DYNAMITE] = cav->dynamite_score;
4549 level->score[SC_KEY] = cav->key_score;
4550 level->score[SC_TIME_BONUS] = cav->exit_score;
4552 level->num_yamyam_contents = cav->num_eater_arrays;
4554 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4555 for (y = 0; y < 3; y++)
4556 for (x = 0; x < 3; x++)
4557 level->yamyam_content[i].e[x][y] =
4558 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4560 level->amoeba_speed = cav->amoeba_time;
4561 level->time_magic_wall = cav->wonderwall_time;
4562 level->time_wheel = cav->wheel_time;
4564 level->android_move_time = cav->android_move_time;
4565 level->android_clone_time = cav->android_clone_time;
4566 level->ball_random = cav->ball_random;
4567 level->ball_active_initial = cav->ball_active;
4568 level->ball_time = cav->ball_time;
4569 level->num_ball_contents = cav->num_ball_arrays;
4571 level->lenses_score = cav->lenses_score;
4572 level->magnify_score = cav->magnify_score;
4573 level->slurp_score = cav->slurp_score;
4575 level->lenses_time = cav->lenses_time;
4576 level->magnify_time = cav->magnify_time;
4578 level->wind_direction_initial =
4579 map_direction_EM_to_RND(cav->wind_direction);
4581 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4582 for (j = 0; j < 8; j++)
4583 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4584 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4586 map_android_clone_elements_EM_to_RND(level);
4588 // convert the playfield (some elements need special treatment)
4589 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4591 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4593 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4594 new_element = EL_AMOEBA_DEAD;
4596 level->field[x][y] = new_element;
4599 for (i = 0; i < MAX_PLAYERS; i++)
4601 // in case of all players set to the same field, use the first player
4602 int nr = MAX_PLAYERS - i - 1;
4603 int jx = cav->player_x[nr];
4604 int jy = cav->player_y[nr];
4606 if (jx != -1 && jy != -1)
4607 level->field[jx][jy] = EL_PLAYER_1 + nr;
4610 // time score is counted for each 10 seconds left in Emerald Mine levels
4611 level->time_score_base = 10;
4615 // ----------------------------------------------------------------------------
4616 // functions for loading SP level
4617 // ----------------------------------------------------------------------------
4619 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4621 struct LevelInfo_SP *level_sp = level->native_sp_level;
4622 LevelInfoType *header = &level_sp->header;
4625 level_sp->width = level->fieldx;
4626 level_sp->height = level->fieldy;
4628 for (x = 0; x < level->fieldx; x++)
4629 for (y = 0; y < level->fieldy; y++)
4630 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4632 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4634 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4635 header->LevelTitle[i] = level->name[i];
4636 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4638 header->InfotronsNeeded = level->gems_needed;
4640 header->SpecialPortCount = 0;
4642 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4644 boolean gravity_port_found = FALSE;
4645 boolean gravity_port_valid = FALSE;
4646 int gravity_port_flag;
4647 int gravity_port_base_element;
4648 int element = level->field[x][y];
4650 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4651 element <= EL_SP_GRAVITY_ON_PORT_UP)
4653 gravity_port_found = TRUE;
4654 gravity_port_valid = TRUE;
4655 gravity_port_flag = 1;
4656 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4658 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4659 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4661 gravity_port_found = TRUE;
4662 gravity_port_valid = TRUE;
4663 gravity_port_flag = 0;
4664 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4666 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4667 element <= EL_SP_GRAVITY_PORT_UP)
4669 // change R'n'D style gravity inverting special port to normal port
4670 // (there are no gravity inverting ports in native Supaplex engine)
4672 gravity_port_found = TRUE;
4673 gravity_port_valid = FALSE;
4674 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4677 if (gravity_port_found)
4679 if (gravity_port_valid &&
4680 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4682 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4684 port->PortLocation = (y * level->fieldx + x) * 2;
4685 port->Gravity = gravity_port_flag;
4687 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4689 header->SpecialPortCount++;
4693 // change special gravity port to normal port
4695 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4698 level_sp->playfield[x][y] = element - EL_SP_START;
4703 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4705 struct LevelInfo_SP *level_sp = level->native_sp_level;
4706 LevelInfoType *header = &level_sp->header;
4707 boolean num_invalid_elements = 0;
4710 level->fieldx = level_sp->width;
4711 level->fieldy = level_sp->height;
4713 for (x = 0; x < level->fieldx; x++)
4715 for (y = 0; y < level->fieldy; y++)
4717 int element_old = level_sp->playfield[x][y];
4718 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4720 if (element_new == EL_UNKNOWN)
4722 num_invalid_elements++;
4724 Debug("level:native:SP", "invalid element %d at position %d, %d",
4728 level->field[x][y] = element_new;
4732 if (num_invalid_elements > 0)
4733 Warn("found %d invalid elements%s", num_invalid_elements,
4734 (!options.debug ? " (use '--debug' for more details)" : ""));
4736 for (i = 0; i < MAX_PLAYERS; i++)
4737 level->initial_player_gravity[i] =
4738 (header->InitialGravity == 1 ? TRUE : FALSE);
4740 // skip leading spaces
4741 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4742 if (header->LevelTitle[i] != ' ')
4746 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4747 level->name[j] = header->LevelTitle[i];
4748 level->name[j] = '\0';
4750 // cut trailing spaces
4752 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4753 level->name[j - 1] = '\0';
4755 level->gems_needed = header->InfotronsNeeded;
4757 for (i = 0; i < header->SpecialPortCount; i++)
4759 SpecialPortType *port = &header->SpecialPort[i];
4760 int port_location = port->PortLocation;
4761 int gravity = port->Gravity;
4762 int port_x, port_y, port_element;
4764 port_x = (port_location / 2) % level->fieldx;
4765 port_y = (port_location / 2) / level->fieldx;
4767 if (port_x < 0 || port_x >= level->fieldx ||
4768 port_y < 0 || port_y >= level->fieldy)
4770 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4775 port_element = level->field[port_x][port_y];
4777 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4778 port_element > EL_SP_GRAVITY_PORT_UP)
4780 Warn("no special port at position (%d, %d)", port_x, port_y);
4785 // change previous (wrong) gravity inverting special port to either
4786 // gravity enabling special port or gravity disabling special port
4787 level->field[port_x][port_y] +=
4788 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4789 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4792 // change special gravity ports without database entries to normal ports
4793 for (x = 0; x < level->fieldx; x++)
4794 for (y = 0; y < level->fieldy; y++)
4795 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4796 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4797 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4799 level->time = 0; // no time limit
4800 level->amoeba_speed = 0;
4801 level->time_magic_wall = 0;
4802 level->time_wheel = 0;
4803 level->amoeba_content = EL_EMPTY;
4805 // original Supaplex does not use score values -- rate by playing time
4806 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4807 level->score[i] = 0;
4809 level->rate_time_over_score = TRUE;
4811 // there are no yamyams in supaplex levels
4812 for (i = 0; i < level->num_yamyam_contents; i++)
4813 for (x = 0; x < 3; x++)
4814 for (y = 0; y < 3; y++)
4815 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4818 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4820 struct LevelInfo_SP *level_sp = level->native_sp_level;
4821 struct DemoInfo_SP *demo = &level_sp->demo;
4824 // always start with reliable default values
4825 demo->is_available = FALSE;
4828 if (TAPE_IS_EMPTY(tape))
4831 demo->level_nr = tape.level_nr; // (currently not used)
4833 level_sp->header.DemoRandomSeed = tape.random_seed;
4837 for (i = 0; i < tape.length; i++)
4839 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4840 int demo_repeat = tape.pos[i].delay;
4841 int demo_entries = (demo_repeat + 15) / 16;
4843 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4845 Warn("tape truncated: size exceeds maximum SP demo size %d",
4851 for (j = 0; j < demo_repeat / 16; j++)
4852 demo->data[demo->length++] = 0xf0 | demo_action;
4854 if (demo_repeat % 16)
4855 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4858 demo->is_available = TRUE;
4861 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4863 struct LevelInfo_SP *level_sp = level->native_sp_level;
4864 struct DemoInfo_SP *demo = &level_sp->demo;
4865 char *filename = level->file_info.filename;
4868 // always start with reliable default values
4869 setTapeInfoToDefaults();
4871 if (!demo->is_available)
4874 tape.level_nr = demo->level_nr; // (currently not used)
4875 tape.random_seed = level_sp->header.DemoRandomSeed;
4877 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4880 tape.pos[tape.counter].delay = 0;
4882 for (i = 0; i < demo->length; i++)
4884 int demo_action = demo->data[i] & 0x0f;
4885 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4886 int tape_action = map_key_SP_to_RND(demo_action);
4887 int tape_repeat = demo_repeat + 1;
4888 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4889 boolean success = 0;
4892 for (j = 0; j < tape_repeat; j++)
4893 success = TapeAddAction(action);
4897 Warn("SP demo truncated: size exceeds maximum tape size %d",
4904 TapeHaltRecording();
4908 // ----------------------------------------------------------------------------
4909 // functions for loading MM level
4910 // ----------------------------------------------------------------------------
4912 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4914 struct LevelInfo_MM *level_mm = level->native_mm_level;
4917 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4918 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4920 level_mm->time = level->time;
4921 level_mm->kettles_needed = level->gems_needed;
4922 level_mm->auto_count_kettles = level->auto_count_gems;
4924 level_mm->mm_laser_red = level->mm_laser_red;
4925 level_mm->mm_laser_green = level->mm_laser_green;
4926 level_mm->mm_laser_blue = level->mm_laser_blue;
4928 level_mm->df_laser_red = level->df_laser_red;
4929 level_mm->df_laser_green = level->df_laser_green;
4930 level_mm->df_laser_blue = level->df_laser_blue;
4932 strcpy(level_mm->name, level->name);
4933 strcpy(level_mm->author, level->author);
4935 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4936 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4937 level_mm->score[SC_KEY] = level->score[SC_KEY];
4938 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4939 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4941 level_mm->amoeba_speed = level->amoeba_speed;
4942 level_mm->time_fuse = level->mm_time_fuse;
4943 level_mm->time_bomb = level->mm_time_bomb;
4944 level_mm->time_ball = level->mm_time_ball;
4945 level_mm->time_block = level->mm_time_block;
4947 level_mm->num_ball_contents = level->num_mm_ball_contents;
4948 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4949 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4950 level_mm->explode_ball = level->explode_mm_ball;
4952 for (i = 0; i < level->num_mm_ball_contents; i++)
4953 level_mm->ball_content[i] =
4954 map_element_RND_to_MM(level->mm_ball_content[i]);
4956 for (x = 0; x < level->fieldx; x++)
4957 for (y = 0; y < level->fieldy; y++)
4959 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4962 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4964 struct LevelInfo_MM *level_mm = level->native_mm_level;
4967 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4968 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4970 level->time = level_mm->time;
4971 level->gems_needed = level_mm->kettles_needed;
4972 level->auto_count_gems = level_mm->auto_count_kettles;
4974 level->mm_laser_red = level_mm->mm_laser_red;
4975 level->mm_laser_green = level_mm->mm_laser_green;
4976 level->mm_laser_blue = level_mm->mm_laser_blue;
4978 level->df_laser_red = level_mm->df_laser_red;
4979 level->df_laser_green = level_mm->df_laser_green;
4980 level->df_laser_blue = level_mm->df_laser_blue;
4982 strcpy(level->name, level_mm->name);
4984 // only overwrite author from 'levelinfo.conf' if author defined in level
4985 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4986 strcpy(level->author, level_mm->author);
4988 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4989 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4990 level->score[SC_KEY] = level_mm->score[SC_KEY];
4991 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4992 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4994 level->amoeba_speed = level_mm->amoeba_speed;
4995 level->mm_time_fuse = level_mm->time_fuse;
4996 level->mm_time_bomb = level_mm->time_bomb;
4997 level->mm_time_ball = level_mm->time_ball;
4998 level->mm_time_block = level_mm->time_block;
5000 level->num_mm_ball_contents = level_mm->num_ball_contents;
5001 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5002 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5003 level->explode_mm_ball = level_mm->explode_ball;
5005 for (i = 0; i < level->num_mm_ball_contents; i++)
5006 level->mm_ball_content[i] =
5007 map_element_MM_to_RND(level_mm->ball_content[i]);
5009 for (x = 0; x < level->fieldx; x++)
5010 for (y = 0; y < level->fieldy; y++)
5011 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5015 // ----------------------------------------------------------------------------
5016 // functions for loading DC level
5017 // ----------------------------------------------------------------------------
5019 #define DC_LEVEL_HEADER_SIZE 344
5021 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5024 static int last_data_encoded;
5028 int diff_hi, diff_lo;
5029 int data_hi, data_lo;
5030 unsigned short data_decoded;
5034 last_data_encoded = 0;
5041 diff = data_encoded - last_data_encoded;
5042 diff_hi = diff & ~0xff;
5043 diff_lo = diff & 0xff;
5047 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5048 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5049 data_hi = data_hi & 0xff00;
5051 data_decoded = data_hi | data_lo;
5053 last_data_encoded = data_encoded;
5055 offset1 = (offset1 + 1) % 31;
5056 offset2 = offset2 & 0xff;
5058 return data_decoded;
5061 static int getMappedElement_DC(int element)
5069 // 0x0117 - 0x036e: (?)
5072 // 0x042d - 0x0684: (?)
5088 element = EL_CRYSTAL;
5091 case 0x0e77: // quicksand (boulder)
5092 element = EL_QUICKSAND_FAST_FULL;
5095 case 0x0e99: // slow quicksand (boulder)
5096 element = EL_QUICKSAND_FULL;
5100 element = EL_EM_EXIT_OPEN;
5104 element = EL_EM_EXIT_CLOSED;
5108 element = EL_EM_STEEL_EXIT_OPEN;
5112 element = EL_EM_STEEL_EXIT_CLOSED;
5115 case 0x0f4f: // dynamite (lit 1)
5116 element = EL_EM_DYNAMITE_ACTIVE;
5119 case 0x0f57: // dynamite (lit 2)
5120 element = EL_EM_DYNAMITE_ACTIVE;
5123 case 0x0f5f: // dynamite (lit 3)
5124 element = EL_EM_DYNAMITE_ACTIVE;
5127 case 0x0f67: // dynamite (lit 4)
5128 element = EL_EM_DYNAMITE_ACTIVE;
5135 element = EL_AMOEBA_WET;
5139 element = EL_AMOEBA_DROP;
5143 element = EL_DC_MAGIC_WALL;
5147 element = EL_SPACESHIP_UP;
5151 element = EL_SPACESHIP_DOWN;
5155 element = EL_SPACESHIP_LEFT;
5159 element = EL_SPACESHIP_RIGHT;
5163 element = EL_BUG_UP;
5167 element = EL_BUG_DOWN;
5171 element = EL_BUG_LEFT;
5175 element = EL_BUG_RIGHT;
5179 element = EL_MOLE_UP;
5183 element = EL_MOLE_DOWN;
5187 element = EL_MOLE_LEFT;
5191 element = EL_MOLE_RIGHT;
5199 element = EL_YAMYAM_UP;
5203 element = EL_SWITCHGATE_OPEN;
5207 element = EL_SWITCHGATE_CLOSED;
5211 element = EL_DC_SWITCHGATE_SWITCH_UP;
5215 element = EL_TIMEGATE_CLOSED;
5218 case 0x144c: // conveyor belt switch (green)
5219 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5222 case 0x144f: // conveyor belt switch (red)
5223 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5226 case 0x1452: // conveyor belt switch (blue)
5227 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5231 element = EL_CONVEYOR_BELT_3_MIDDLE;
5235 element = EL_CONVEYOR_BELT_3_LEFT;
5239 element = EL_CONVEYOR_BELT_3_RIGHT;
5243 element = EL_CONVEYOR_BELT_1_MIDDLE;
5247 element = EL_CONVEYOR_BELT_1_LEFT;
5251 element = EL_CONVEYOR_BELT_1_RIGHT;
5255 element = EL_CONVEYOR_BELT_4_MIDDLE;
5259 element = EL_CONVEYOR_BELT_4_LEFT;
5263 element = EL_CONVEYOR_BELT_4_RIGHT;
5267 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5271 element = EL_EXPANDABLE_WALL_VERTICAL;
5275 element = EL_EXPANDABLE_WALL_ANY;
5278 case 0x14ce: // growing steel wall (left/right)
5279 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5282 case 0x14df: // growing steel wall (up/down)
5283 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5286 case 0x14e8: // growing steel wall (up/down/left/right)
5287 element = EL_EXPANDABLE_STEELWALL_ANY;
5291 element = EL_SHIELD_DEADLY;
5295 element = EL_EXTRA_TIME;
5303 element = EL_EMPTY_SPACE;
5306 case 0x1578: // quicksand (empty)
5307 element = EL_QUICKSAND_FAST_EMPTY;
5310 case 0x1579: // slow quicksand (empty)
5311 element = EL_QUICKSAND_EMPTY;
5321 element = EL_EM_DYNAMITE;
5324 case 0x15a1: // key (red)
5325 element = EL_EM_KEY_1;
5328 case 0x15a2: // key (yellow)
5329 element = EL_EM_KEY_2;
5332 case 0x15a3: // key (blue)
5333 element = EL_EM_KEY_4;
5336 case 0x15a4: // key (green)
5337 element = EL_EM_KEY_3;
5340 case 0x15a5: // key (white)
5341 element = EL_DC_KEY_WHITE;
5345 element = EL_WALL_SLIPPERY;
5352 case 0x15a8: // wall (not round)
5356 case 0x15a9: // (blue)
5357 element = EL_CHAR_A;
5360 case 0x15aa: // (blue)
5361 element = EL_CHAR_B;
5364 case 0x15ab: // (blue)
5365 element = EL_CHAR_C;
5368 case 0x15ac: // (blue)
5369 element = EL_CHAR_D;
5372 case 0x15ad: // (blue)
5373 element = EL_CHAR_E;
5376 case 0x15ae: // (blue)
5377 element = EL_CHAR_F;
5380 case 0x15af: // (blue)
5381 element = EL_CHAR_G;
5384 case 0x15b0: // (blue)
5385 element = EL_CHAR_H;
5388 case 0x15b1: // (blue)
5389 element = EL_CHAR_I;
5392 case 0x15b2: // (blue)
5393 element = EL_CHAR_J;
5396 case 0x15b3: // (blue)
5397 element = EL_CHAR_K;
5400 case 0x15b4: // (blue)
5401 element = EL_CHAR_L;
5404 case 0x15b5: // (blue)
5405 element = EL_CHAR_M;
5408 case 0x15b6: // (blue)
5409 element = EL_CHAR_N;
5412 case 0x15b7: // (blue)
5413 element = EL_CHAR_O;
5416 case 0x15b8: // (blue)
5417 element = EL_CHAR_P;
5420 case 0x15b9: // (blue)
5421 element = EL_CHAR_Q;
5424 case 0x15ba: // (blue)
5425 element = EL_CHAR_R;
5428 case 0x15bb: // (blue)
5429 element = EL_CHAR_S;
5432 case 0x15bc: // (blue)
5433 element = EL_CHAR_T;
5436 case 0x15bd: // (blue)
5437 element = EL_CHAR_U;
5440 case 0x15be: // (blue)
5441 element = EL_CHAR_V;
5444 case 0x15bf: // (blue)
5445 element = EL_CHAR_W;
5448 case 0x15c0: // (blue)
5449 element = EL_CHAR_X;
5452 case 0x15c1: // (blue)
5453 element = EL_CHAR_Y;
5456 case 0x15c2: // (blue)
5457 element = EL_CHAR_Z;
5460 case 0x15c3: // (blue)
5461 element = EL_CHAR_AUMLAUT;
5464 case 0x15c4: // (blue)
5465 element = EL_CHAR_OUMLAUT;
5468 case 0x15c5: // (blue)
5469 element = EL_CHAR_UUMLAUT;
5472 case 0x15c6: // (blue)
5473 element = EL_CHAR_0;
5476 case 0x15c7: // (blue)
5477 element = EL_CHAR_1;
5480 case 0x15c8: // (blue)
5481 element = EL_CHAR_2;
5484 case 0x15c9: // (blue)
5485 element = EL_CHAR_3;
5488 case 0x15ca: // (blue)
5489 element = EL_CHAR_4;
5492 case 0x15cb: // (blue)
5493 element = EL_CHAR_5;
5496 case 0x15cc: // (blue)
5497 element = EL_CHAR_6;
5500 case 0x15cd: // (blue)
5501 element = EL_CHAR_7;
5504 case 0x15ce: // (blue)
5505 element = EL_CHAR_8;
5508 case 0x15cf: // (blue)
5509 element = EL_CHAR_9;
5512 case 0x15d0: // (blue)
5513 element = EL_CHAR_PERIOD;
5516 case 0x15d1: // (blue)
5517 element = EL_CHAR_EXCLAM;
5520 case 0x15d2: // (blue)
5521 element = EL_CHAR_COLON;
5524 case 0x15d3: // (blue)
5525 element = EL_CHAR_LESS;
5528 case 0x15d4: // (blue)
5529 element = EL_CHAR_GREATER;
5532 case 0x15d5: // (blue)
5533 element = EL_CHAR_QUESTION;
5536 case 0x15d6: // (blue)
5537 element = EL_CHAR_COPYRIGHT;
5540 case 0x15d7: // (blue)
5541 element = EL_CHAR_UP;
5544 case 0x15d8: // (blue)
5545 element = EL_CHAR_DOWN;
5548 case 0x15d9: // (blue)
5549 element = EL_CHAR_BUTTON;
5552 case 0x15da: // (blue)
5553 element = EL_CHAR_PLUS;
5556 case 0x15db: // (blue)
5557 element = EL_CHAR_MINUS;
5560 case 0x15dc: // (blue)
5561 element = EL_CHAR_APOSTROPHE;
5564 case 0x15dd: // (blue)
5565 element = EL_CHAR_PARENLEFT;
5568 case 0x15de: // (blue)
5569 element = EL_CHAR_PARENRIGHT;
5572 case 0x15df: // (green)
5573 element = EL_CHAR_A;
5576 case 0x15e0: // (green)
5577 element = EL_CHAR_B;
5580 case 0x15e1: // (green)
5581 element = EL_CHAR_C;
5584 case 0x15e2: // (green)
5585 element = EL_CHAR_D;
5588 case 0x15e3: // (green)
5589 element = EL_CHAR_E;
5592 case 0x15e4: // (green)
5593 element = EL_CHAR_F;
5596 case 0x15e5: // (green)
5597 element = EL_CHAR_G;
5600 case 0x15e6: // (green)
5601 element = EL_CHAR_H;
5604 case 0x15e7: // (green)
5605 element = EL_CHAR_I;
5608 case 0x15e8: // (green)
5609 element = EL_CHAR_J;
5612 case 0x15e9: // (green)
5613 element = EL_CHAR_K;
5616 case 0x15ea: // (green)
5617 element = EL_CHAR_L;
5620 case 0x15eb: // (green)
5621 element = EL_CHAR_M;
5624 case 0x15ec: // (green)
5625 element = EL_CHAR_N;
5628 case 0x15ed: // (green)
5629 element = EL_CHAR_O;
5632 case 0x15ee: // (green)
5633 element = EL_CHAR_P;
5636 case 0x15ef: // (green)
5637 element = EL_CHAR_Q;
5640 case 0x15f0: // (green)
5641 element = EL_CHAR_R;
5644 case 0x15f1: // (green)
5645 element = EL_CHAR_S;
5648 case 0x15f2: // (green)
5649 element = EL_CHAR_T;
5652 case 0x15f3: // (green)
5653 element = EL_CHAR_U;
5656 case 0x15f4: // (green)
5657 element = EL_CHAR_V;
5660 case 0x15f5: // (green)
5661 element = EL_CHAR_W;
5664 case 0x15f6: // (green)
5665 element = EL_CHAR_X;
5668 case 0x15f7: // (green)
5669 element = EL_CHAR_Y;
5672 case 0x15f8: // (green)
5673 element = EL_CHAR_Z;
5676 case 0x15f9: // (green)
5677 element = EL_CHAR_AUMLAUT;
5680 case 0x15fa: // (green)
5681 element = EL_CHAR_OUMLAUT;
5684 case 0x15fb: // (green)
5685 element = EL_CHAR_UUMLAUT;
5688 case 0x15fc: // (green)
5689 element = EL_CHAR_0;
5692 case 0x15fd: // (green)
5693 element = EL_CHAR_1;
5696 case 0x15fe: // (green)
5697 element = EL_CHAR_2;
5700 case 0x15ff: // (green)
5701 element = EL_CHAR_3;
5704 case 0x1600: // (green)
5705 element = EL_CHAR_4;
5708 case 0x1601: // (green)
5709 element = EL_CHAR_5;
5712 case 0x1602: // (green)
5713 element = EL_CHAR_6;
5716 case 0x1603: // (green)
5717 element = EL_CHAR_7;
5720 case 0x1604: // (green)
5721 element = EL_CHAR_8;
5724 case 0x1605: // (green)
5725 element = EL_CHAR_9;
5728 case 0x1606: // (green)
5729 element = EL_CHAR_PERIOD;
5732 case 0x1607: // (green)
5733 element = EL_CHAR_EXCLAM;
5736 case 0x1608: // (green)
5737 element = EL_CHAR_COLON;
5740 case 0x1609: // (green)
5741 element = EL_CHAR_LESS;
5744 case 0x160a: // (green)
5745 element = EL_CHAR_GREATER;
5748 case 0x160b: // (green)
5749 element = EL_CHAR_QUESTION;
5752 case 0x160c: // (green)
5753 element = EL_CHAR_COPYRIGHT;
5756 case 0x160d: // (green)
5757 element = EL_CHAR_UP;
5760 case 0x160e: // (green)
5761 element = EL_CHAR_DOWN;
5764 case 0x160f: // (green)
5765 element = EL_CHAR_BUTTON;
5768 case 0x1610: // (green)
5769 element = EL_CHAR_PLUS;
5772 case 0x1611: // (green)
5773 element = EL_CHAR_MINUS;
5776 case 0x1612: // (green)
5777 element = EL_CHAR_APOSTROPHE;
5780 case 0x1613: // (green)
5781 element = EL_CHAR_PARENLEFT;
5784 case 0x1614: // (green)
5785 element = EL_CHAR_PARENRIGHT;
5788 case 0x1615: // (blue steel)
5789 element = EL_STEEL_CHAR_A;
5792 case 0x1616: // (blue steel)
5793 element = EL_STEEL_CHAR_B;
5796 case 0x1617: // (blue steel)
5797 element = EL_STEEL_CHAR_C;
5800 case 0x1618: // (blue steel)
5801 element = EL_STEEL_CHAR_D;
5804 case 0x1619: // (blue steel)
5805 element = EL_STEEL_CHAR_E;
5808 case 0x161a: // (blue steel)
5809 element = EL_STEEL_CHAR_F;
5812 case 0x161b: // (blue steel)
5813 element = EL_STEEL_CHAR_G;
5816 case 0x161c: // (blue steel)
5817 element = EL_STEEL_CHAR_H;
5820 case 0x161d: // (blue steel)
5821 element = EL_STEEL_CHAR_I;
5824 case 0x161e: // (blue steel)
5825 element = EL_STEEL_CHAR_J;
5828 case 0x161f: // (blue steel)
5829 element = EL_STEEL_CHAR_K;
5832 case 0x1620: // (blue steel)
5833 element = EL_STEEL_CHAR_L;
5836 case 0x1621: // (blue steel)
5837 element = EL_STEEL_CHAR_M;
5840 case 0x1622: // (blue steel)
5841 element = EL_STEEL_CHAR_N;
5844 case 0x1623: // (blue steel)
5845 element = EL_STEEL_CHAR_O;
5848 case 0x1624: // (blue steel)
5849 element = EL_STEEL_CHAR_P;
5852 case 0x1625: // (blue steel)
5853 element = EL_STEEL_CHAR_Q;
5856 case 0x1626: // (blue steel)
5857 element = EL_STEEL_CHAR_R;
5860 case 0x1627: // (blue steel)
5861 element = EL_STEEL_CHAR_S;
5864 case 0x1628: // (blue steel)
5865 element = EL_STEEL_CHAR_T;
5868 case 0x1629: // (blue steel)
5869 element = EL_STEEL_CHAR_U;
5872 case 0x162a: // (blue steel)
5873 element = EL_STEEL_CHAR_V;
5876 case 0x162b: // (blue steel)
5877 element = EL_STEEL_CHAR_W;
5880 case 0x162c: // (blue steel)
5881 element = EL_STEEL_CHAR_X;
5884 case 0x162d: // (blue steel)
5885 element = EL_STEEL_CHAR_Y;
5888 case 0x162e: // (blue steel)
5889 element = EL_STEEL_CHAR_Z;
5892 case 0x162f: // (blue steel)
5893 element = EL_STEEL_CHAR_AUMLAUT;
5896 case 0x1630: // (blue steel)
5897 element = EL_STEEL_CHAR_OUMLAUT;
5900 case 0x1631: // (blue steel)
5901 element = EL_STEEL_CHAR_UUMLAUT;
5904 case 0x1632: // (blue steel)
5905 element = EL_STEEL_CHAR_0;
5908 case 0x1633: // (blue steel)
5909 element = EL_STEEL_CHAR_1;
5912 case 0x1634: // (blue steel)
5913 element = EL_STEEL_CHAR_2;
5916 case 0x1635: // (blue steel)
5917 element = EL_STEEL_CHAR_3;
5920 case 0x1636: // (blue steel)
5921 element = EL_STEEL_CHAR_4;
5924 case 0x1637: // (blue steel)
5925 element = EL_STEEL_CHAR_5;
5928 case 0x1638: // (blue steel)
5929 element = EL_STEEL_CHAR_6;
5932 case 0x1639: // (blue steel)
5933 element = EL_STEEL_CHAR_7;
5936 case 0x163a: // (blue steel)
5937 element = EL_STEEL_CHAR_8;
5940 case 0x163b: // (blue steel)
5941 element = EL_STEEL_CHAR_9;
5944 case 0x163c: // (blue steel)
5945 element = EL_STEEL_CHAR_PERIOD;
5948 case 0x163d: // (blue steel)
5949 element = EL_STEEL_CHAR_EXCLAM;
5952 case 0x163e: // (blue steel)
5953 element = EL_STEEL_CHAR_COLON;
5956 case 0x163f: // (blue steel)
5957 element = EL_STEEL_CHAR_LESS;
5960 case 0x1640: // (blue steel)
5961 element = EL_STEEL_CHAR_GREATER;
5964 case 0x1641: // (blue steel)
5965 element = EL_STEEL_CHAR_QUESTION;
5968 case 0x1642: // (blue steel)
5969 element = EL_STEEL_CHAR_COPYRIGHT;
5972 case 0x1643: // (blue steel)
5973 element = EL_STEEL_CHAR_UP;
5976 case 0x1644: // (blue steel)
5977 element = EL_STEEL_CHAR_DOWN;
5980 case 0x1645: // (blue steel)
5981 element = EL_STEEL_CHAR_BUTTON;
5984 case 0x1646: // (blue steel)
5985 element = EL_STEEL_CHAR_PLUS;
5988 case 0x1647: // (blue steel)
5989 element = EL_STEEL_CHAR_MINUS;
5992 case 0x1648: // (blue steel)
5993 element = EL_STEEL_CHAR_APOSTROPHE;
5996 case 0x1649: // (blue steel)
5997 element = EL_STEEL_CHAR_PARENLEFT;
6000 case 0x164a: // (blue steel)
6001 element = EL_STEEL_CHAR_PARENRIGHT;
6004 case 0x164b: // (green steel)
6005 element = EL_STEEL_CHAR_A;
6008 case 0x164c: // (green steel)
6009 element = EL_STEEL_CHAR_B;
6012 case 0x164d: // (green steel)
6013 element = EL_STEEL_CHAR_C;
6016 case 0x164e: // (green steel)
6017 element = EL_STEEL_CHAR_D;
6020 case 0x164f: // (green steel)
6021 element = EL_STEEL_CHAR_E;
6024 case 0x1650: // (green steel)
6025 element = EL_STEEL_CHAR_F;
6028 case 0x1651: // (green steel)
6029 element = EL_STEEL_CHAR_G;
6032 case 0x1652: // (green steel)
6033 element = EL_STEEL_CHAR_H;
6036 case 0x1653: // (green steel)
6037 element = EL_STEEL_CHAR_I;
6040 case 0x1654: // (green steel)
6041 element = EL_STEEL_CHAR_J;
6044 case 0x1655: // (green steel)
6045 element = EL_STEEL_CHAR_K;
6048 case 0x1656: // (green steel)
6049 element = EL_STEEL_CHAR_L;
6052 case 0x1657: // (green steel)
6053 element = EL_STEEL_CHAR_M;
6056 case 0x1658: // (green steel)
6057 element = EL_STEEL_CHAR_N;
6060 case 0x1659: // (green steel)
6061 element = EL_STEEL_CHAR_O;
6064 case 0x165a: // (green steel)
6065 element = EL_STEEL_CHAR_P;
6068 case 0x165b: // (green steel)
6069 element = EL_STEEL_CHAR_Q;
6072 case 0x165c: // (green steel)
6073 element = EL_STEEL_CHAR_R;
6076 case 0x165d: // (green steel)
6077 element = EL_STEEL_CHAR_S;
6080 case 0x165e: // (green steel)
6081 element = EL_STEEL_CHAR_T;
6084 case 0x165f: // (green steel)
6085 element = EL_STEEL_CHAR_U;
6088 case 0x1660: // (green steel)
6089 element = EL_STEEL_CHAR_V;
6092 case 0x1661: // (green steel)
6093 element = EL_STEEL_CHAR_W;
6096 case 0x1662: // (green steel)
6097 element = EL_STEEL_CHAR_X;
6100 case 0x1663: // (green steel)
6101 element = EL_STEEL_CHAR_Y;
6104 case 0x1664: // (green steel)
6105 element = EL_STEEL_CHAR_Z;
6108 case 0x1665: // (green steel)
6109 element = EL_STEEL_CHAR_AUMLAUT;
6112 case 0x1666: // (green steel)
6113 element = EL_STEEL_CHAR_OUMLAUT;
6116 case 0x1667: // (green steel)
6117 element = EL_STEEL_CHAR_UUMLAUT;
6120 case 0x1668: // (green steel)
6121 element = EL_STEEL_CHAR_0;
6124 case 0x1669: // (green steel)
6125 element = EL_STEEL_CHAR_1;
6128 case 0x166a: // (green steel)
6129 element = EL_STEEL_CHAR_2;
6132 case 0x166b: // (green steel)
6133 element = EL_STEEL_CHAR_3;
6136 case 0x166c: // (green steel)
6137 element = EL_STEEL_CHAR_4;
6140 case 0x166d: // (green steel)
6141 element = EL_STEEL_CHAR_5;
6144 case 0x166e: // (green steel)
6145 element = EL_STEEL_CHAR_6;
6148 case 0x166f: // (green steel)
6149 element = EL_STEEL_CHAR_7;
6152 case 0x1670: // (green steel)
6153 element = EL_STEEL_CHAR_8;
6156 case 0x1671: // (green steel)
6157 element = EL_STEEL_CHAR_9;
6160 case 0x1672: // (green steel)
6161 element = EL_STEEL_CHAR_PERIOD;
6164 case 0x1673: // (green steel)
6165 element = EL_STEEL_CHAR_EXCLAM;
6168 case 0x1674: // (green steel)
6169 element = EL_STEEL_CHAR_COLON;
6172 case 0x1675: // (green steel)
6173 element = EL_STEEL_CHAR_LESS;
6176 case 0x1676: // (green steel)
6177 element = EL_STEEL_CHAR_GREATER;
6180 case 0x1677: // (green steel)
6181 element = EL_STEEL_CHAR_QUESTION;
6184 case 0x1678: // (green steel)
6185 element = EL_STEEL_CHAR_COPYRIGHT;
6188 case 0x1679: // (green steel)
6189 element = EL_STEEL_CHAR_UP;
6192 case 0x167a: // (green steel)
6193 element = EL_STEEL_CHAR_DOWN;
6196 case 0x167b: // (green steel)
6197 element = EL_STEEL_CHAR_BUTTON;
6200 case 0x167c: // (green steel)
6201 element = EL_STEEL_CHAR_PLUS;
6204 case 0x167d: // (green steel)
6205 element = EL_STEEL_CHAR_MINUS;
6208 case 0x167e: // (green steel)
6209 element = EL_STEEL_CHAR_APOSTROPHE;
6212 case 0x167f: // (green steel)
6213 element = EL_STEEL_CHAR_PARENLEFT;
6216 case 0x1680: // (green steel)
6217 element = EL_STEEL_CHAR_PARENRIGHT;
6220 case 0x1681: // gate (red)
6221 element = EL_EM_GATE_1;
6224 case 0x1682: // secret gate (red)
6225 element = EL_EM_GATE_1_GRAY;
6228 case 0x1683: // gate (yellow)
6229 element = EL_EM_GATE_2;
6232 case 0x1684: // secret gate (yellow)
6233 element = EL_EM_GATE_2_GRAY;
6236 case 0x1685: // gate (blue)
6237 element = EL_EM_GATE_4;
6240 case 0x1686: // secret gate (blue)
6241 element = EL_EM_GATE_4_GRAY;
6244 case 0x1687: // gate (green)
6245 element = EL_EM_GATE_3;
6248 case 0x1688: // secret gate (green)
6249 element = EL_EM_GATE_3_GRAY;
6252 case 0x1689: // gate (white)
6253 element = EL_DC_GATE_WHITE;
6256 case 0x168a: // secret gate (white)
6257 element = EL_DC_GATE_WHITE_GRAY;
6260 case 0x168b: // secret gate (no key)
6261 element = EL_DC_GATE_FAKE_GRAY;
6265 element = EL_ROBOT_WHEEL;
6269 element = EL_DC_TIMEGATE_SWITCH;
6273 element = EL_ACID_POOL_BOTTOM;
6277 element = EL_ACID_POOL_TOPLEFT;
6281 element = EL_ACID_POOL_TOPRIGHT;
6285 element = EL_ACID_POOL_BOTTOMLEFT;
6289 element = EL_ACID_POOL_BOTTOMRIGHT;
6293 element = EL_STEELWALL;
6297 element = EL_STEELWALL_SLIPPERY;
6300 case 0x1695: // steel wall (not round)
6301 element = EL_STEELWALL;
6304 case 0x1696: // steel wall (left)
6305 element = EL_DC_STEELWALL_1_LEFT;
6308 case 0x1697: // steel wall (bottom)
6309 element = EL_DC_STEELWALL_1_BOTTOM;
6312 case 0x1698: // steel wall (right)
6313 element = EL_DC_STEELWALL_1_RIGHT;
6316 case 0x1699: // steel wall (top)
6317 element = EL_DC_STEELWALL_1_TOP;
6320 case 0x169a: // steel wall (left/bottom)
6321 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6324 case 0x169b: // steel wall (right/bottom)
6325 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6328 case 0x169c: // steel wall (right/top)
6329 element = EL_DC_STEELWALL_1_TOPRIGHT;
6332 case 0x169d: // steel wall (left/top)
6333 element = EL_DC_STEELWALL_1_TOPLEFT;
6336 case 0x169e: // steel wall (right/bottom small)
6337 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6340 case 0x169f: // steel wall (left/bottom small)
6341 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6344 case 0x16a0: // steel wall (right/top small)
6345 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6348 case 0x16a1: // steel wall (left/top small)
6349 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6352 case 0x16a2: // steel wall (left/right)
6353 element = EL_DC_STEELWALL_1_VERTICAL;
6356 case 0x16a3: // steel wall (top/bottom)
6357 element = EL_DC_STEELWALL_1_HORIZONTAL;
6360 case 0x16a4: // steel wall 2 (left end)
6361 element = EL_DC_STEELWALL_2_LEFT;
6364 case 0x16a5: // steel wall 2 (right end)
6365 element = EL_DC_STEELWALL_2_RIGHT;
6368 case 0x16a6: // steel wall 2 (top end)
6369 element = EL_DC_STEELWALL_2_TOP;
6372 case 0x16a7: // steel wall 2 (bottom end)
6373 element = EL_DC_STEELWALL_2_BOTTOM;
6376 case 0x16a8: // steel wall 2 (left/right)
6377 element = EL_DC_STEELWALL_2_HORIZONTAL;
6380 case 0x16a9: // steel wall 2 (up/down)
6381 element = EL_DC_STEELWALL_2_VERTICAL;
6384 case 0x16aa: // steel wall 2 (mid)
6385 element = EL_DC_STEELWALL_2_MIDDLE;
6389 element = EL_SIGN_EXCLAMATION;
6393 element = EL_SIGN_RADIOACTIVITY;
6397 element = EL_SIGN_STOP;
6401 element = EL_SIGN_WHEELCHAIR;
6405 element = EL_SIGN_PARKING;
6409 element = EL_SIGN_NO_ENTRY;
6413 element = EL_SIGN_HEART;
6417 element = EL_SIGN_GIVE_WAY;
6421 element = EL_SIGN_ENTRY_FORBIDDEN;
6425 element = EL_SIGN_EMERGENCY_EXIT;
6429 element = EL_SIGN_YIN_YANG;
6433 element = EL_WALL_EMERALD;
6437 element = EL_WALL_DIAMOND;
6441 element = EL_WALL_PEARL;
6445 element = EL_WALL_CRYSTAL;
6449 element = EL_INVISIBLE_WALL;
6453 element = EL_INVISIBLE_STEELWALL;
6457 // EL_INVISIBLE_SAND
6460 element = EL_LIGHT_SWITCH;
6464 element = EL_ENVELOPE_1;
6468 if (element >= 0x0117 && element <= 0x036e) // (?)
6469 element = EL_DIAMOND;
6470 else if (element >= 0x042d && element <= 0x0684) // (?)
6471 element = EL_EMERALD;
6472 else if (element >= 0x157c && element <= 0x158b)
6474 else if (element >= 0x1590 && element <= 0x159f)
6475 element = EL_DC_LANDMINE;
6476 else if (element >= 0x16bc && element <= 0x16cb)
6477 element = EL_INVISIBLE_SAND;
6480 Warn("unknown Diamond Caves element 0x%04x", element);
6482 element = EL_UNKNOWN;
6487 return getMappedElement(element);
6490 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6492 byte header[DC_LEVEL_HEADER_SIZE];
6494 int envelope_header_pos = 62;
6495 int envelope_content_pos = 94;
6496 int level_name_pos = 251;
6497 int level_author_pos = 292;
6498 int envelope_header_len;
6499 int envelope_content_len;
6501 int level_author_len;
6503 int num_yamyam_contents;
6506 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6508 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6510 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6512 header[i * 2 + 0] = header_word >> 8;
6513 header[i * 2 + 1] = header_word & 0xff;
6516 // read some values from level header to check level decoding integrity
6517 fieldx = header[6] | (header[7] << 8);
6518 fieldy = header[8] | (header[9] << 8);
6519 num_yamyam_contents = header[60] | (header[61] << 8);
6521 // do some simple sanity checks to ensure that level was correctly decoded
6522 if (fieldx < 1 || fieldx > 256 ||
6523 fieldy < 1 || fieldy > 256 ||
6524 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6526 level->no_valid_file = TRUE;
6528 Warn("cannot decode level from stream -- using empty level");
6533 // maximum envelope header size is 31 bytes
6534 envelope_header_len = header[envelope_header_pos];
6535 // maximum envelope content size is 110 (156?) bytes
6536 envelope_content_len = header[envelope_content_pos];
6538 // maximum level title size is 40 bytes
6539 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6540 // maximum level author size is 30 (51?) bytes
6541 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6545 for (i = 0; i < envelope_header_len; i++)
6546 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6547 level->envelope[0].text[envelope_size++] =
6548 header[envelope_header_pos + 1 + i];
6550 if (envelope_header_len > 0 && envelope_content_len > 0)
6552 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6553 level->envelope[0].text[envelope_size++] = '\n';
6554 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6555 level->envelope[0].text[envelope_size++] = '\n';
6558 for (i = 0; i < envelope_content_len; i++)
6559 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6560 level->envelope[0].text[envelope_size++] =
6561 header[envelope_content_pos + 1 + i];
6563 level->envelope[0].text[envelope_size] = '\0';
6565 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6566 level->envelope[0].ysize = 10;
6567 level->envelope[0].autowrap = TRUE;
6568 level->envelope[0].centered = TRUE;
6570 for (i = 0; i < level_name_len; i++)
6571 level->name[i] = header[level_name_pos + 1 + i];
6572 level->name[level_name_len] = '\0';
6574 for (i = 0; i < level_author_len; i++)
6575 level->author[i] = header[level_author_pos + 1 + i];
6576 level->author[level_author_len] = '\0';
6578 num_yamyam_contents = header[60] | (header[61] << 8);
6579 level->num_yamyam_contents =
6580 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6582 for (i = 0; i < num_yamyam_contents; i++)
6584 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6586 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6587 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6589 if (i < MAX_ELEMENT_CONTENTS)
6590 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6594 fieldx = header[6] | (header[7] << 8);
6595 fieldy = header[8] | (header[9] << 8);
6596 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6597 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6599 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6601 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6602 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6604 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6605 level->field[x][y] = getMappedElement_DC(element_dc);
6608 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6609 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6610 level->field[x][y] = EL_PLAYER_1;
6612 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6613 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6614 level->field[x][y] = EL_PLAYER_2;
6616 level->gems_needed = header[18] | (header[19] << 8);
6618 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6619 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6620 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6621 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6622 level->score[SC_NUT] = header[28] | (header[29] << 8);
6623 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6624 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6625 level->score[SC_BUG] = header[34] | (header[35] << 8);
6626 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6627 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6628 level->score[SC_KEY] = header[40] | (header[41] << 8);
6629 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6631 level->time = header[44] | (header[45] << 8);
6633 level->amoeba_speed = header[46] | (header[47] << 8);
6634 level->time_light = header[48] | (header[49] << 8);
6635 level->time_timegate = header[50] | (header[51] << 8);
6636 level->time_wheel = header[52] | (header[53] << 8);
6637 level->time_magic_wall = header[54] | (header[55] << 8);
6638 level->extra_time = header[56] | (header[57] << 8);
6639 level->shield_normal_time = header[58] | (header[59] << 8);
6641 // shield and extra time elements do not have a score
6642 level->score[SC_SHIELD] = 0;
6643 level->extra_time_score = 0;
6645 // set time for normal and deadly shields to the same value
6646 level->shield_deadly_time = level->shield_normal_time;
6648 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6649 // can slip down from flat walls, like normal walls and steel walls
6650 level->em_slippery_gems = TRUE;
6652 // time score is counted for each 10 seconds left in Diamond Caves levels
6653 level->time_score_base = 10;
6656 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6657 struct LevelFileInfo *level_file_info,
6658 boolean level_info_only)
6660 char *filename = level_file_info->filename;
6662 int num_magic_bytes = 8;
6663 char magic_bytes[num_magic_bytes + 1];
6664 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6666 if (!(file = openFile(filename, MODE_READ)))
6668 level->no_valid_file = TRUE;
6670 if (!level_info_only)
6671 Warn("cannot read level '%s' -- using empty level", filename);
6676 // fseek(file, 0x0000, SEEK_SET);
6678 if (level_file_info->packed)
6680 // read "magic bytes" from start of file
6681 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6682 magic_bytes[0] = '\0';
6684 // check "magic bytes" for correct file format
6685 if (!strPrefix(magic_bytes, "DC2"))
6687 level->no_valid_file = TRUE;
6689 Warn("unknown DC level file '%s' -- using empty level", filename);
6694 if (strPrefix(magic_bytes, "DC2Win95") ||
6695 strPrefix(magic_bytes, "DC2Win98"))
6697 int position_first_level = 0x00fa;
6698 int extra_bytes = 4;
6701 // advance file stream to first level inside the level package
6702 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6704 // each block of level data is followed by block of non-level data
6705 num_levels_to_skip *= 2;
6707 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6708 while (num_levels_to_skip >= 0)
6710 // advance file stream to next level inside the level package
6711 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6713 level->no_valid_file = TRUE;
6715 Warn("cannot fseek in file '%s' -- using empty level", filename);
6720 // skip apparently unused extra bytes following each level
6721 ReadUnusedBytesFromFile(file, extra_bytes);
6723 // read size of next level in level package
6724 skip_bytes = getFile32BitLE(file);
6726 num_levels_to_skip--;
6731 level->no_valid_file = TRUE;
6733 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6739 LoadLevelFromFileStream_DC(file, level);
6745 // ----------------------------------------------------------------------------
6746 // functions for loading SB level
6747 // ----------------------------------------------------------------------------
6749 int getMappedElement_SB(int element_ascii, boolean use_ces)
6757 sb_element_mapping[] =
6759 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6760 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6761 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6762 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6763 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6764 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6765 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6766 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6773 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6774 if (element_ascii == sb_element_mapping[i].ascii)
6775 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6777 return EL_UNDEFINED;
6780 static void SetLevelSettings_SB(struct LevelInfo *level)
6784 level->use_step_counter = TRUE;
6787 level->score[SC_TIME_BONUS] = 0;
6788 level->time_score_base = 1;
6789 level->rate_time_over_score = TRUE;
6792 level->auto_exit_sokoban = TRUE;
6795 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6796 struct LevelFileInfo *level_file_info,
6797 boolean level_info_only)
6799 char *filename = level_file_info->filename;
6800 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6801 char last_comment[MAX_LINE_LEN];
6802 char level_name[MAX_LINE_LEN];
6805 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6806 boolean read_continued_line = FALSE;
6807 boolean reading_playfield = FALSE;
6808 boolean got_valid_playfield_line = FALSE;
6809 boolean invalid_playfield_char = FALSE;
6810 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6811 int file_level_nr = 0;
6812 int x = 0, y = 0; // initialized to make compilers happy
6814 last_comment[0] = '\0';
6815 level_name[0] = '\0';
6817 if (!(file = openFile(filename, MODE_READ)))
6819 level->no_valid_file = TRUE;
6821 if (!level_info_only)
6822 Warn("cannot read level '%s' -- using empty level", filename);
6827 while (!checkEndOfFile(file))
6829 // level successfully read, but next level may follow here
6830 if (!got_valid_playfield_line && reading_playfield)
6832 // read playfield from single level file -- skip remaining file
6833 if (!level_file_info->packed)
6836 if (file_level_nr >= num_levels_to_skip)
6841 last_comment[0] = '\0';
6842 level_name[0] = '\0';
6844 reading_playfield = FALSE;
6847 got_valid_playfield_line = FALSE;
6849 // read next line of input file
6850 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6853 // cut trailing line break (this can be newline and/or carriage return)
6854 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6855 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6858 // copy raw input line for later use (mainly debugging output)
6859 strcpy(line_raw, line);
6861 if (read_continued_line)
6863 // append new line to existing line, if there is enough space
6864 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6865 strcat(previous_line, line_ptr);
6867 strcpy(line, previous_line); // copy storage buffer to line
6869 read_continued_line = FALSE;
6872 // if the last character is '\', continue at next line
6873 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6875 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6876 strcpy(previous_line, line); // copy line to storage buffer
6878 read_continued_line = TRUE;
6884 if (line[0] == '\0')
6887 // extract comment text from comment line
6890 for (line_ptr = line; *line_ptr; line_ptr++)
6891 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6894 strcpy(last_comment, line_ptr);
6899 // extract level title text from line containing level title
6900 if (line[0] == '\'')
6902 strcpy(level_name, &line[1]);
6904 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6905 level_name[strlen(level_name) - 1] = '\0';
6910 // skip lines containing only spaces (or empty lines)
6911 for (line_ptr = line; *line_ptr; line_ptr++)
6912 if (*line_ptr != ' ')
6914 if (*line_ptr == '\0')
6917 // at this point, we have found a line containing part of a playfield
6919 got_valid_playfield_line = TRUE;
6921 if (!reading_playfield)
6923 reading_playfield = TRUE;
6924 invalid_playfield_char = FALSE;
6926 for (x = 0; x < MAX_LEV_FIELDX; x++)
6927 for (y = 0; y < MAX_LEV_FIELDY; y++)
6928 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6933 // start with topmost tile row
6937 // skip playfield line if larger row than allowed
6938 if (y >= MAX_LEV_FIELDY)
6941 // start with leftmost tile column
6944 // read playfield elements from line
6945 for (line_ptr = line; *line_ptr; line_ptr++)
6947 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6949 // stop parsing playfield line if larger column than allowed
6950 if (x >= MAX_LEV_FIELDX)
6953 if (mapped_sb_element == EL_UNDEFINED)
6955 invalid_playfield_char = TRUE;
6960 level->field[x][y] = mapped_sb_element;
6962 // continue with next tile column
6965 level->fieldx = MAX(x, level->fieldx);
6968 if (invalid_playfield_char)
6970 // if first playfield line, treat invalid lines as comment lines
6972 reading_playfield = FALSE;
6977 // continue with next tile row
6985 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6986 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6988 if (!reading_playfield)
6990 level->no_valid_file = TRUE;
6992 Warn("cannot read level '%s' -- using empty level", filename);
6997 if (*level_name != '\0')
6999 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7000 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7002 else if (*last_comment != '\0')
7004 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7005 level->name[MAX_LEVEL_NAME_LEN] = '\0';
7009 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7012 // set all empty fields beyond the border walls to invisible steel wall
7013 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7015 if ((x == 0 || x == level->fieldx - 1 ||
7016 y == 0 || y == level->fieldy - 1) &&
7017 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7018 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7019 level->field, level->fieldx, level->fieldy);
7022 // set special level settings for Sokoban levels
7023 SetLevelSettings_SB(level);
7025 if (load_xsb_to_ces)
7027 // special global settings can now be set in level template
7028 level->use_custom_template = TRUE;
7033 // -------------------------------------------------------------------------
7034 // functions for handling native levels
7035 // -------------------------------------------------------------------------
7037 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7038 struct LevelFileInfo *level_file_info,
7039 boolean level_info_only)
7043 // determine position of requested level inside level package
7044 if (level_file_info->packed)
7045 pos = level_file_info->nr - leveldir_current->first_level;
7047 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7048 level->no_valid_file = TRUE;
7051 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7052 struct LevelFileInfo *level_file_info,
7053 boolean level_info_only)
7055 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7056 level->no_valid_file = TRUE;
7059 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7060 struct LevelFileInfo *level_file_info,
7061 boolean level_info_only)
7065 // determine position of requested level inside level package
7066 if (level_file_info->packed)
7067 pos = level_file_info->nr - leveldir_current->first_level;
7069 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7070 level->no_valid_file = TRUE;
7073 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7074 struct LevelFileInfo *level_file_info,
7075 boolean level_info_only)
7077 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7078 level->no_valid_file = TRUE;
7081 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7083 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7084 CopyNativeLevel_RND_to_BD(level);
7085 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7086 CopyNativeLevel_RND_to_EM(level);
7087 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7088 CopyNativeLevel_RND_to_SP(level);
7089 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7090 CopyNativeLevel_RND_to_MM(level);
7093 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7095 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7096 CopyNativeLevel_BD_to_RND(level);
7097 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7098 CopyNativeLevel_EM_to_RND(level);
7099 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7100 CopyNativeLevel_SP_to_RND(level);
7101 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7102 CopyNativeLevel_MM_to_RND(level);
7105 void SaveNativeLevel(struct LevelInfo *level)
7107 // saving native level files only supported for some game engines
7108 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7109 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7112 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7113 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7114 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7115 char *filename = getLevelFilenameFromBasename(basename);
7117 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7120 boolean success = FALSE;
7122 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7124 CopyNativeLevel_RND_to_BD(level);
7125 // CopyNativeTape_RND_to_BD(level);
7127 success = SaveNativeLevel_BD(filename);
7129 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7131 CopyNativeLevel_RND_to_SP(level);
7132 CopyNativeTape_RND_to_SP(level);
7134 success = SaveNativeLevel_SP(filename);
7138 Request("Native level file saved!", REQ_CONFIRM);
7140 Request("Failed to save native level file!", REQ_CONFIRM);
7144 // ----------------------------------------------------------------------------
7145 // functions for loading generic level
7146 // ----------------------------------------------------------------------------
7148 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7149 struct LevelFileInfo *level_file_info,
7150 boolean level_info_only)
7152 // always start with reliable default values
7153 setLevelInfoToDefaults(level, level_info_only, TRUE);
7155 switch (level_file_info->type)
7157 case LEVEL_FILE_TYPE_RND:
7158 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7161 case LEVEL_FILE_TYPE_BD:
7162 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7163 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7166 case LEVEL_FILE_TYPE_EM:
7167 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7168 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7171 case LEVEL_FILE_TYPE_SP:
7172 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7173 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7176 case LEVEL_FILE_TYPE_MM:
7177 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7178 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7181 case LEVEL_FILE_TYPE_DC:
7182 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7185 case LEVEL_FILE_TYPE_SB:
7186 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7190 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7194 // if level file is invalid, restore level structure to default values
7195 if (level->no_valid_file)
7196 setLevelInfoToDefaults(level, level_info_only, FALSE);
7198 if (check_special_flags("use_native_bd_game_engine"))
7199 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7201 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7202 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7204 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7205 CopyNativeLevel_Native_to_RND(level);
7208 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7210 static struct LevelFileInfo level_file_info;
7212 // always start with reliable default values
7213 setFileInfoToDefaults(&level_file_info);
7215 level_file_info.nr = 0; // unknown level number
7216 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7218 setString(&level_file_info.filename, filename);
7220 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7223 static void LoadLevel_InitVersion(struct LevelInfo *level)
7227 if (leveldir_current == NULL) // only when dumping level
7230 // all engine modifications also valid for levels which use latest engine
7231 if (level->game_version < VERSION_IDENT(3,2,0,5))
7233 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7234 level->time_score_base = 10;
7237 if (leveldir_current->latest_engine)
7239 // ---------- use latest game engine --------------------------------------
7241 /* For all levels which are forced to use the latest game engine version
7242 (normally all but user contributed, private and undefined levels), set
7243 the game engine version to the actual version; this allows for actual
7244 corrections in the game engine to take effect for existing, converted
7245 levels (from "classic" or other existing games) to make the emulation
7246 of the corresponding game more accurate, while (hopefully) not breaking
7247 existing levels created from other players. */
7249 level->game_version = GAME_VERSION_ACTUAL;
7251 /* Set special EM style gems behaviour: EM style gems slip down from
7252 normal, steel and growing wall. As this is a more fundamental change,
7253 it seems better to set the default behaviour to "off" (as it is more
7254 natural) and make it configurable in the level editor (as a property
7255 of gem style elements). Already existing converted levels (neither
7256 private nor contributed levels) are changed to the new behaviour. */
7258 if (level->file_version < FILE_VERSION_2_0)
7259 level->em_slippery_gems = TRUE;
7264 // ---------- use game engine the level was created with --------------------
7266 /* For all levels which are not forced to use the latest game engine
7267 version (normally user contributed, private and undefined levels),
7268 use the version of the game engine the levels were created for.
7270 Since 2.0.1, the game engine version is now directly stored
7271 in the level file (chunk "VERS"), so there is no need anymore
7272 to set the game version from the file version (except for old,
7273 pre-2.0 levels, where the game version is still taken from the
7274 file format version used to store the level -- see above). */
7276 // player was faster than enemies in 1.0.0 and before
7277 if (level->file_version == FILE_VERSION_1_0)
7278 for (i = 0; i < MAX_PLAYERS; i++)
7279 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7281 // default behaviour for EM style gems was "slippery" only in 2.0.1
7282 if (level->game_version == VERSION_IDENT(2,0,1,0))
7283 level->em_slippery_gems = TRUE;
7285 // springs could be pushed over pits before (pre-release version) 2.2.0
7286 if (level->game_version < VERSION_IDENT(2,2,0,0))
7287 level->use_spring_bug = TRUE;
7289 if (level->game_version < VERSION_IDENT(3,2,0,5))
7291 // time orb caused limited time in endless time levels before 3.2.0-5
7292 level->use_time_orb_bug = TRUE;
7294 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7295 level->block_snap_field = FALSE;
7297 // extra time score was same value as time left score before 3.2.0-5
7298 level->extra_time_score = level->score[SC_TIME_BONUS];
7301 if (level->game_version < VERSION_IDENT(3,2,0,7))
7303 // default behaviour for snapping was "not continuous" before 3.2.0-7
7304 level->continuous_snapping = FALSE;
7307 // only few elements were able to actively move into acid before 3.1.0
7308 // trigger settings did not exist before 3.1.0; set to default "any"
7309 if (level->game_version < VERSION_IDENT(3,1,0,0))
7311 // correct "can move into acid" settings (all zero in old levels)
7313 level->can_move_into_acid_bits = 0; // nothing can move into acid
7314 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7316 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7317 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7318 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7319 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7321 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7322 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7324 // correct trigger settings (stored as zero == "none" in old levels)
7326 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7328 int element = EL_CUSTOM_START + i;
7329 struct ElementInfo *ei = &element_info[element];
7331 for (j = 0; j < ei->num_change_pages; j++)
7333 struct ElementChangeInfo *change = &ei->change_page[j];
7335 change->trigger_player = CH_PLAYER_ANY;
7336 change->trigger_page = CH_PAGE_ANY;
7341 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7343 int element = EL_CUSTOM_256;
7344 struct ElementInfo *ei = &element_info[element];
7345 struct ElementChangeInfo *change = &ei->change_page[0];
7347 /* This is needed to fix a problem that was caused by a bugfix in function
7348 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7349 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7350 not replace walkable elements, but instead just placed the player on it,
7351 without placing the Sokoban field under the player). Unfortunately, this
7352 breaks "Snake Bite" style levels when the snake is halfway through a door
7353 that just closes (the snake head is still alive and can be moved in this
7354 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7355 player (without Sokoban element) which then gets killed as designed). */
7357 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7358 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7359 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7360 change->target_element = EL_PLAYER_1;
7363 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7364 if (level->game_version < VERSION_IDENT(3,2,5,0))
7366 /* This is needed to fix a problem that was caused by a bugfix in function
7367 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7368 corrects the behaviour when a custom element changes to another custom
7369 element with a higher element number that has change actions defined.
7370 Normally, only one change per frame is allowed for custom elements.
7371 Therefore, it is checked if a custom element already changed in the
7372 current frame; if it did, subsequent changes are suppressed.
7373 Unfortunately, this is only checked for element changes, but not for
7374 change actions, which are still executed. As the function above loops
7375 through all custom elements from lower to higher, an element change
7376 resulting in a lower CE number won't be checked again, while a target
7377 element with a higher number will also be checked, and potential change
7378 actions will get executed for this CE, too (which is wrong), while
7379 further changes are ignored (which is correct). As this bugfix breaks
7380 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7381 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7382 behaviour for existing levels and tapes that make use of this bug */
7384 level->use_action_after_change_bug = TRUE;
7387 // not centering level after relocating player was default only in 3.2.3
7388 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7389 level->shifted_relocation = TRUE;
7391 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7392 if (level->game_version < VERSION_IDENT(3,2,6,0))
7393 level->em_explodes_by_fire = TRUE;
7395 // levels were solved by the first player entering an exit up to 4.1.0.0
7396 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7397 level->solved_by_one_player = TRUE;
7399 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7400 if (level->game_version < VERSION_IDENT(4,1,1,1))
7401 level->use_life_bugs = TRUE;
7403 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7404 if (level->game_version < VERSION_IDENT(4,1,1,1))
7405 level->sb_objects_needed = FALSE;
7407 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7408 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7409 level->finish_dig_collect = FALSE;
7411 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7412 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7413 level->keep_walkable_ce = TRUE;
7416 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7418 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7421 // check if this level is (not) a Sokoban level
7422 for (y = 0; y < level->fieldy; y++)
7423 for (x = 0; x < level->fieldx; x++)
7424 if (!IS_SB_ELEMENT(Tile[x][y]))
7425 is_sokoban_level = FALSE;
7427 if (is_sokoban_level)
7429 // set special level settings for Sokoban levels
7430 SetLevelSettings_SB(level);
7434 static void LoadLevel_InitSettings(struct LevelInfo *level)
7436 // adjust level settings for (non-native) Sokoban-style levels
7437 LoadLevel_InitSettings_SB(level);
7439 // rename levels with title "nameless level" or if renaming is forced
7440 if (leveldir_current->empty_level_name != NULL &&
7441 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7442 leveldir_current->force_level_name))
7443 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7444 leveldir_current->empty_level_name, level_nr);
7447 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7451 // map elements that have changed in newer versions
7452 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7453 level->game_version);
7454 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7455 for (x = 0; x < 3; x++)
7456 for (y = 0; y < 3; y++)
7457 level->yamyam_content[i].e[x][y] =
7458 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7459 level->game_version);
7463 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7467 // map custom element change events that have changed in newer versions
7468 // (these following values were accidentally changed in version 3.0.1)
7469 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7470 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7472 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7474 int element = EL_CUSTOM_START + i;
7476 // order of checking and copying events to be mapped is important
7477 // (do not change the start and end value -- they are constant)
7478 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7480 if (HAS_CHANGE_EVENT(element, j - 2))
7482 SET_CHANGE_EVENT(element, j - 2, FALSE);
7483 SET_CHANGE_EVENT(element, j, TRUE);
7487 // order of checking and copying events to be mapped is important
7488 // (do not change the start and end value -- they are constant)
7489 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7491 if (HAS_CHANGE_EVENT(element, j - 1))
7493 SET_CHANGE_EVENT(element, j - 1, FALSE);
7494 SET_CHANGE_EVENT(element, j, TRUE);
7500 // initialize "can_change" field for old levels with only one change page
7501 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7503 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7505 int element = EL_CUSTOM_START + i;
7507 if (CAN_CHANGE(element))
7508 element_info[element].change->can_change = TRUE;
7512 // correct custom element values (for old levels without these options)
7513 if (level->game_version < VERSION_IDENT(3,1,1,0))
7515 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7517 int element = EL_CUSTOM_START + i;
7518 struct ElementInfo *ei = &element_info[element];
7520 if (ei->access_direction == MV_NO_DIRECTION)
7521 ei->access_direction = MV_ALL_DIRECTIONS;
7525 // correct custom element values (fix invalid values for all versions)
7528 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7530 int element = EL_CUSTOM_START + i;
7531 struct ElementInfo *ei = &element_info[element];
7533 for (j = 0; j < ei->num_change_pages; j++)
7535 struct ElementChangeInfo *change = &ei->change_page[j];
7537 if (change->trigger_player == CH_PLAYER_NONE)
7538 change->trigger_player = CH_PLAYER_ANY;
7540 if (change->trigger_side == CH_SIDE_NONE)
7541 change->trigger_side = CH_SIDE_ANY;
7546 // initialize "can_explode" field for old levels which did not store this
7547 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7548 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7552 int element = EL_CUSTOM_START + i;
7554 if (EXPLODES_1X1_OLD(element))
7555 element_info[element].explosion_type = EXPLODES_1X1;
7557 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7558 EXPLODES_SMASHED(element) ||
7559 EXPLODES_IMPACT(element)));
7563 // correct previously hard-coded move delay values for maze runner style
7564 if (level->game_version < VERSION_IDENT(3,1,1,0))
7566 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7568 int element = EL_CUSTOM_START + i;
7570 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7572 // previously hard-coded and therefore ignored
7573 element_info[element].move_delay_fixed = 9;
7574 element_info[element].move_delay_random = 0;
7579 // set some other uninitialized values of custom elements in older levels
7580 if (level->game_version < VERSION_IDENT(3,1,0,0))
7582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7584 int element = EL_CUSTOM_START + i;
7586 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7588 element_info[element].explosion_delay = 17;
7589 element_info[element].ignition_delay = 8;
7593 // set mouse click change events to work for left/middle/right mouse button
7594 if (level->game_version < VERSION_IDENT(4,2,3,0))
7596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7598 int element = EL_CUSTOM_START + i;
7599 struct ElementInfo *ei = &element_info[element];
7601 for (j = 0; j < ei->num_change_pages; j++)
7603 struct ElementChangeInfo *change = &ei->change_page[j];
7605 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7606 change->has_event[CE_PRESSED_BY_MOUSE] ||
7607 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7608 change->has_event[CE_MOUSE_PRESSED_ON_X])
7609 change->trigger_side = CH_SIDE_ANY;
7615 static void LoadLevel_InitElements(struct LevelInfo *level)
7617 LoadLevel_InitStandardElements(level);
7619 if (level->file_has_custom_elements)
7620 LoadLevel_InitCustomElements(level);
7622 // initialize element properties for level editor etc.
7623 InitElementPropertiesEngine(level->game_version);
7624 InitElementPropertiesGfxElement();
7627 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7631 // map elements that have changed in newer versions
7632 for (y = 0; y < level->fieldy; y++)
7633 for (x = 0; x < level->fieldx; x++)
7634 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7635 level->game_version);
7637 // clear unused playfield data (nicer if level gets resized in editor)
7638 for (x = 0; x < MAX_LEV_FIELDX; x++)
7639 for (y = 0; y < MAX_LEV_FIELDY; y++)
7640 if (x >= level->fieldx || y >= level->fieldy)
7641 level->field[x][y] = EL_EMPTY;
7643 // copy elements to runtime playfield array
7644 for (x = 0; x < MAX_LEV_FIELDX; x++)
7645 for (y = 0; y < MAX_LEV_FIELDY; y++)
7646 Tile[x][y] = level->field[x][y];
7648 // initialize level size variables for faster access
7649 lev_fieldx = level->fieldx;
7650 lev_fieldy = level->fieldy;
7652 // determine border element for this level
7653 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7654 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7659 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7661 struct LevelFileInfo *level_file_info = &level->file_info;
7663 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7664 CopyNativeLevel_RND_to_Native(level);
7667 static void LoadLevelTemplate_LoadAndInit(void)
7669 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7671 LoadLevel_InitVersion(&level_template);
7672 LoadLevel_InitElements(&level_template);
7673 LoadLevel_InitSettings(&level_template);
7675 ActivateLevelTemplate();
7678 void LoadLevelTemplate(int nr)
7680 if (!fileExists(getGlobalLevelTemplateFilename()))
7682 Warn("no level template found for this level");
7687 setLevelFileInfo(&level_template.file_info, nr);
7689 LoadLevelTemplate_LoadAndInit();
7692 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7694 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7696 LoadLevelTemplate_LoadAndInit();
7699 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7701 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7703 if (level.use_custom_template)
7705 if (network_level != NULL)
7706 LoadNetworkLevelTemplate(network_level);
7708 LoadLevelTemplate(-1);
7711 LoadLevel_InitVersion(&level);
7712 LoadLevel_InitElements(&level);
7713 LoadLevel_InitPlayfield(&level);
7714 LoadLevel_InitSettings(&level);
7716 LoadLevel_InitNativeEngines(&level);
7719 void LoadLevel(int nr)
7721 SetLevelSetInfo(leveldir_current->identifier, nr);
7723 setLevelFileInfo(&level.file_info, nr);
7725 LoadLevel_LoadAndInit(NULL);
7728 void LoadLevelInfoOnly(int nr)
7730 setLevelFileInfo(&level.file_info, nr);
7732 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7735 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7737 SetLevelSetInfo(network_level->leveldir_identifier,
7738 network_level->file_info.nr);
7740 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7742 LoadLevel_LoadAndInit(network_level);
7745 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7749 chunk_size += putFileVersion(file, level->file_version);
7750 chunk_size += putFileVersion(file, level->game_version);
7755 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7759 chunk_size += putFile16BitBE(file, level->creation_date.year);
7760 chunk_size += putFile8Bit(file, level->creation_date.month);
7761 chunk_size += putFile8Bit(file, level->creation_date.day);
7766 #if ENABLE_HISTORIC_CHUNKS
7767 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7771 putFile8Bit(file, level->fieldx);
7772 putFile8Bit(file, level->fieldy);
7774 putFile16BitBE(file, level->time);
7775 putFile16BitBE(file, level->gems_needed);
7777 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7778 putFile8Bit(file, level->name[i]);
7780 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7781 putFile8Bit(file, level->score[i]);
7783 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7784 for (y = 0; y < 3; y++)
7785 for (x = 0; x < 3; x++)
7786 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7787 level->yamyam_content[i].e[x][y]));
7788 putFile8Bit(file, level->amoeba_speed);
7789 putFile8Bit(file, level->time_magic_wall);
7790 putFile8Bit(file, level->time_wheel);
7791 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7792 level->amoeba_content));
7793 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7794 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7795 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7796 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7798 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7800 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7801 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7802 putFile32BitBE(file, level->can_move_into_acid_bits);
7803 putFile8Bit(file, level->dont_collide_with_bits);
7805 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7806 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7808 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7809 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7810 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7812 putFile8Bit(file, level->game_engine_type);
7814 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7818 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7823 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7824 chunk_size += putFile8Bit(file, level->name[i]);
7829 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7834 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7835 chunk_size += putFile8Bit(file, level->author[i]);
7840 #if ENABLE_HISTORIC_CHUNKS
7841 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7846 for (y = 0; y < level->fieldy; y++)
7847 for (x = 0; x < level->fieldx; x++)
7848 if (level->encoding_16bit_field)
7849 chunk_size += putFile16BitBE(file, level->field[x][y]);
7851 chunk_size += putFile8Bit(file, level->field[x][y]);
7857 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7862 for (y = 0; y < level->fieldy; y++)
7863 for (x = 0; x < level->fieldx; x++)
7864 chunk_size += putFile16BitBE(file, level->field[x][y]);
7869 #if ENABLE_HISTORIC_CHUNKS
7870 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7874 putFile8Bit(file, EL_YAMYAM);
7875 putFile8Bit(file, level->num_yamyam_contents);
7876 putFile8Bit(file, 0);
7877 putFile8Bit(file, 0);
7879 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7880 for (y = 0; y < 3; y++)
7881 for (x = 0; x < 3; x++)
7882 if (level->encoding_16bit_field)
7883 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7885 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7889 #if ENABLE_HISTORIC_CHUNKS
7890 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7893 int num_contents, content_xsize, content_ysize;
7894 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7896 if (element == EL_YAMYAM)
7898 num_contents = level->num_yamyam_contents;
7902 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7903 for (y = 0; y < 3; y++)
7904 for (x = 0; x < 3; x++)
7905 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7907 else if (element == EL_BD_AMOEBA)
7913 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7914 for (y = 0; y < 3; y++)
7915 for (x = 0; x < 3; x++)
7916 content_array[i][x][y] = EL_EMPTY;
7917 content_array[0][0][0] = level->amoeba_content;
7921 // chunk header already written -- write empty chunk data
7922 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7924 Warn("cannot save content for element '%d'", element);
7929 putFile16BitBE(file, element);
7930 putFile8Bit(file, num_contents);
7931 putFile8Bit(file, content_xsize);
7932 putFile8Bit(file, content_ysize);
7934 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7936 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7937 for (y = 0; y < 3; y++)
7938 for (x = 0; x < 3; x++)
7939 putFile16BitBE(file, content_array[i][x][y]);
7943 #if ENABLE_HISTORIC_CHUNKS
7944 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7946 int envelope_nr = element - EL_ENVELOPE_1;
7947 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7951 chunk_size += putFile16BitBE(file, element);
7952 chunk_size += putFile16BitBE(file, envelope_len);
7953 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7954 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7956 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7957 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7959 for (i = 0; i < envelope_len; i++)
7960 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7966 #if ENABLE_HISTORIC_CHUNKS
7967 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7968 int num_changed_custom_elements)
7972 putFile16BitBE(file, num_changed_custom_elements);
7974 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7976 int element = EL_CUSTOM_START + i;
7978 struct ElementInfo *ei = &element_info[element];
7980 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7982 if (check < num_changed_custom_elements)
7984 putFile16BitBE(file, element);
7985 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7992 if (check != num_changed_custom_elements) // should not happen
7993 Warn("inconsistent number of custom element properties");
7997 #if ENABLE_HISTORIC_CHUNKS
7998 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7999 int num_changed_custom_elements)
8003 putFile16BitBE(file, num_changed_custom_elements);
8005 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8007 int element = EL_CUSTOM_START + i;
8009 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8011 if (check < num_changed_custom_elements)
8013 putFile16BitBE(file, element);
8014 putFile16BitBE(file, element_info[element].change->target_element);
8021 if (check != num_changed_custom_elements) // should not happen
8022 Warn("inconsistent number of custom target elements");
8026 #if ENABLE_HISTORIC_CHUNKS
8027 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8028 int num_changed_custom_elements)
8030 int i, j, x, y, check = 0;
8032 putFile16BitBE(file, num_changed_custom_elements);
8034 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8036 int element = EL_CUSTOM_START + i;
8037 struct ElementInfo *ei = &element_info[element];
8039 if (ei->modified_settings)
8041 if (check < num_changed_custom_elements)
8043 putFile16BitBE(file, element);
8045 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8046 putFile8Bit(file, ei->description[j]);
8048 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8050 // some free bytes for future properties and padding
8051 WriteUnusedBytesToFile(file, 7);
8053 putFile8Bit(file, ei->use_gfx_element);
8054 putFile16BitBE(file, ei->gfx_element_initial);
8056 putFile8Bit(file, ei->collect_score_initial);
8057 putFile8Bit(file, ei->collect_count_initial);
8059 putFile16BitBE(file, ei->push_delay_fixed);
8060 putFile16BitBE(file, ei->push_delay_random);
8061 putFile16BitBE(file, ei->move_delay_fixed);
8062 putFile16BitBE(file, ei->move_delay_random);
8064 putFile16BitBE(file, ei->move_pattern);
8065 putFile8Bit(file, ei->move_direction_initial);
8066 putFile8Bit(file, ei->move_stepsize);
8068 for (y = 0; y < 3; y++)
8069 for (x = 0; x < 3; x++)
8070 putFile16BitBE(file, ei->content.e[x][y]);
8072 putFile32BitBE(file, ei->change->events);
8074 putFile16BitBE(file, ei->change->target_element);
8076 putFile16BitBE(file, ei->change->delay_fixed);
8077 putFile16BitBE(file, ei->change->delay_random);
8078 putFile16BitBE(file, ei->change->delay_frames);
8080 putFile16BitBE(file, ei->change->initial_trigger_element);
8082 putFile8Bit(file, ei->change->explode);
8083 putFile8Bit(file, ei->change->use_target_content);
8084 putFile8Bit(file, ei->change->only_if_complete);
8085 putFile8Bit(file, ei->change->use_random_replace);
8087 putFile8Bit(file, ei->change->random_percentage);
8088 putFile8Bit(file, ei->change->replace_when);
8090 for (y = 0; y < 3; y++)
8091 for (x = 0; x < 3; x++)
8092 putFile16BitBE(file, ei->change->content.e[x][y]);
8094 putFile8Bit(file, ei->slippery_type);
8096 // some free bytes for future properties and padding
8097 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8104 if (check != num_changed_custom_elements) // should not happen
8105 Warn("inconsistent number of custom element properties");
8109 #if ENABLE_HISTORIC_CHUNKS
8110 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8112 struct ElementInfo *ei = &element_info[element];
8115 // ---------- custom element base property values (96 bytes) ----------------
8117 putFile16BitBE(file, element);
8119 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8120 putFile8Bit(file, ei->description[i]);
8122 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8124 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8126 putFile8Bit(file, ei->num_change_pages);
8128 putFile16BitBE(file, ei->ce_value_fixed_initial);
8129 putFile16BitBE(file, ei->ce_value_random_initial);
8130 putFile8Bit(file, ei->use_last_ce_value);
8132 putFile8Bit(file, ei->use_gfx_element);
8133 putFile16BitBE(file, ei->gfx_element_initial);
8135 putFile8Bit(file, ei->collect_score_initial);
8136 putFile8Bit(file, ei->collect_count_initial);
8138 putFile8Bit(file, ei->drop_delay_fixed);
8139 putFile8Bit(file, ei->push_delay_fixed);
8140 putFile8Bit(file, ei->drop_delay_random);
8141 putFile8Bit(file, ei->push_delay_random);
8142 putFile16BitBE(file, ei->move_delay_fixed);
8143 putFile16BitBE(file, ei->move_delay_random);
8145 // bits 0 - 15 of "move_pattern" ...
8146 putFile16BitBE(file, ei->move_pattern & 0xffff);
8147 putFile8Bit(file, ei->move_direction_initial);
8148 putFile8Bit(file, ei->move_stepsize);
8150 putFile8Bit(file, ei->slippery_type);
8152 for (y = 0; y < 3; y++)
8153 for (x = 0; x < 3; x++)
8154 putFile16BitBE(file, ei->content.e[x][y]);
8156 putFile16BitBE(file, ei->move_enter_element);
8157 putFile16BitBE(file, ei->move_leave_element);
8158 putFile8Bit(file, ei->move_leave_type);
8160 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8161 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8163 putFile8Bit(file, ei->access_direction);
8165 putFile8Bit(file, ei->explosion_delay);
8166 putFile8Bit(file, ei->ignition_delay);
8167 putFile8Bit(file, ei->explosion_type);
8169 // some free bytes for future custom property values and padding
8170 WriteUnusedBytesToFile(file, 1);
8172 // ---------- change page property values (48 bytes) ------------------------
8174 for (i = 0; i < ei->num_change_pages; i++)
8176 struct ElementChangeInfo *change = &ei->change_page[i];
8177 unsigned int event_bits;
8179 // bits 0 - 31 of "has_event[]" ...
8181 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8182 if (change->has_event[j])
8183 event_bits |= (1u << j);
8184 putFile32BitBE(file, event_bits);
8186 putFile16BitBE(file, change->target_element);
8188 putFile16BitBE(file, change->delay_fixed);
8189 putFile16BitBE(file, change->delay_random);
8190 putFile16BitBE(file, change->delay_frames);
8192 putFile16BitBE(file, change->initial_trigger_element);
8194 putFile8Bit(file, change->explode);
8195 putFile8Bit(file, change->use_target_content);
8196 putFile8Bit(file, change->only_if_complete);
8197 putFile8Bit(file, change->use_random_replace);
8199 putFile8Bit(file, change->random_percentage);
8200 putFile8Bit(file, change->replace_when);
8202 for (y = 0; y < 3; y++)
8203 for (x = 0; x < 3; x++)
8204 putFile16BitBE(file, change->target_content.e[x][y]);
8206 putFile8Bit(file, change->can_change);
8208 putFile8Bit(file, change->trigger_side);
8210 putFile8Bit(file, change->trigger_player);
8211 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8212 log_2(change->trigger_page)));
8214 putFile8Bit(file, change->has_action);
8215 putFile8Bit(file, change->action_type);
8216 putFile8Bit(file, change->action_mode);
8217 putFile16BitBE(file, change->action_arg);
8219 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8221 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8222 if (change->has_event[j])
8223 event_bits |= (1u << (j - 32));
8224 putFile8Bit(file, event_bits);
8229 #if ENABLE_HISTORIC_CHUNKS
8230 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8232 struct ElementInfo *ei = &element_info[element];
8233 struct ElementGroupInfo *group = ei->group;
8236 putFile16BitBE(file, element);
8238 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8239 putFile8Bit(file, ei->description[i]);
8241 putFile8Bit(file, group->num_elements);
8243 putFile8Bit(file, ei->use_gfx_element);
8244 putFile16BitBE(file, ei->gfx_element_initial);
8246 putFile8Bit(file, group->choice_mode);
8248 // some free bytes for future values and padding
8249 WriteUnusedBytesToFile(file, 3);
8251 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8252 putFile16BitBE(file, group->element[i]);
8256 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8257 boolean write_element)
8259 int save_type = entry->save_type;
8260 int data_type = entry->data_type;
8261 int conf_type = entry->conf_type;
8262 int byte_mask = conf_type & CONF_MASK_BYTES;
8263 int element = entry->element;
8264 int default_value = entry->default_value;
8266 boolean modified = FALSE;
8268 if (byte_mask != CONF_MASK_MULTI_BYTES)
8270 void *value_ptr = entry->value;
8271 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8274 // check if any settings have been modified before saving them
8275 if (value != default_value)
8278 // do not save if explicitly told or if unmodified default settings
8279 if ((save_type == SAVE_CONF_NEVER) ||
8280 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8284 num_bytes += putFile16BitBE(file, element);
8286 num_bytes += putFile8Bit(file, conf_type);
8287 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8288 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8289 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8292 else if (data_type == TYPE_STRING)
8294 char *default_string = entry->default_string;
8295 char *string = (char *)(entry->value);
8296 int string_length = strlen(string);
8299 // check if any settings have been modified before saving them
8300 if (!strEqual(string, default_string))
8303 // do not save if explicitly told or if unmodified default settings
8304 if ((save_type == SAVE_CONF_NEVER) ||
8305 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8309 num_bytes += putFile16BitBE(file, element);
8311 num_bytes += putFile8Bit(file, conf_type);
8312 num_bytes += putFile16BitBE(file, string_length);
8314 for (i = 0; i < string_length; i++)
8315 num_bytes += putFile8Bit(file, string[i]);
8317 else if (data_type == TYPE_ELEMENT_LIST)
8319 int *element_array = (int *)(entry->value);
8320 int num_elements = *(int *)(entry->num_entities);
8323 // check if any settings have been modified before saving them
8324 for (i = 0; i < num_elements; i++)
8325 if (element_array[i] != default_value)
8328 // do not save if explicitly told or if unmodified default settings
8329 if ((save_type == SAVE_CONF_NEVER) ||
8330 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8334 num_bytes += putFile16BitBE(file, element);
8336 num_bytes += putFile8Bit(file, conf_type);
8337 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8339 for (i = 0; i < num_elements; i++)
8340 num_bytes += putFile16BitBE(file, element_array[i]);
8342 else if (data_type == TYPE_CONTENT_LIST)
8344 struct Content *content = (struct Content *)(entry->value);
8345 int num_contents = *(int *)(entry->num_entities);
8348 // check if any settings have been modified before saving them
8349 for (i = 0; i < num_contents; i++)
8350 for (y = 0; y < 3; y++)
8351 for (x = 0; x < 3; x++)
8352 if (content[i].e[x][y] != default_value)
8355 // do not save if explicitly told or if unmodified default settings
8356 if ((save_type == SAVE_CONF_NEVER) ||
8357 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8361 num_bytes += putFile16BitBE(file, element);
8363 num_bytes += putFile8Bit(file, conf_type);
8364 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8366 for (i = 0; i < num_contents; i++)
8367 for (y = 0; y < 3; y++)
8368 for (x = 0; x < 3; x++)
8369 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8375 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8380 li = *level; // copy level data into temporary buffer
8382 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8383 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8388 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8393 li = *level; // copy level data into temporary buffer
8395 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8396 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8401 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8403 int envelope_nr = element - EL_ENVELOPE_1;
8407 chunk_size += putFile16BitBE(file, element);
8409 // copy envelope data into temporary buffer
8410 xx_envelope = level->envelope[envelope_nr];
8412 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8413 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8418 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8420 struct ElementInfo *ei = &element_info[element];
8424 chunk_size += putFile16BitBE(file, element);
8426 xx_ei = *ei; // copy element data into temporary buffer
8428 // set default description string for this specific element
8429 strcpy(xx_default_description, getDefaultElementDescription(ei));
8431 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8432 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8434 for (i = 0; i < ei->num_change_pages; i++)
8436 struct ElementChangeInfo *change = &ei->change_page[i];
8438 xx_current_change_page = i;
8440 xx_change = *change; // copy change data into temporary buffer
8443 setEventBitsFromEventFlags(change);
8445 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8446 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8453 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8455 struct ElementInfo *ei = &element_info[element];
8456 struct ElementGroupInfo *group = ei->group;
8460 chunk_size += putFile16BitBE(file, element);
8462 xx_ei = *ei; // copy element data into temporary buffer
8463 xx_group = *group; // copy group 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_GRPX[i].data_type != -1; i++)
8469 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8474 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8476 struct ElementInfo *ei = &element_info[element];
8480 chunk_size += putFile16BitBE(file, element);
8482 xx_ei = *ei; // copy element data into temporary buffer
8484 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8485 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8490 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8491 boolean save_as_template)
8497 if (!(file = fopen(filename, MODE_WRITE)))
8499 Warn("cannot save level file '%s'", filename);
8504 level->file_version = FILE_VERSION_ACTUAL;
8505 level->game_version = GAME_VERSION_ACTUAL;
8507 level->creation_date = getCurrentDate();
8509 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8510 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8512 chunk_size = SaveLevel_VERS(NULL, level);
8513 putFileChunkBE(file, "VERS", chunk_size);
8514 SaveLevel_VERS(file, level);
8516 chunk_size = SaveLevel_DATE(NULL, level);
8517 putFileChunkBE(file, "DATE", chunk_size);
8518 SaveLevel_DATE(file, level);
8520 chunk_size = SaveLevel_NAME(NULL, level);
8521 putFileChunkBE(file, "NAME", chunk_size);
8522 SaveLevel_NAME(file, level);
8524 chunk_size = SaveLevel_AUTH(NULL, level);
8525 putFileChunkBE(file, "AUTH", chunk_size);
8526 SaveLevel_AUTH(file, level);
8528 chunk_size = SaveLevel_INFO(NULL, level);
8529 putFileChunkBE(file, "INFO", chunk_size);
8530 SaveLevel_INFO(file, level);
8532 chunk_size = SaveLevel_BODY(NULL, level);
8533 putFileChunkBE(file, "BODY", chunk_size);
8534 SaveLevel_BODY(file, level);
8536 chunk_size = SaveLevel_ELEM(NULL, level);
8537 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8539 putFileChunkBE(file, "ELEM", chunk_size);
8540 SaveLevel_ELEM(file, level);
8543 for (i = 0; i < NUM_ENVELOPES; i++)
8545 int element = EL_ENVELOPE_1 + i;
8547 chunk_size = SaveLevel_NOTE(NULL, level, element);
8548 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8550 putFileChunkBE(file, "NOTE", chunk_size);
8551 SaveLevel_NOTE(file, level, element);
8555 // if not using template level, check for non-default custom/group elements
8556 if (!level->use_custom_template || save_as_template)
8558 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8560 int element = EL_CUSTOM_START + i;
8562 chunk_size = SaveLevel_CUSX(NULL, level, element);
8563 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8565 putFileChunkBE(file, "CUSX", chunk_size);
8566 SaveLevel_CUSX(file, level, element);
8570 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8572 int element = EL_GROUP_START + i;
8574 chunk_size = SaveLevel_GRPX(NULL, level, element);
8575 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8577 putFileChunkBE(file, "GRPX", chunk_size);
8578 SaveLevel_GRPX(file, level, element);
8582 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8584 int element = GET_EMPTY_ELEMENT(i);
8586 chunk_size = SaveLevel_EMPX(NULL, level, element);
8587 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8589 putFileChunkBE(file, "EMPX", chunk_size);
8590 SaveLevel_EMPX(file, level, element);
8597 SetFilePermissions(filename, PERMS_PRIVATE);
8600 void SaveLevel(int nr)
8602 char *filename = getDefaultLevelFilename(nr);
8604 SaveLevelFromFilename(&level, filename, FALSE);
8607 void SaveLevelTemplate(void)
8609 char *filename = getLocalLevelTemplateFilename();
8611 SaveLevelFromFilename(&level, filename, TRUE);
8614 boolean SaveLevelChecked(int nr)
8616 char *filename = getDefaultLevelFilename(nr);
8617 boolean new_level = !fileExists(filename);
8618 boolean level_saved = FALSE;
8620 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8625 Request("Level saved!", REQ_CONFIRM);
8633 void DumpLevel(struct LevelInfo *level)
8635 if (level->no_level_file || level->no_valid_file)
8637 Warn("cannot dump -- no valid level file found");
8643 Print("Level xxx (file version %08d, game version %08d)\n",
8644 level->file_version, level->game_version);
8647 Print("Level author: '%s'\n", level->author);
8648 Print("Level title: '%s'\n", level->name);
8650 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8652 Print("Level time: %d seconds\n", level->time);
8653 Print("Gems needed: %d\n", level->gems_needed);
8655 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8656 Print("Time for wheel: %d seconds\n", level->time_wheel);
8657 Print("Time for light: %d seconds\n", level->time_light);
8658 Print("Time for timegate: %d seconds\n", level->time_timegate);
8660 Print("Amoeba speed: %d\n", level->amoeba_speed);
8663 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8664 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8665 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8666 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8667 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8668 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8674 for (i = 0; i < NUM_ENVELOPES; i++)
8676 char *text = level->envelope[i].text;
8677 int text_len = strlen(text);
8678 boolean has_text = FALSE;
8680 for (j = 0; j < text_len; j++)
8681 if (text[j] != ' ' && text[j] != '\n')
8687 Print("Envelope %d:\n'%s'\n", i + 1, text);
8695 void DumpLevels(void)
8697 static LevelDirTree *dumplevel_leveldir = NULL;
8699 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8700 global.dumplevel_leveldir);
8702 if (dumplevel_leveldir == NULL)
8703 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8705 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8706 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8707 Fail("no such level number: %d", global.dumplevel_level_nr);
8709 leveldir_current = dumplevel_leveldir;
8711 LoadLevel(global.dumplevel_level_nr);
8718 // ============================================================================
8719 // tape file functions
8720 // ============================================================================
8722 static void setTapeInfoToDefaults(void)
8726 // always start with reliable default values (empty tape)
8729 // default values (also for pre-1.2 tapes) with only the first player
8730 tape.player_participates[0] = TRUE;
8731 for (i = 1; i < MAX_PLAYERS; i++)
8732 tape.player_participates[i] = FALSE;
8734 // at least one (default: the first) player participates in every tape
8735 tape.num_participating_players = 1;
8737 tape.property_bits = TAPE_PROPERTY_NONE;
8739 tape.level_nr = level_nr;
8741 tape.changed = FALSE;
8742 tape.solved = FALSE;
8744 tape.recording = FALSE;
8745 tape.playing = FALSE;
8746 tape.pausing = FALSE;
8748 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8749 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8751 tape.no_info_chunk = TRUE;
8752 tape.no_valid_file = FALSE;
8755 static int getTapePosSize(struct TapeInfo *tape)
8757 int tape_pos_size = 0;
8759 if (tape->use_key_actions)
8760 tape_pos_size += tape->num_participating_players;
8762 if (tape->use_mouse_actions)
8763 tape_pos_size += 3; // x and y position and mouse button mask
8765 tape_pos_size += 1; // tape action delay value
8767 return tape_pos_size;
8770 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8772 tape->use_key_actions = FALSE;
8773 tape->use_mouse_actions = FALSE;
8775 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8776 tape->use_key_actions = TRUE;
8778 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8779 tape->use_mouse_actions = TRUE;
8782 static int getTapeActionValue(struct TapeInfo *tape)
8784 return (tape->use_key_actions &&
8785 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8786 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8787 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8788 TAPE_ACTIONS_DEFAULT);
8791 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8793 tape->file_version = getFileVersion(file);
8794 tape->game_version = getFileVersion(file);
8799 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8803 tape->random_seed = getFile32BitBE(file);
8804 tape->date = getFile32BitBE(file);
8805 tape->length = getFile32BitBE(file);
8807 // read header fields that are new since version 1.2
8808 if (tape->file_version >= FILE_VERSION_1_2)
8810 byte store_participating_players = getFile8Bit(file);
8813 // since version 1.2, tapes store which players participate in the tape
8814 tape->num_participating_players = 0;
8815 for (i = 0; i < MAX_PLAYERS; i++)
8817 tape->player_participates[i] = FALSE;
8819 if (store_participating_players & (1 << i))
8821 tape->player_participates[i] = TRUE;
8822 tape->num_participating_players++;
8826 setTapeActionFlags(tape, getFile8Bit(file));
8828 tape->property_bits = getFile8Bit(file);
8829 tape->solved = getFile8Bit(file);
8831 engine_version = getFileVersion(file);
8832 if (engine_version > 0)
8833 tape->engine_version = engine_version;
8835 tape->engine_version = tape->game_version;
8841 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8843 tape->scr_fieldx = getFile8Bit(file);
8844 tape->scr_fieldy = getFile8Bit(file);
8849 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8851 char *level_identifier = NULL;
8852 int level_identifier_size;
8855 tape->no_info_chunk = FALSE;
8857 level_identifier_size = getFile16BitBE(file);
8859 level_identifier = checked_malloc(level_identifier_size);
8861 for (i = 0; i < level_identifier_size; i++)
8862 level_identifier[i] = getFile8Bit(file);
8864 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8865 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8867 checked_free(level_identifier);
8869 tape->level_nr = getFile16BitBE(file);
8871 chunk_size = 2 + level_identifier_size + 2;
8876 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8879 int tape_pos_size = getTapePosSize(tape);
8880 int chunk_size_expected = tape_pos_size * tape->length;
8882 if (chunk_size_expected != chunk_size)
8884 ReadUnusedBytesFromFile(file, chunk_size);
8885 return chunk_size_expected;
8888 for (i = 0; i < tape->length; i++)
8890 if (i >= MAX_TAPE_LEN)
8892 Warn("tape truncated -- size exceeds maximum tape size %d",
8895 // tape too large; read and ignore remaining tape data from this chunk
8896 for (;i < tape->length; i++)
8897 ReadUnusedBytesFromFile(file, tape_pos_size);
8902 if (tape->use_key_actions)
8904 for (j = 0; j < MAX_PLAYERS; j++)
8906 tape->pos[i].action[j] = MV_NONE;
8908 if (tape->player_participates[j])
8909 tape->pos[i].action[j] = getFile8Bit(file);
8913 if (tape->use_mouse_actions)
8915 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8916 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8917 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8920 tape->pos[i].delay = getFile8Bit(file);
8922 if (tape->file_version == FILE_VERSION_1_0)
8924 // eliminate possible diagonal moves in old tapes
8925 // this is only for backward compatibility
8927 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8928 byte action = tape->pos[i].action[0];
8929 int k, num_moves = 0;
8931 for (k = 0; k < 4; k++)
8933 if (action & joy_dir[k])
8935 tape->pos[i + num_moves].action[0] = joy_dir[k];
8937 tape->pos[i + num_moves].delay = 0;
8946 tape->length += num_moves;
8949 else if (tape->file_version < FILE_VERSION_2_0)
8951 // convert pre-2.0 tapes to new tape format
8953 if (tape->pos[i].delay > 1)
8956 tape->pos[i + 1] = tape->pos[i];
8957 tape->pos[i + 1].delay = 1;
8960 for (j = 0; j < MAX_PLAYERS; j++)
8961 tape->pos[i].action[j] = MV_NONE;
8962 tape->pos[i].delay--;
8969 if (checkEndOfFile(file))
8973 if (i != tape->length)
8974 chunk_size = tape_pos_size * i;
8979 static void LoadTape_SokobanSolution(char *filename)
8982 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8984 if (!(file = openFile(filename, MODE_READ)))
8986 tape.no_valid_file = TRUE;
8991 while (!checkEndOfFile(file))
8993 unsigned char c = getByteFromFile(file);
8995 if (checkEndOfFile(file))
9002 tape.pos[tape.length].action[0] = MV_UP;
9003 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9009 tape.pos[tape.length].action[0] = MV_DOWN;
9010 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9016 tape.pos[tape.length].action[0] = MV_LEFT;
9017 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9023 tape.pos[tape.length].action[0] = MV_RIGHT;
9024 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9032 // ignore white-space characters
9036 tape.no_valid_file = TRUE;
9038 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9046 if (tape.no_valid_file)
9049 tape.length_frames = GetTapeLengthFrames();
9050 tape.length_seconds = GetTapeLengthSeconds();
9053 void LoadTapeFromFilename(char *filename)
9055 char cookie[MAX_LINE_LEN];
9056 char chunk_name[CHUNK_ID_LEN + 1];
9060 // always start with reliable default values
9061 setTapeInfoToDefaults();
9063 if (strSuffix(filename, ".sln"))
9065 LoadTape_SokobanSolution(filename);
9070 if (!(file = openFile(filename, MODE_READ)))
9072 tape.no_valid_file = TRUE;
9077 getFileChunkBE(file, chunk_name, NULL);
9078 if (strEqual(chunk_name, "RND1"))
9080 getFile32BitBE(file); // not used
9082 getFileChunkBE(file, chunk_name, NULL);
9083 if (!strEqual(chunk_name, "TAPE"))
9085 tape.no_valid_file = TRUE;
9087 Warn("unknown format of tape file '%s'", filename);
9094 else // check for pre-2.0 file format with cookie string
9096 strcpy(cookie, chunk_name);
9097 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9099 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9100 cookie[strlen(cookie) - 1] = '\0';
9102 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9104 tape.no_valid_file = TRUE;
9106 Warn("unknown format of tape file '%s'", filename);
9113 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9115 tape.no_valid_file = TRUE;
9117 Warn("unsupported version of tape file '%s'", filename);
9124 // pre-2.0 tape files have no game version, so use file version here
9125 tape.game_version = tape.file_version;
9128 if (tape.file_version < FILE_VERSION_1_2)
9130 // tape files from versions before 1.2.0 without chunk structure
9131 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9132 LoadTape_BODY(file, 2 * tape.length, &tape);
9140 int (*loader)(File *, int, struct TapeInfo *);
9144 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9145 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9146 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9147 { "INFO", -1, LoadTape_INFO },
9148 { "BODY", -1, LoadTape_BODY },
9152 while (getFileChunkBE(file, chunk_name, &chunk_size))
9156 while (chunk_info[i].name != NULL &&
9157 !strEqual(chunk_name, chunk_info[i].name))
9160 if (chunk_info[i].name == NULL)
9162 Warn("unknown chunk '%s' in tape file '%s'",
9163 chunk_name, filename);
9165 ReadUnusedBytesFromFile(file, chunk_size);
9167 else if (chunk_info[i].size != -1 &&
9168 chunk_info[i].size != chunk_size)
9170 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9171 chunk_size, chunk_name, filename);
9173 ReadUnusedBytesFromFile(file, chunk_size);
9177 // call function to load this tape chunk
9178 int chunk_size_expected =
9179 (chunk_info[i].loader)(file, chunk_size, &tape);
9181 // the size of some chunks cannot be checked before reading other
9182 // chunks first (like "HEAD" and "BODY") that contain some header
9183 // information, so check them here
9184 if (chunk_size_expected != chunk_size)
9186 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9187 chunk_size, chunk_name, filename);
9195 tape.length_frames = GetTapeLengthFrames();
9196 tape.length_seconds = GetTapeLengthSeconds();
9199 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9201 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9203 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9204 tape.engine_version);
9208 void LoadTape(int nr)
9210 char *filename = getTapeFilename(nr);
9212 LoadTapeFromFilename(filename);
9215 void LoadSolutionTape(int nr)
9217 char *filename = getSolutionTapeFilename(nr);
9219 LoadTapeFromFilename(filename);
9221 if (TAPE_IS_EMPTY(tape))
9223 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9224 level.native_bd_level->replay != NULL)
9225 CopyNativeTape_BD_to_RND(&level);
9226 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9227 level.native_sp_level->demo.is_available)
9228 CopyNativeTape_SP_to_RND(&level);
9232 void LoadScoreTape(char *score_tape_basename, int nr)
9234 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9236 LoadTapeFromFilename(filename);
9239 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9241 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9243 LoadTapeFromFilename(filename);
9246 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9248 // chunk required for team mode tapes with non-default screen size
9249 return (tape->num_participating_players > 1 &&
9250 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9251 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9254 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9256 putFileVersion(file, tape->file_version);
9257 putFileVersion(file, tape->game_version);
9260 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9263 byte store_participating_players = 0;
9265 // set bits for participating players for compact storage
9266 for (i = 0; i < MAX_PLAYERS; i++)
9267 if (tape->player_participates[i])
9268 store_participating_players |= (1 << i);
9270 putFile32BitBE(file, tape->random_seed);
9271 putFile32BitBE(file, tape->date);
9272 putFile32BitBE(file, tape->length);
9274 putFile8Bit(file, store_participating_players);
9276 putFile8Bit(file, getTapeActionValue(tape));
9278 putFile8Bit(file, tape->property_bits);
9279 putFile8Bit(file, tape->solved);
9281 putFileVersion(file, tape->engine_version);
9284 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9286 putFile8Bit(file, tape->scr_fieldx);
9287 putFile8Bit(file, tape->scr_fieldy);
9290 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9292 int level_identifier_size = strlen(tape->level_identifier) + 1;
9295 putFile16BitBE(file, level_identifier_size);
9297 for (i = 0; i < level_identifier_size; i++)
9298 putFile8Bit(file, tape->level_identifier[i]);
9300 putFile16BitBE(file, tape->level_nr);
9303 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9307 for (i = 0; i < tape->length; i++)
9309 if (tape->use_key_actions)
9311 for (j = 0; j < MAX_PLAYERS; j++)
9312 if (tape->player_participates[j])
9313 putFile8Bit(file, tape->pos[i].action[j]);
9316 if (tape->use_mouse_actions)
9318 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9319 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9320 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9323 putFile8Bit(file, tape->pos[i].delay);
9327 void SaveTapeToFilename(char *filename)
9331 int info_chunk_size;
9332 int body_chunk_size;
9334 if (!(file = fopen(filename, MODE_WRITE)))
9336 Warn("cannot save level recording file '%s'", filename);
9341 tape_pos_size = getTapePosSize(&tape);
9343 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9344 body_chunk_size = tape_pos_size * tape.length;
9346 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9347 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9349 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9350 SaveTape_VERS(file, &tape);
9352 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9353 SaveTape_HEAD(file, &tape);
9355 if (checkSaveTape_SCRN(&tape))
9357 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9358 SaveTape_SCRN(file, &tape);
9361 putFileChunkBE(file, "INFO", info_chunk_size);
9362 SaveTape_INFO(file, &tape);
9364 putFileChunkBE(file, "BODY", body_chunk_size);
9365 SaveTape_BODY(file, &tape);
9369 SetFilePermissions(filename, PERMS_PRIVATE);
9372 static void SaveTapeExt(char *filename)
9376 tape.file_version = FILE_VERSION_ACTUAL;
9377 tape.game_version = GAME_VERSION_ACTUAL;
9379 tape.num_participating_players = 0;
9381 // count number of participating players
9382 for (i = 0; i < MAX_PLAYERS; i++)
9383 if (tape.player_participates[i])
9384 tape.num_participating_players++;
9386 SaveTapeToFilename(filename);
9388 tape.changed = FALSE;
9391 void SaveTape(int nr)
9393 char *filename = getTapeFilename(nr);
9395 InitTapeDirectory(leveldir_current->subdir);
9397 SaveTapeExt(filename);
9400 void SaveScoreTape(int nr)
9402 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9404 // used instead of "leveldir_current->subdir" (for network games)
9405 InitScoreTapeDirectory(levelset.identifier, nr);
9407 SaveTapeExt(filename);
9410 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9411 unsigned int req_state_added)
9413 char *filename = getTapeFilename(nr);
9414 boolean new_tape = !fileExists(filename);
9415 boolean tape_saved = FALSE;
9417 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9422 Request(msg_saved, REQ_CONFIRM | req_state_added);
9430 boolean SaveTapeChecked(int nr)
9432 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9435 boolean SaveTapeChecked_LevelSolved(int nr)
9437 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9438 "Level solved! Tape saved!", REQ_STAY_OPEN);
9441 void DumpTape(struct TapeInfo *tape)
9443 int tape_frame_counter;
9446 if (tape->no_valid_file)
9448 Warn("cannot dump -- no valid tape file found");
9455 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9456 tape->level_nr, tape->file_version, tape->game_version);
9457 Print(" (effective engine version %08d)\n",
9458 tape->engine_version);
9459 Print("Level series identifier: '%s'\n", tape->level_identifier);
9461 Print("Solution tape: %s\n",
9462 tape->solved ? "yes" :
9463 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9465 Print("Special tape properties: ");
9466 if (tape->property_bits == TAPE_PROPERTY_NONE)
9468 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9469 Print("[em_random_bug]");
9470 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9471 Print("[game_speed]");
9472 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9474 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9475 Print("[single_step]");
9476 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9477 Print("[snapshot]");
9478 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9479 Print("[replayed]");
9480 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9481 Print("[tas_keys]");
9482 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9483 Print("[small_graphics]");
9486 int year2 = tape->date / 10000;
9487 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9488 int month_index_raw = (tape->date / 100) % 100;
9489 int month_index = month_index_raw % 12; // prevent invalid index
9490 int month = month_index + 1;
9491 int day = tape->date % 100;
9493 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9497 tape_frame_counter = 0;
9499 for (i = 0; i < tape->length; i++)
9501 if (i >= MAX_TAPE_LEN)
9506 for (j = 0; j < MAX_PLAYERS; j++)
9508 if (tape->player_participates[j])
9510 int action = tape->pos[i].action[j];
9512 Print("%d:%02x ", j, action);
9513 Print("[%c%c%c%c|%c%c] - ",
9514 (action & JOY_LEFT ? '<' : ' '),
9515 (action & JOY_RIGHT ? '>' : ' '),
9516 (action & JOY_UP ? '^' : ' '),
9517 (action & JOY_DOWN ? 'v' : ' '),
9518 (action & JOY_BUTTON_1 ? '1' : ' '),
9519 (action & JOY_BUTTON_2 ? '2' : ' '));
9523 Print("(%03d) ", tape->pos[i].delay);
9524 Print("[%05d]\n", tape_frame_counter);
9526 tape_frame_counter += tape->pos[i].delay;
9532 void DumpTapes(void)
9534 static LevelDirTree *dumptape_leveldir = NULL;
9536 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9537 global.dumptape_leveldir);
9539 if (dumptape_leveldir == NULL)
9540 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9542 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9543 global.dumptape_level_nr > dumptape_leveldir->last_level)
9544 Fail("no such level number: %d", global.dumptape_level_nr);
9546 leveldir_current = dumptape_leveldir;
9548 if (options.mytapes)
9549 LoadTape(global.dumptape_level_nr);
9551 LoadSolutionTape(global.dumptape_level_nr);
9559 // ============================================================================
9560 // score file functions
9561 // ============================================================================
9563 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9567 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9569 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9570 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9571 scores->entry[i].score = 0;
9572 scores->entry[i].time = 0;
9574 scores->entry[i].id = -1;
9575 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9576 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9577 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9578 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9579 strcpy(scores->entry[i].country_code, "??");
9582 scores->num_entries = 0;
9583 scores->last_added = -1;
9584 scores->last_added_local = -1;
9586 scores->updated = FALSE;
9587 scores->uploaded = FALSE;
9588 scores->tape_downloaded = FALSE;
9589 scores->force_last_added = FALSE;
9591 // The following values are intentionally not reset here:
9595 // - continue_playing
9596 // - continue_on_return
9599 static void setScoreInfoToDefaults(void)
9601 setScoreInfoToDefaultsExt(&scores);
9604 static void setServerScoreInfoToDefaults(void)
9606 setScoreInfoToDefaultsExt(&server_scores);
9609 static void LoadScore_OLD(int nr)
9612 char *filename = getScoreFilename(nr);
9613 char cookie[MAX_LINE_LEN];
9614 char line[MAX_LINE_LEN];
9618 if (!(file = fopen(filename, MODE_READ)))
9621 // check file identifier
9622 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9624 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9625 cookie[strlen(cookie) - 1] = '\0';
9627 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9629 Warn("unknown format of score file '%s'", filename);
9636 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9638 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9639 Warn("fscanf() failed; %s", strerror(errno));
9641 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9644 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9645 line[strlen(line) - 1] = '\0';
9647 for (line_ptr = line; *line_ptr; line_ptr++)
9649 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9651 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9652 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9661 static void ConvertScore_OLD(void)
9663 // only convert score to time for levels that rate playing time over score
9664 if (!level.rate_time_over_score)
9667 // convert old score to playing time for score-less levels (like Supaplex)
9668 int time_final_max = 999;
9671 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9673 int score = scores.entry[i].score;
9675 if (score > 0 && score < time_final_max)
9676 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9680 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9682 scores->file_version = getFileVersion(file);
9683 scores->game_version = getFileVersion(file);
9688 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9690 char *level_identifier = NULL;
9691 int level_identifier_size;
9694 level_identifier_size = getFile16BitBE(file);
9696 level_identifier = checked_malloc(level_identifier_size);
9698 for (i = 0; i < level_identifier_size; i++)
9699 level_identifier[i] = getFile8Bit(file);
9701 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9702 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9704 checked_free(level_identifier);
9706 scores->level_nr = getFile16BitBE(file);
9707 scores->num_entries = getFile16BitBE(file);
9709 chunk_size = 2 + level_identifier_size + 2 + 2;
9714 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9718 for (i = 0; i < scores->num_entries; i++)
9720 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9721 scores->entry[i].name[j] = getFile8Bit(file);
9723 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9726 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9731 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9735 for (i = 0; i < scores->num_entries; i++)
9736 scores->entry[i].score = getFile16BitBE(file);
9738 chunk_size = scores->num_entries * 2;
9743 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9747 for (i = 0; i < scores->num_entries; i++)
9748 scores->entry[i].score = getFile32BitBE(file);
9750 chunk_size = scores->num_entries * 4;
9755 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9759 for (i = 0; i < scores->num_entries; i++)
9760 scores->entry[i].time = getFile32BitBE(file);
9762 chunk_size = scores->num_entries * 4;
9767 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9771 for (i = 0; i < scores->num_entries; i++)
9773 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9774 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9776 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9779 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9784 void LoadScore(int nr)
9786 char *filename = getScoreFilename(nr);
9787 char cookie[MAX_LINE_LEN];
9788 char chunk_name[CHUNK_ID_LEN + 1];
9790 boolean old_score_file_format = FALSE;
9793 // always start with reliable default values
9794 setScoreInfoToDefaults();
9796 if (!(file = openFile(filename, MODE_READ)))
9799 getFileChunkBE(file, chunk_name, NULL);
9800 if (strEqual(chunk_name, "RND1"))
9802 getFile32BitBE(file); // not used
9804 getFileChunkBE(file, chunk_name, NULL);
9805 if (!strEqual(chunk_name, "SCOR"))
9807 Warn("unknown format of score file '%s'", filename);
9814 else // check for old file format with cookie string
9816 strcpy(cookie, chunk_name);
9817 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9819 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9820 cookie[strlen(cookie) - 1] = '\0';
9822 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9824 Warn("unknown format of score file '%s'", filename);
9831 old_score_file_format = TRUE;
9834 if (old_score_file_format)
9836 // score files from versions before 4.2.4.0 without chunk structure
9839 // convert score to time, if possible (mainly for Supaplex levels)
9848 int (*loader)(File *, int, struct ScoreInfo *);
9852 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9853 { "INFO", -1, LoadScore_INFO },
9854 { "NAME", -1, LoadScore_NAME },
9855 { "SCOR", -1, LoadScore_SCOR },
9856 { "SC4R", -1, LoadScore_SC4R },
9857 { "TIME", -1, LoadScore_TIME },
9858 { "TAPE", -1, LoadScore_TAPE },
9863 while (getFileChunkBE(file, chunk_name, &chunk_size))
9867 while (chunk_info[i].name != NULL &&
9868 !strEqual(chunk_name, chunk_info[i].name))
9871 if (chunk_info[i].name == NULL)
9873 Warn("unknown chunk '%s' in score file '%s'",
9874 chunk_name, filename);
9876 ReadUnusedBytesFromFile(file, chunk_size);
9878 else if (chunk_info[i].size != -1 &&
9879 chunk_info[i].size != chunk_size)
9881 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9882 chunk_size, chunk_name, filename);
9884 ReadUnusedBytesFromFile(file, chunk_size);
9888 // call function to load this score chunk
9889 int chunk_size_expected =
9890 (chunk_info[i].loader)(file, chunk_size, &scores);
9892 // the size of some chunks cannot be checked before reading other
9893 // chunks first (like "HEAD" and "BODY") that contain some header
9894 // information, so check them here
9895 if (chunk_size_expected != chunk_size)
9897 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9898 chunk_size, chunk_name, filename);
9907 #if ENABLE_HISTORIC_CHUNKS
9908 void SaveScore_OLD(int nr)
9911 char *filename = getScoreFilename(nr);
9914 // used instead of "leveldir_current->subdir" (for network games)
9915 InitScoreDirectory(levelset.identifier);
9917 if (!(file = fopen(filename, MODE_WRITE)))
9919 Warn("cannot save score for level %d", nr);
9924 fprintf(file, "%s\n\n", SCORE_COOKIE);
9926 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9927 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9931 SetFilePermissions(filename, PERMS_PRIVATE);
9935 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9937 putFileVersion(file, scores->file_version);
9938 putFileVersion(file, scores->game_version);
9941 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9943 int level_identifier_size = strlen(scores->level_identifier) + 1;
9946 putFile16BitBE(file, level_identifier_size);
9948 for (i = 0; i < level_identifier_size; i++)
9949 putFile8Bit(file, scores->level_identifier[i]);
9951 putFile16BitBE(file, scores->level_nr);
9952 putFile16BitBE(file, scores->num_entries);
9955 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9959 for (i = 0; i < scores->num_entries; i++)
9961 int name_size = strlen(scores->entry[i].name);
9963 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9964 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9968 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9972 for (i = 0; i < scores->num_entries; i++)
9973 putFile16BitBE(file, scores->entry[i].score);
9976 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9980 for (i = 0; i < scores->num_entries; i++)
9981 putFile32BitBE(file, scores->entry[i].score);
9984 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9988 for (i = 0; i < scores->num_entries; i++)
9989 putFile32BitBE(file, scores->entry[i].time);
9992 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9996 for (i = 0; i < scores->num_entries; i++)
9998 int size = strlen(scores->entry[i].tape_basename);
10000 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10001 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10005 static void SaveScoreToFilename(char *filename)
10008 int info_chunk_size;
10009 int name_chunk_size;
10010 int scor_chunk_size;
10011 int sc4r_chunk_size;
10012 int time_chunk_size;
10013 int tape_chunk_size;
10014 boolean has_large_score_values;
10017 if (!(file = fopen(filename, MODE_WRITE)))
10019 Warn("cannot save score file '%s'", filename);
10024 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10025 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10026 scor_chunk_size = scores.num_entries * 2;
10027 sc4r_chunk_size = scores.num_entries * 4;
10028 time_chunk_size = scores.num_entries * 4;
10029 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10031 has_large_score_values = FALSE;
10032 for (i = 0; i < scores.num_entries; i++)
10033 if (scores.entry[i].score > 0xffff)
10034 has_large_score_values = TRUE;
10036 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10037 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10039 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10040 SaveScore_VERS(file, &scores);
10042 putFileChunkBE(file, "INFO", info_chunk_size);
10043 SaveScore_INFO(file, &scores);
10045 putFileChunkBE(file, "NAME", name_chunk_size);
10046 SaveScore_NAME(file, &scores);
10048 if (has_large_score_values)
10050 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10051 SaveScore_SC4R(file, &scores);
10055 putFileChunkBE(file, "SCOR", scor_chunk_size);
10056 SaveScore_SCOR(file, &scores);
10059 putFileChunkBE(file, "TIME", time_chunk_size);
10060 SaveScore_TIME(file, &scores);
10062 putFileChunkBE(file, "TAPE", tape_chunk_size);
10063 SaveScore_TAPE(file, &scores);
10067 SetFilePermissions(filename, PERMS_PRIVATE);
10070 void SaveScore(int nr)
10072 char *filename = getScoreFilename(nr);
10075 // used instead of "leveldir_current->subdir" (for network games)
10076 InitScoreDirectory(levelset.identifier);
10078 scores.file_version = FILE_VERSION_ACTUAL;
10079 scores.game_version = GAME_VERSION_ACTUAL;
10081 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10082 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10083 scores.level_nr = level_nr;
10085 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10086 if (scores.entry[i].score == 0 &&
10087 scores.entry[i].time == 0 &&
10088 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10091 scores.num_entries = i;
10093 if (scores.num_entries == 0)
10096 SaveScoreToFilename(filename);
10099 static void LoadServerScoreFromCache(int nr)
10101 struct ScoreEntry score_entry;
10110 { &score_entry.score, FALSE, 0 },
10111 { &score_entry.time, FALSE, 0 },
10112 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10113 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10114 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10115 { &score_entry.id, FALSE, 0 },
10116 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10117 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10118 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10119 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10123 char *filename = getScoreCacheFilename(nr);
10124 SetupFileHash *score_hash = loadSetupFileHash(filename);
10127 server_scores.num_entries = 0;
10129 if (score_hash == NULL)
10132 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10134 score_entry = server_scores.entry[i];
10136 for (j = 0; score_mapping[j].value != NULL; j++)
10140 sprintf(token, "%02d.%d", i, j);
10142 char *value = getHashEntry(score_hash, token);
10147 if (score_mapping[j].is_string)
10149 char *score_value = (char *)score_mapping[j].value;
10150 int value_size = score_mapping[j].string_size;
10152 strncpy(score_value, value, value_size);
10153 score_value[value_size] = '\0';
10157 int *score_value = (int *)score_mapping[j].value;
10159 *score_value = atoi(value);
10162 server_scores.num_entries = i + 1;
10165 server_scores.entry[i] = score_entry;
10168 freeSetupFileHash(score_hash);
10171 void LoadServerScore(int nr, boolean download_score)
10173 if (!setup.use_api_server)
10176 // always start with reliable default values
10177 setServerScoreInfoToDefaults();
10179 // 1st step: load server scores from cache file (which may not exist)
10180 // (this should prevent reading it while the thread is writing to it)
10181 LoadServerScoreFromCache(nr);
10183 if (download_score && runtime.use_api_server)
10185 // 2nd step: download server scores from score server to cache file
10186 // (as thread, as it might time out if the server is not reachable)
10187 ApiGetScoreAsThread(nr);
10191 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10193 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10195 // if score tape not uploaded, ask for uploading missing tapes later
10196 if (!setup.has_remaining_tapes)
10197 setup.ask_for_remaining_tapes = TRUE;
10199 setup.provide_uploading_tapes = TRUE;
10200 setup.has_remaining_tapes = TRUE;
10202 SaveSetup_ServerSetup();
10205 void SaveServerScore(int nr, boolean tape_saved)
10207 if (!runtime.use_api_server)
10209 PrepareScoreTapesForUpload(leveldir_current->subdir);
10214 ApiAddScoreAsThread(nr, tape_saved, NULL);
10217 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10218 char *score_tape_filename)
10220 if (!runtime.use_api_server)
10223 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10226 void LoadLocalAndServerScore(int nr, boolean download_score)
10228 int last_added_local = scores.last_added_local;
10229 boolean force_last_added = scores.force_last_added;
10231 // needed if only showing server scores
10232 setScoreInfoToDefaults();
10234 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10237 // restore last added local score entry (before merging server scores)
10238 scores.last_added = scores.last_added_local = last_added_local;
10240 if (setup.use_api_server &&
10241 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10243 // load server scores from cache file and trigger update from server
10244 LoadServerScore(nr, download_score);
10246 // merge local scores with scores from server
10247 MergeServerScore();
10250 if (force_last_added)
10251 scores.force_last_added = force_last_added;
10255 // ============================================================================
10256 // setup file functions
10257 // ============================================================================
10259 #define TOKEN_STR_PLAYER_PREFIX "player_"
10262 static struct TokenInfo global_setup_tokens[] =
10266 &setup.player_name, "player_name"
10270 &setup.multiple_users, "multiple_users"
10274 &setup.sound, "sound"
10278 &setup.sound_loops, "repeating_sound_loops"
10282 &setup.sound_music, "background_music"
10286 &setup.sound_simple, "simple_sound_effects"
10290 &setup.toons, "toons"
10294 &setup.global_animations, "global_animations"
10298 &setup.scroll_delay, "scroll_delay"
10302 &setup.forced_scroll_delay, "forced_scroll_delay"
10306 &setup.scroll_delay_value, "scroll_delay_value"
10310 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10314 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10318 &setup.fade_screens, "fade_screens"
10322 &setup.autorecord, "automatic_tape_recording"
10326 &setup.autorecord_after_replay, "autorecord_after_replay"
10330 &setup.auto_pause_on_start, "auto_pause_on_start"
10334 &setup.show_titlescreen, "show_titlescreen"
10338 &setup.quick_doors, "quick_doors"
10342 &setup.team_mode, "team_mode"
10346 &setup.handicap, "handicap"
10350 &setup.skip_levels, "skip_levels"
10354 &setup.increment_levels, "increment_levels"
10358 &setup.auto_play_next_level, "auto_play_next_level"
10362 &setup.count_score_after_game, "count_score_after_game"
10366 &setup.show_scores_after_game, "show_scores_after_game"
10370 &setup.time_limit, "time_limit"
10374 &setup.fullscreen, "fullscreen"
10378 &setup.window_scaling_percent, "window_scaling_percent"
10382 &setup.window_scaling_quality, "window_scaling_quality"
10386 &setup.screen_rendering_mode, "screen_rendering_mode"
10390 &setup.vsync_mode, "vsync_mode"
10394 &setup.ask_on_escape, "ask_on_escape"
10398 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10402 &setup.ask_on_game_over, "ask_on_game_over"
10406 &setup.ask_on_quit_game, "ask_on_quit_game"
10410 &setup.ask_on_quit_program, "ask_on_quit_program"
10414 &setup.quick_switch, "quick_player_switch"
10418 &setup.input_on_focus, "input_on_focus"
10422 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10426 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10430 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10434 &setup.game_speed_extended, "game_speed_extended"
10438 &setup.game_frame_delay, "game_frame_delay"
10442 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10446 &setup.bd_skip_hatching, "bd_skip_hatching"
10450 &setup.bd_scroll_delay, "bd_scroll_delay"
10454 &setup.bd_smooth_movements, "bd_smooth_movements"
10458 &setup.sp_show_border_elements, "sp_show_border_elements"
10462 &setup.small_game_graphics, "small_game_graphics"
10466 &setup.show_load_save_buttons, "show_load_save_buttons"
10470 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10474 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10478 &setup.graphics_set, "graphics_set"
10482 &setup.sounds_set, "sounds_set"
10486 &setup.music_set, "music_set"
10490 &setup.override_level_graphics, "override_level_graphics"
10494 &setup.override_level_sounds, "override_level_sounds"
10498 &setup.override_level_music, "override_level_music"
10502 &setup.volume_simple, "volume_simple"
10506 &setup.volume_loops, "volume_loops"
10510 &setup.volume_music, "volume_music"
10514 &setup.network_mode, "network_mode"
10518 &setup.network_player_nr, "network_player"
10522 &setup.network_server_hostname, "network_server_hostname"
10526 &setup.touch.control_type, "touch.control_type"
10530 &setup.touch.move_distance, "touch.move_distance"
10534 &setup.touch.drop_distance, "touch.drop_distance"
10538 &setup.touch.transparency, "touch.transparency"
10542 &setup.touch.draw_outlined, "touch.draw_outlined"
10546 &setup.touch.draw_pressed, "touch.draw_pressed"
10550 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10554 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10558 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10562 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10566 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10570 static struct TokenInfo auto_setup_tokens[] =
10574 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10578 static struct TokenInfo server_setup_tokens[] =
10582 &setup.player_uuid, "player_uuid"
10586 &setup.player_version, "player_version"
10590 &setup.use_api_server, TEST_PREFIX "use_api_server"
10594 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10598 &setup.api_server_password, TEST_PREFIX "api_server_password"
10602 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10606 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10610 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10614 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10618 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10622 static struct TokenInfo editor_setup_tokens[] =
10626 &setup.editor.el_classic, "editor.el_classic"
10630 &setup.editor.el_custom, "editor.el_custom"
10634 &setup.editor.el_user_defined, "editor.el_user_defined"
10638 &setup.editor.el_dynamic, "editor.el_dynamic"
10642 &setup.editor.el_headlines, "editor.el_headlines"
10646 &setup.editor.show_element_token, "editor.show_element_token"
10650 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10654 static struct TokenInfo editor_cascade_setup_tokens[] =
10658 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10662 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10666 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10670 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10674 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10678 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10682 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10686 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10690 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10694 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10698 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10702 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10706 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10710 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10714 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10718 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10722 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10726 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10730 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10734 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10738 static struct TokenInfo shortcut_setup_tokens[] =
10742 &setup.shortcut.save_game, "shortcut.save_game"
10746 &setup.shortcut.load_game, "shortcut.load_game"
10750 &setup.shortcut.restart_game, "shortcut.restart_game"
10754 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10758 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10762 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10766 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10770 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10774 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10778 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10782 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10786 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10790 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10794 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10798 &setup.shortcut.tape_record, "shortcut.tape_record"
10802 &setup.shortcut.tape_play, "shortcut.tape_play"
10806 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10810 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10814 &setup.shortcut.sound_music, "shortcut.sound_music"
10818 &setup.shortcut.snap_left, "shortcut.snap_left"
10822 &setup.shortcut.snap_right, "shortcut.snap_right"
10826 &setup.shortcut.snap_up, "shortcut.snap_up"
10830 &setup.shortcut.snap_down, "shortcut.snap_down"
10834 static struct SetupInputInfo setup_input;
10835 static struct TokenInfo player_setup_tokens[] =
10839 &setup_input.use_joystick, ".use_joystick"
10843 &setup_input.joy.device_name, ".joy.device_name"
10847 &setup_input.joy.xleft, ".joy.xleft"
10851 &setup_input.joy.xmiddle, ".joy.xmiddle"
10855 &setup_input.joy.xright, ".joy.xright"
10859 &setup_input.joy.yupper, ".joy.yupper"
10863 &setup_input.joy.ymiddle, ".joy.ymiddle"
10867 &setup_input.joy.ylower, ".joy.ylower"
10871 &setup_input.joy.snap, ".joy.snap_field"
10875 &setup_input.joy.drop, ".joy.place_bomb"
10879 &setup_input.key.left, ".key.move_left"
10883 &setup_input.key.right, ".key.move_right"
10887 &setup_input.key.up, ".key.move_up"
10891 &setup_input.key.down, ".key.move_down"
10895 &setup_input.key.snap, ".key.snap_field"
10899 &setup_input.key.drop, ".key.place_bomb"
10903 static struct TokenInfo system_setup_tokens[] =
10907 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10911 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10915 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10919 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10923 static struct TokenInfo internal_setup_tokens[] =
10927 &setup.internal.program_title, "program_title"
10931 &setup.internal.program_version, "program_version"
10935 &setup.internal.program_author, "program_author"
10939 &setup.internal.program_email, "program_email"
10943 &setup.internal.program_website, "program_website"
10947 &setup.internal.program_copyright, "program_copyright"
10951 &setup.internal.program_company, "program_company"
10955 &setup.internal.program_icon_file, "program_icon_file"
10959 &setup.internal.default_graphics_set, "default_graphics_set"
10963 &setup.internal.default_sounds_set, "default_sounds_set"
10967 &setup.internal.default_music_set, "default_music_set"
10971 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10975 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10979 &setup.internal.fallback_music_file, "fallback_music_file"
10983 &setup.internal.default_level_series, "default_level_series"
10987 &setup.internal.default_window_width, "default_window_width"
10991 &setup.internal.default_window_height, "default_window_height"
10995 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10999 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
11003 &setup.internal.create_user_levelset, "create_user_levelset"
11007 &setup.internal.info_screens_from_main, "info_screens_from_main"
11011 &setup.internal.menu_game, "menu_game"
11015 &setup.internal.menu_engines, "menu_engines"
11019 &setup.internal.menu_editor, "menu_editor"
11023 &setup.internal.menu_graphics, "menu_graphics"
11027 &setup.internal.menu_sound, "menu_sound"
11031 &setup.internal.menu_artwork, "menu_artwork"
11035 &setup.internal.menu_input, "menu_input"
11039 &setup.internal.menu_touch, "menu_touch"
11043 &setup.internal.menu_shortcuts, "menu_shortcuts"
11047 &setup.internal.menu_exit, "menu_exit"
11051 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11055 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11059 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11063 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11067 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11071 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11075 &setup.internal.info_title, "info_title"
11079 &setup.internal.info_elements, "info_elements"
11083 &setup.internal.info_music, "info_music"
11087 &setup.internal.info_credits, "info_credits"
11091 &setup.internal.info_program, "info_program"
11095 &setup.internal.info_version, "info_version"
11099 &setup.internal.info_levelset, "info_levelset"
11103 &setup.internal.info_exit, "info_exit"
11107 static struct TokenInfo debug_setup_tokens[] =
11111 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11115 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11119 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11123 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11127 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11131 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11135 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11139 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11143 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11147 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11151 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11155 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11159 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11163 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11167 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11171 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11175 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11179 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11183 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11187 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11191 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11194 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11198 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11202 &setup.debug.xsn_mode, "debug.xsn_mode"
11206 &setup.debug.xsn_percent, "debug.xsn_percent"
11210 static struct TokenInfo options_setup_tokens[] =
11214 &setup.options.verbose, "options.verbose"
11218 &setup.options.debug, "options.debug"
11222 &setup.options.debug_mode, "options.debug_mode"
11226 static void setSetupInfoToDefaults(struct SetupInfo *si)
11230 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11232 si->multiple_users = TRUE;
11235 si->sound_loops = TRUE;
11236 si->sound_music = TRUE;
11237 si->sound_simple = TRUE;
11239 si->global_animations = TRUE;
11240 si->scroll_delay = TRUE;
11241 si->forced_scroll_delay = FALSE;
11242 si->scroll_delay_value = STD_SCROLL_DELAY;
11243 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11244 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11245 si->fade_screens = TRUE;
11246 si->autorecord = TRUE;
11247 si->autorecord_after_replay = TRUE;
11248 si->auto_pause_on_start = FALSE;
11249 si->show_titlescreen = TRUE;
11250 si->quick_doors = FALSE;
11251 si->team_mode = FALSE;
11252 si->handicap = TRUE;
11253 si->skip_levels = TRUE;
11254 si->increment_levels = TRUE;
11255 si->auto_play_next_level = TRUE;
11256 si->count_score_after_game = TRUE;
11257 si->show_scores_after_game = TRUE;
11258 si->time_limit = TRUE;
11259 si->fullscreen = FALSE;
11260 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11261 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11262 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11263 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11264 si->ask_on_escape = TRUE;
11265 si->ask_on_escape_editor = TRUE;
11266 si->ask_on_game_over = TRUE;
11267 si->ask_on_quit_game = TRUE;
11268 si->ask_on_quit_program = TRUE;
11269 si->quick_switch = FALSE;
11270 si->input_on_focus = FALSE;
11271 si->prefer_aga_graphics = TRUE;
11272 si->prefer_lowpass_sounds = FALSE;
11273 si->prefer_extra_panel_items = TRUE;
11274 si->game_speed_extended = FALSE;
11275 si->game_frame_delay = GAME_FRAME_DELAY;
11276 si->bd_skip_uncovering = FALSE;
11277 si->bd_skip_hatching = FALSE;
11278 si->bd_scroll_delay = TRUE;
11279 si->bd_smooth_movements = AUTO;
11280 si->sp_show_border_elements = FALSE;
11281 si->small_game_graphics = FALSE;
11282 si->show_load_save_buttons = FALSE;
11283 si->show_undo_redo_buttons = FALSE;
11284 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11286 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11287 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11288 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11290 si->override_level_graphics = FALSE;
11291 si->override_level_sounds = FALSE;
11292 si->override_level_music = FALSE;
11294 si->volume_simple = 100; // percent
11295 si->volume_loops = 100; // percent
11296 si->volume_music = 100; // percent
11298 si->network_mode = FALSE;
11299 si->network_player_nr = 0; // first player
11300 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11302 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11303 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11304 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11305 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11306 si->touch.draw_outlined = TRUE;
11307 si->touch.draw_pressed = TRUE;
11309 for (i = 0; i < 2; i++)
11311 char *default_grid_button[6][2] =
11317 { "111222", " vv " },
11318 { "111222", " vv " }
11320 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11321 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11322 int min_xsize = MIN(6, grid_xsize);
11323 int min_ysize = MIN(6, grid_ysize);
11324 int startx = grid_xsize - min_xsize;
11325 int starty = grid_ysize - min_ysize;
11328 // virtual buttons grid can only be set to defaults if video is initialized
11329 // (this will be repeated if virtual buttons are not loaded from setup file)
11330 if (video.initialized)
11332 si->touch.grid_xsize[i] = grid_xsize;
11333 si->touch.grid_ysize[i] = grid_ysize;
11337 si->touch.grid_xsize[i] = -1;
11338 si->touch.grid_ysize[i] = -1;
11341 for (x = 0; x < MAX_GRID_XSIZE; x++)
11342 for (y = 0; y < MAX_GRID_YSIZE; y++)
11343 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11345 for (x = 0; x < min_xsize; x++)
11346 for (y = 0; y < min_ysize; y++)
11347 si->touch.grid_button[i][x][starty + y] =
11348 default_grid_button[y][0][x];
11350 for (x = 0; x < min_xsize; x++)
11351 for (y = 0; y < min_ysize; y++)
11352 si->touch.grid_button[i][startx + x][starty + y] =
11353 default_grid_button[y][1][x];
11356 si->touch.grid_initialized = video.initialized;
11358 si->touch.overlay_buttons = FALSE;
11360 si->editor.el_boulderdash = TRUE;
11361 si->editor.el_boulderdash_native = TRUE;
11362 si->editor.el_boulderdash_effects = TRUE;
11363 si->editor.el_emerald_mine = TRUE;
11364 si->editor.el_emerald_mine_club = TRUE;
11365 si->editor.el_more = TRUE;
11366 si->editor.el_sokoban = TRUE;
11367 si->editor.el_supaplex = TRUE;
11368 si->editor.el_diamond_caves = TRUE;
11369 si->editor.el_dx_boulderdash = TRUE;
11371 si->editor.el_mirror_magic = TRUE;
11372 si->editor.el_deflektor = TRUE;
11374 si->editor.el_chars = TRUE;
11375 si->editor.el_steel_chars = TRUE;
11377 si->editor.el_classic = TRUE;
11378 si->editor.el_custom = TRUE;
11380 si->editor.el_user_defined = FALSE;
11381 si->editor.el_dynamic = TRUE;
11383 si->editor.el_headlines = TRUE;
11385 si->editor.show_element_token = FALSE;
11387 si->editor.show_read_only_warning = TRUE;
11389 si->editor.use_template_for_new_levels = TRUE;
11391 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11392 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11393 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11394 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11395 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11397 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11398 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11399 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11400 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11401 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11403 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11404 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11405 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11406 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11407 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11408 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11410 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11411 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11412 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11414 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11415 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11416 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11417 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11419 for (i = 0; i < MAX_PLAYERS; i++)
11421 si->input[i].use_joystick = FALSE;
11422 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11423 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11424 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11425 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11426 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11427 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11428 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11429 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11430 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11431 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11432 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11433 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11434 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11435 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11436 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11439 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11440 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11441 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11442 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11444 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11445 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11446 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11447 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11448 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11449 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11450 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11452 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11454 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11455 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11456 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11458 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11459 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11460 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11462 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11463 si->internal.choose_from_top_leveldir = FALSE;
11464 si->internal.show_scaling_in_title = TRUE;
11465 si->internal.create_user_levelset = TRUE;
11466 si->internal.info_screens_from_main = FALSE;
11468 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11469 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11471 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11472 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11473 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11474 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11475 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11476 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11477 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11478 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11479 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11480 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11482 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11483 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11484 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11485 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11486 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11487 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11488 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11489 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11490 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11491 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11493 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11494 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11496 si->debug.show_frames_per_second = FALSE;
11498 si->debug.xsn_mode = AUTO;
11499 si->debug.xsn_percent = 0;
11501 si->options.verbose = FALSE;
11502 si->options.debug = FALSE;
11503 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11505 #if defined(PLATFORM_ANDROID)
11506 si->fullscreen = TRUE;
11507 si->touch.overlay_buttons = TRUE;
11510 setHideSetupEntry(&setup.debug.xsn_mode);
11513 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11515 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11518 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11520 si->player_uuid = NULL; // (will be set later)
11521 si->player_version = 1; // (will be set later)
11523 si->use_api_server = TRUE;
11524 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11525 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11526 si->ask_for_uploading_tapes = TRUE;
11527 si->ask_for_remaining_tapes = FALSE;
11528 si->provide_uploading_tapes = TRUE;
11529 si->ask_for_using_api_server = TRUE;
11530 si->has_remaining_tapes = FALSE;
11533 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11535 si->editor_cascade.el_bd = TRUE;
11536 si->editor_cascade.el_bd_native = TRUE;
11537 si->editor_cascade.el_bd_effects = FALSE;
11538 si->editor_cascade.el_em = TRUE;
11539 si->editor_cascade.el_emc = TRUE;
11540 si->editor_cascade.el_rnd = TRUE;
11541 si->editor_cascade.el_sb = TRUE;
11542 si->editor_cascade.el_sp = TRUE;
11543 si->editor_cascade.el_dc = TRUE;
11544 si->editor_cascade.el_dx = TRUE;
11546 si->editor_cascade.el_mm = TRUE;
11547 si->editor_cascade.el_df = TRUE;
11549 si->editor_cascade.el_chars = FALSE;
11550 si->editor_cascade.el_steel_chars = FALSE;
11551 si->editor_cascade.el_ce = FALSE;
11552 si->editor_cascade.el_ge = FALSE;
11553 si->editor_cascade.el_es = FALSE;
11554 si->editor_cascade.el_ref = FALSE;
11555 si->editor_cascade.el_user = FALSE;
11556 si->editor_cascade.el_dynamic = FALSE;
11559 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11561 static char *getHideSetupToken(void *setup_value)
11563 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11565 if (setup_value != NULL)
11566 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11568 return hide_setup_token;
11571 void setHideSetupEntry(void *setup_value)
11573 char *hide_setup_token = getHideSetupToken(setup_value);
11575 if (hide_setup_hash == NULL)
11576 hide_setup_hash = newSetupFileHash();
11578 if (setup_value != NULL)
11579 setHashEntry(hide_setup_hash, hide_setup_token, "");
11582 void removeHideSetupEntry(void *setup_value)
11584 char *hide_setup_token = getHideSetupToken(setup_value);
11586 if (setup_value != NULL)
11587 removeHashEntry(hide_setup_hash, hide_setup_token);
11590 boolean hideSetupEntry(void *setup_value)
11592 char *hide_setup_token = getHideSetupToken(setup_value);
11594 return (setup_value != NULL &&
11595 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11598 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11599 struct TokenInfo *token_info,
11600 int token_nr, char *token_text)
11602 char *token_hide_text = getStringCat2(token_text, ".hide");
11603 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11605 // set the value of this setup option in the setup option structure
11606 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11608 // check if this setup option should be hidden in the setup menu
11609 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11610 setHideSetupEntry(token_info[token_nr].value);
11612 free(token_hide_text);
11615 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11616 struct TokenInfo *token_info,
11619 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11620 token_info[token_nr].text);
11623 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11627 if (!setup_file_hash)
11630 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11631 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11633 setup.touch.grid_initialized = TRUE;
11634 for (i = 0; i < 2; i++)
11636 int grid_xsize = setup.touch.grid_xsize[i];
11637 int grid_ysize = setup.touch.grid_ysize[i];
11640 // if virtual buttons are not loaded from setup file, repeat initializing
11641 // virtual buttons grid with default values later when video is initialized
11642 if (grid_xsize == -1 ||
11645 setup.touch.grid_initialized = FALSE;
11650 for (y = 0; y < grid_ysize; y++)
11652 char token_string[MAX_LINE_LEN];
11654 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11656 char *value_string = getHashEntry(setup_file_hash, token_string);
11658 if (value_string == NULL)
11661 for (x = 0; x < grid_xsize; x++)
11663 char c = value_string[x];
11665 setup.touch.grid_button[i][x][y] =
11666 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11671 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11672 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11674 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11675 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11677 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11681 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11683 setup_input = setup.input[pnr];
11684 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11686 char full_token[100];
11688 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11689 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11692 setup.input[pnr] = setup_input;
11695 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11696 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11698 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11699 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11701 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11702 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11704 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11705 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11707 setHideRelatedSetupEntries();
11710 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11714 if (!setup_file_hash)
11717 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11718 setSetupInfo(auto_setup_tokens, i,
11719 getHashEntry(setup_file_hash,
11720 auto_setup_tokens[i].text));
11723 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11727 if (!setup_file_hash)
11730 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11731 setSetupInfo(server_setup_tokens, i,
11732 getHashEntry(setup_file_hash,
11733 server_setup_tokens[i].text));
11736 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11740 if (!setup_file_hash)
11743 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11744 setSetupInfo(editor_cascade_setup_tokens, i,
11745 getHashEntry(setup_file_hash,
11746 editor_cascade_setup_tokens[i].text));
11749 void LoadUserNames(void)
11751 int last_user_nr = user.nr;
11754 if (global.user_names != NULL)
11756 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11757 checked_free(global.user_names[i]);
11759 checked_free(global.user_names);
11762 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11764 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11768 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11770 if (setup_file_hash)
11772 char *player_name = getHashEntry(setup_file_hash, "player_name");
11774 global.user_names[i] = getFixedUserName(player_name);
11776 freeSetupFileHash(setup_file_hash);
11779 if (global.user_names[i] == NULL)
11780 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11783 user.nr = last_user_nr;
11786 void LoadSetupFromFilename(char *filename)
11788 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11790 if (setup_file_hash)
11792 decodeSetupFileHash_Default(setup_file_hash);
11794 freeSetupFileHash(setup_file_hash);
11798 Debug("setup", "using default setup values");
11802 static void LoadSetup_SpecialPostProcessing(void)
11804 char *player_name_new;
11806 // needed to work around problems with fixed length strings
11807 player_name_new = getFixedUserName(setup.player_name);
11808 free(setup.player_name);
11809 setup.player_name = player_name_new;
11811 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11812 if (setup.scroll_delay == FALSE)
11814 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11815 setup.scroll_delay = TRUE; // now always "on"
11818 // make sure that scroll delay value stays inside valid range
11819 setup.scroll_delay_value =
11820 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11823 void LoadSetup_Default(void)
11827 // always start with reliable default values
11828 setSetupInfoToDefaults(&setup);
11830 // try to load setup values from default setup file
11831 filename = getDefaultSetupFilename();
11833 if (fileExists(filename))
11834 LoadSetupFromFilename(filename);
11836 // try to load setup values from platform setup file
11837 filename = getPlatformSetupFilename();
11839 if (fileExists(filename))
11840 LoadSetupFromFilename(filename);
11842 // try to load setup values from user setup file
11843 filename = getSetupFilename();
11845 LoadSetupFromFilename(filename);
11847 LoadSetup_SpecialPostProcessing();
11850 void LoadSetup_AutoSetup(void)
11852 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11853 SetupFileHash *setup_file_hash = NULL;
11855 // always start with reliable default values
11856 setSetupInfoToDefaults_AutoSetup(&setup);
11858 setup_file_hash = loadSetupFileHash(filename);
11860 if (setup_file_hash)
11862 decodeSetupFileHash_AutoSetup(setup_file_hash);
11864 freeSetupFileHash(setup_file_hash);
11870 void LoadSetup_ServerSetup(void)
11872 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11873 SetupFileHash *setup_file_hash = NULL;
11875 // always start with reliable default values
11876 setSetupInfoToDefaults_ServerSetup(&setup);
11878 setup_file_hash = loadSetupFileHash(filename);
11880 if (setup_file_hash)
11882 decodeSetupFileHash_ServerSetup(setup_file_hash);
11884 freeSetupFileHash(setup_file_hash);
11889 if (setup.player_uuid == NULL)
11891 // player UUID does not yet exist in setup file
11892 setup.player_uuid = getStringCopy(getUUID());
11893 setup.player_version = 2;
11895 SaveSetup_ServerSetup();
11899 void LoadSetup_EditorCascade(void)
11901 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11902 SetupFileHash *setup_file_hash = NULL;
11904 // always start with reliable default values
11905 setSetupInfoToDefaults_EditorCascade(&setup);
11907 setup_file_hash = loadSetupFileHash(filename);
11909 if (setup_file_hash)
11911 decodeSetupFileHash_EditorCascade(setup_file_hash);
11913 freeSetupFileHash(setup_file_hash);
11919 void LoadSetup(void)
11921 LoadSetup_Default();
11922 LoadSetup_AutoSetup();
11923 LoadSetup_ServerSetup();
11924 LoadSetup_EditorCascade();
11927 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11928 char *mapping_line)
11930 char mapping_guid[MAX_LINE_LEN];
11931 char *mapping_start, *mapping_end;
11933 // get GUID from game controller mapping line: copy complete line
11934 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11935 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11937 // get GUID from game controller mapping line: cut after GUID part
11938 mapping_start = strchr(mapping_guid, ',');
11939 if (mapping_start != NULL)
11940 *mapping_start = '\0';
11942 // cut newline from game controller mapping line
11943 mapping_end = strchr(mapping_line, '\n');
11944 if (mapping_end != NULL)
11945 *mapping_end = '\0';
11947 // add mapping entry to game controller mappings hash
11948 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11951 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11956 if (!(file = fopen(filename, MODE_READ)))
11958 Warn("cannot read game controller mappings file '%s'", filename);
11963 while (!feof(file))
11965 char line[MAX_LINE_LEN];
11967 if (!fgets(line, MAX_LINE_LEN, file))
11970 addGameControllerMappingToHash(mappings_hash, line);
11976 void SaveSetup_Default(void)
11978 char *filename = getSetupFilename();
11982 InitUserDataDirectory();
11984 if (!(file = fopen(filename, MODE_WRITE)))
11986 Warn("cannot write setup file '%s'", filename);
11991 fprintFileHeader(file, SETUP_FILENAME);
11993 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11995 // just to make things nicer :)
11996 if (global_setup_tokens[i].value == &setup.multiple_users ||
11997 global_setup_tokens[i].value == &setup.sound ||
11998 global_setup_tokens[i].value == &setup.graphics_set ||
11999 global_setup_tokens[i].value == &setup.volume_simple ||
12000 global_setup_tokens[i].value == &setup.network_mode ||
12001 global_setup_tokens[i].value == &setup.touch.control_type ||
12002 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
12003 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12004 fprintf(file, "\n");
12006 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12009 for (i = 0; i < 2; i++)
12011 int grid_xsize = setup.touch.grid_xsize[i];
12012 int grid_ysize = setup.touch.grid_ysize[i];
12015 fprintf(file, "\n");
12017 for (y = 0; y < grid_ysize; y++)
12019 char token_string[MAX_LINE_LEN];
12020 char value_string[MAX_LINE_LEN];
12022 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12024 for (x = 0; x < grid_xsize; x++)
12026 char c = setup.touch.grid_button[i][x][y];
12028 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12031 value_string[grid_xsize] = '\0';
12033 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12037 fprintf(file, "\n");
12038 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12039 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12041 fprintf(file, "\n");
12042 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12043 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12045 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12049 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12050 fprintf(file, "\n");
12052 setup_input = setup.input[pnr];
12053 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12054 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12057 fprintf(file, "\n");
12058 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12059 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12061 // (internal setup values not saved to user setup file)
12063 fprintf(file, "\n");
12064 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12065 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12066 setup.debug.xsn_mode != AUTO)
12067 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12069 fprintf(file, "\n");
12070 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12071 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12075 SetFilePermissions(filename, PERMS_PRIVATE);
12078 void SaveSetup_AutoSetup(void)
12080 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12084 InitUserDataDirectory();
12086 if (!(file = fopen(filename, MODE_WRITE)))
12088 Warn("cannot write auto setup file '%s'", filename);
12095 fprintFileHeader(file, AUTOSETUP_FILENAME);
12097 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12098 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12102 SetFilePermissions(filename, PERMS_PRIVATE);
12107 void SaveSetup_ServerSetup(void)
12109 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12113 InitUserDataDirectory();
12115 if (!(file = fopen(filename, MODE_WRITE)))
12117 Warn("cannot write server setup file '%s'", filename);
12124 fprintFileHeader(file, SERVERSETUP_FILENAME);
12126 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12128 // just to make things nicer :)
12129 if (server_setup_tokens[i].value == &setup.use_api_server)
12130 fprintf(file, "\n");
12132 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12137 SetFilePermissions(filename, PERMS_PRIVATE);
12142 void SaveSetup_EditorCascade(void)
12144 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12148 InitUserDataDirectory();
12150 if (!(file = fopen(filename, MODE_WRITE)))
12152 Warn("cannot write editor cascade state file '%s'", filename);
12159 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12161 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12162 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12166 SetFilePermissions(filename, PERMS_PRIVATE);
12171 void SaveSetup(void)
12173 SaveSetup_Default();
12174 SaveSetup_AutoSetup();
12175 SaveSetup_ServerSetup();
12176 SaveSetup_EditorCascade();
12179 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12184 if (!(file = fopen(filename, MODE_WRITE)))
12186 Warn("cannot write game controller mappings file '%s'", filename);
12191 BEGIN_HASH_ITERATION(mappings_hash, itr)
12193 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12195 END_HASH_ITERATION(mappings_hash, itr)
12200 void SaveSetup_AddGameControllerMapping(char *mapping)
12202 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12203 SetupFileHash *mappings_hash = newSetupFileHash();
12205 InitUserDataDirectory();
12207 // load existing personal game controller mappings
12208 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12210 // add new mapping to personal game controller mappings
12211 addGameControllerMappingToHash(mappings_hash, mapping);
12213 // save updated personal game controller mappings
12214 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12216 freeSetupFileHash(mappings_hash);
12220 void LoadCustomElementDescriptions(void)
12222 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12223 SetupFileHash *setup_file_hash;
12226 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12228 if (element_info[i].custom_description != NULL)
12230 free(element_info[i].custom_description);
12231 element_info[i].custom_description = NULL;
12235 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12238 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12240 char *token = getStringCat2(element_info[i].token_name, ".name");
12241 char *value = getHashEntry(setup_file_hash, token);
12244 element_info[i].custom_description = getStringCopy(value);
12249 freeSetupFileHash(setup_file_hash);
12252 static int getElementFromToken(char *token)
12254 char *value = getHashEntry(element_token_hash, token);
12257 return atoi(value);
12259 Warn("unknown element token '%s'", token);
12261 return EL_UNDEFINED;
12264 void FreeGlobalAnimEventInfo(void)
12266 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12268 if (gaei->event_list == NULL)
12273 for (i = 0; i < gaei->num_event_lists; i++)
12275 checked_free(gaei->event_list[i]->event_value);
12276 checked_free(gaei->event_list[i]);
12279 checked_free(gaei->event_list);
12281 gaei->event_list = NULL;
12282 gaei->num_event_lists = 0;
12285 static int AddGlobalAnimEventList(void)
12287 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12288 int list_pos = gaei->num_event_lists++;
12290 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12291 sizeof(struct GlobalAnimEventListInfo *));
12293 gaei->event_list[list_pos] =
12294 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12296 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12298 gaeli->event_value = NULL;
12299 gaeli->num_event_values = 0;
12304 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12306 // do not add empty global animation events
12307 if (event_value == ANIM_EVENT_NONE)
12310 // if list position is undefined, create new list
12311 if (list_pos == ANIM_EVENT_UNDEFINED)
12312 list_pos = AddGlobalAnimEventList();
12314 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12315 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12316 int value_pos = gaeli->num_event_values++;
12318 gaeli->event_value = checked_realloc(gaeli->event_value,
12319 gaeli->num_event_values * sizeof(int *));
12321 gaeli->event_value[value_pos] = event_value;
12326 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12328 if (list_pos == ANIM_EVENT_UNDEFINED)
12329 return ANIM_EVENT_NONE;
12331 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12332 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12334 return gaeli->event_value[value_pos];
12337 int GetGlobalAnimEventValueCount(int list_pos)
12339 if (list_pos == ANIM_EVENT_UNDEFINED)
12342 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12343 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12345 return gaeli->num_event_values;
12348 // This function checks if a string <s> of the format "string1, string2, ..."
12349 // exactly contains a string <s_contained>.
12351 static boolean string_has_parameter(char *s, char *s_contained)
12355 if (s == NULL || s_contained == NULL)
12358 if (strlen(s_contained) > strlen(s))
12361 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12363 char next_char = s[strlen(s_contained)];
12365 // check if next character is delimiter or whitespace
12366 if (next_char == ',' || next_char == '\0' ||
12367 next_char == ' ' || next_char == '\t')
12371 // check if string contains another parameter string after a comma
12372 substring = strchr(s, ',');
12373 if (substring == NULL) // string does not contain a comma
12376 // advance string pointer to next character after the comma
12379 // skip potential whitespaces after the comma
12380 while (*substring == ' ' || *substring == '\t')
12383 return string_has_parameter(substring, s_contained);
12386 static int get_anim_parameter_value_ce(char *s)
12389 char *pattern_1 = "ce_change:custom_";
12390 char *pattern_2 = ".page_";
12391 int pattern_1_len = strlen(pattern_1);
12392 char *matching_char = strstr(s_ptr, pattern_1);
12393 int result = ANIM_EVENT_NONE;
12395 if (matching_char == NULL)
12396 return ANIM_EVENT_NONE;
12398 result = ANIM_EVENT_CE_CHANGE;
12400 s_ptr = matching_char + pattern_1_len;
12402 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12403 if (*s_ptr >= '0' && *s_ptr <= '9')
12405 int gic_ce_nr = (*s_ptr++ - '0');
12407 if (*s_ptr >= '0' && *s_ptr <= '9')
12409 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12411 if (*s_ptr >= '0' && *s_ptr <= '9')
12412 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12415 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12416 return ANIM_EVENT_NONE;
12418 // custom element stored as 0 to 255
12421 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12425 // invalid custom element number specified
12427 return ANIM_EVENT_NONE;
12430 // check for change page number ("page_X" or "page_XX") (optional)
12431 if (strPrefix(s_ptr, pattern_2))
12433 s_ptr += strlen(pattern_2);
12435 if (*s_ptr >= '0' && *s_ptr <= '9')
12437 int gic_page_nr = (*s_ptr++ - '0');
12439 if (*s_ptr >= '0' && *s_ptr <= '9')
12440 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12442 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12443 return ANIM_EVENT_NONE;
12445 // change page stored as 1 to 32 (0 means "all change pages")
12447 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12451 // invalid animation part number specified
12453 return ANIM_EVENT_NONE;
12457 // discard result if next character is neither delimiter nor whitespace
12458 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12459 *s_ptr == ' ' || *s_ptr == '\t'))
12460 return ANIM_EVENT_NONE;
12465 static int get_anim_parameter_value(char *s)
12467 int event_value[] =
12475 char *pattern_1[] =
12483 char *pattern_2 = ".part_";
12484 char *matching_char = NULL;
12486 int pattern_1_len = 0;
12487 int result = ANIM_EVENT_NONE;
12490 result = get_anim_parameter_value_ce(s);
12492 if (result != ANIM_EVENT_NONE)
12495 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12497 matching_char = strstr(s_ptr, pattern_1[i]);
12498 pattern_1_len = strlen(pattern_1[i]);
12499 result = event_value[i];
12501 if (matching_char != NULL)
12505 if (matching_char == NULL)
12506 return ANIM_EVENT_NONE;
12508 s_ptr = matching_char + pattern_1_len;
12510 // check for main animation number ("anim_X" or "anim_XX")
12511 if (*s_ptr >= '0' && *s_ptr <= '9')
12513 int gic_anim_nr = (*s_ptr++ - '0');
12515 if (*s_ptr >= '0' && *s_ptr <= '9')
12516 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12518 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12519 return ANIM_EVENT_NONE;
12521 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12525 // invalid main animation number specified
12527 return ANIM_EVENT_NONE;
12530 // check for animation part number ("part_X" or "part_XX") (optional)
12531 if (strPrefix(s_ptr, pattern_2))
12533 s_ptr += strlen(pattern_2);
12535 if (*s_ptr >= '0' && *s_ptr <= '9')
12537 int gic_part_nr = (*s_ptr++ - '0');
12539 if (*s_ptr >= '0' && *s_ptr <= '9')
12540 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12542 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12543 return ANIM_EVENT_NONE;
12545 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12549 // invalid animation part number specified
12551 return ANIM_EVENT_NONE;
12555 // discard result if next character is neither delimiter nor whitespace
12556 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12557 *s_ptr == ' ' || *s_ptr == '\t'))
12558 return ANIM_EVENT_NONE;
12563 static int get_anim_parameter_values(char *s)
12565 int list_pos = ANIM_EVENT_UNDEFINED;
12566 int event_value = ANIM_EVENT_DEFAULT;
12568 if (string_has_parameter(s, "any"))
12569 event_value |= ANIM_EVENT_ANY;
12571 if (string_has_parameter(s, "click:self") ||
12572 string_has_parameter(s, "click") ||
12573 string_has_parameter(s, "self"))
12574 event_value |= ANIM_EVENT_SELF;
12576 if (string_has_parameter(s, "unclick:any"))
12577 event_value |= ANIM_EVENT_UNCLICK_ANY;
12579 // if animation event found, add it to global animation event list
12580 if (event_value != ANIM_EVENT_NONE)
12581 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12585 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12586 event_value = get_anim_parameter_value(s);
12588 // if animation event found, add it to global animation event list
12589 if (event_value != ANIM_EVENT_NONE)
12590 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12592 // continue with next part of the string, starting with next comma
12593 s = strchr(s + 1, ',');
12599 static int get_anim_action_parameter_value(char *token)
12601 // check most common default case first to massively speed things up
12602 if (strEqual(token, ARG_UNDEFINED))
12603 return ANIM_EVENT_ACTION_NONE;
12605 int result = getImageIDFromToken(token);
12609 char *gfx_token = getStringCat2("gfx.", token);
12611 result = getImageIDFromToken(gfx_token);
12613 checked_free(gfx_token);
12618 Key key = getKeyFromX11KeyName(token);
12620 if (key != KSYM_UNDEFINED)
12621 result = -(int)key;
12628 result = get_hash_from_string(token); // unsigned int => int
12629 result = ABS(result); // may be negative now
12630 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12632 setHashEntry(anim_url_hash, int2str(result, 0), token);
12637 result = ANIM_EVENT_ACTION_NONE;
12642 int get_parameter_value(char *value_raw, char *suffix, int type)
12644 char *value = getStringToLower(value_raw);
12645 int result = 0; // probably a save default value
12647 if (strEqual(suffix, ".direction"))
12649 result = (strEqual(value, "left") ? MV_LEFT :
12650 strEqual(value, "right") ? MV_RIGHT :
12651 strEqual(value, "up") ? MV_UP :
12652 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12654 else if (strEqual(suffix, ".position"))
12656 result = (strEqual(value, "left") ? POS_LEFT :
12657 strEqual(value, "right") ? POS_RIGHT :
12658 strEqual(value, "top") ? POS_TOP :
12659 strEqual(value, "upper") ? POS_UPPER :
12660 strEqual(value, "middle") ? POS_MIDDLE :
12661 strEqual(value, "lower") ? POS_LOWER :
12662 strEqual(value, "bottom") ? POS_BOTTOM :
12663 strEqual(value, "any") ? POS_ANY :
12664 strEqual(value, "ce") ? POS_CE :
12665 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12666 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12668 else if (strEqual(suffix, ".align"))
12670 result = (strEqual(value, "left") ? ALIGN_LEFT :
12671 strEqual(value, "right") ? ALIGN_RIGHT :
12672 strEqual(value, "center") ? ALIGN_CENTER :
12673 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12675 else if (strEqual(suffix, ".valign"))
12677 result = (strEqual(value, "top") ? VALIGN_TOP :
12678 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12679 strEqual(value, "middle") ? VALIGN_MIDDLE :
12680 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12682 else if (strEqual(suffix, ".anim_mode"))
12684 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12685 string_has_parameter(value, "loop") ? ANIM_LOOP :
12686 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12687 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12688 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12689 string_has_parameter(value, "random") ? ANIM_RANDOM :
12690 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12691 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12692 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12693 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12694 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12695 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12696 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12697 string_has_parameter(value, "all") ? ANIM_ALL :
12698 string_has_parameter(value, "tiled") ? ANIM_TILED :
12699 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12702 if (string_has_parameter(value, "once"))
12703 result |= ANIM_ONCE;
12705 if (string_has_parameter(value, "reverse"))
12706 result |= ANIM_REVERSE;
12708 if (string_has_parameter(value, "opaque_player"))
12709 result |= ANIM_OPAQUE_PLAYER;
12711 if (string_has_parameter(value, "static_panel"))
12712 result |= ANIM_STATIC_PANEL;
12714 else if (strEqual(suffix, ".init_event") ||
12715 strEqual(suffix, ".anim_event"))
12717 result = get_anim_parameter_values(value);
12719 else if (strEqual(suffix, ".init_delay_action") ||
12720 strEqual(suffix, ".anim_delay_action") ||
12721 strEqual(suffix, ".post_delay_action") ||
12722 strEqual(suffix, ".init_event_action") ||
12723 strEqual(suffix, ".anim_event_action"))
12725 result = get_anim_action_parameter_value(value_raw);
12727 else if (strEqual(suffix, ".class"))
12729 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12730 get_hash_from_string(value));
12732 else if (strEqual(suffix, ".style"))
12734 result = STYLE_DEFAULT;
12736 if (string_has_parameter(value, "accurate_borders"))
12737 result |= STYLE_ACCURATE_BORDERS;
12739 if (string_has_parameter(value, "inner_corners"))
12740 result |= STYLE_INNER_CORNERS;
12742 if (string_has_parameter(value, "reverse"))
12743 result |= STYLE_REVERSE;
12745 if (string_has_parameter(value, "leftmost_position"))
12746 result |= STYLE_LEFTMOST_POSITION;
12748 if (string_has_parameter(value, "block_clicks"))
12749 result |= STYLE_BLOCK;
12751 if (string_has_parameter(value, "passthrough_clicks"))
12752 result |= STYLE_PASSTHROUGH;
12754 if (string_has_parameter(value, "multiple_actions"))
12755 result |= STYLE_MULTIPLE_ACTIONS;
12757 if (string_has_parameter(value, "consume_ce_event"))
12758 result |= STYLE_CONSUME_CE_EVENT;
12760 else if (strEqual(suffix, ".fade_mode"))
12762 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12763 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12764 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12765 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12766 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12767 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12768 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12769 FADE_MODE_DEFAULT);
12771 else if (strEqual(suffix, ".auto_delay_unit"))
12773 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12774 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12775 AUTO_DELAY_UNIT_DEFAULT);
12777 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12779 result = gfx.get_font_from_token_function(value);
12781 else // generic parameter of type integer or boolean
12783 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12784 type == TYPE_INTEGER ? get_integer_from_string(value) :
12785 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12786 ARG_UNDEFINED_VALUE);
12794 static int get_token_parameter_value(char *token, char *value_raw)
12798 if (token == NULL || value_raw == NULL)
12799 return ARG_UNDEFINED_VALUE;
12801 suffix = strrchr(token, '.');
12802 if (suffix == NULL)
12805 if (strEqual(suffix, ".element"))
12806 return getElementFromToken(value_raw);
12808 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12809 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12812 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12813 boolean ignore_defaults)
12817 for (i = 0; image_config_vars[i].token != NULL; i++)
12819 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12821 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12822 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12826 *image_config_vars[i].value =
12827 get_token_parameter_value(image_config_vars[i].token, value);
12831 void InitMenuDesignSettings_Static(void)
12833 // always start with reliable default values from static default config
12834 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12837 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12841 // the following initializes hierarchical values from static configuration
12843 // special case: initialize "ARG_DEFAULT" values in static default config
12844 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12845 titlescreen_initial_first_default.fade_mode =
12846 title_initial_first_default.fade_mode;
12847 titlescreen_initial_first_default.fade_delay =
12848 title_initial_first_default.fade_delay;
12849 titlescreen_initial_first_default.post_delay =
12850 title_initial_first_default.post_delay;
12851 titlescreen_initial_first_default.auto_delay =
12852 title_initial_first_default.auto_delay;
12853 titlescreen_initial_first_default.auto_delay_unit =
12854 title_initial_first_default.auto_delay_unit;
12855 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12856 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12857 titlescreen_first_default.post_delay = title_first_default.post_delay;
12858 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12859 titlescreen_first_default.auto_delay_unit =
12860 title_first_default.auto_delay_unit;
12861 titlemessage_initial_first_default.fade_mode =
12862 title_initial_first_default.fade_mode;
12863 titlemessage_initial_first_default.fade_delay =
12864 title_initial_first_default.fade_delay;
12865 titlemessage_initial_first_default.post_delay =
12866 title_initial_first_default.post_delay;
12867 titlemessage_initial_first_default.auto_delay =
12868 title_initial_first_default.auto_delay;
12869 titlemessage_initial_first_default.auto_delay_unit =
12870 title_initial_first_default.auto_delay_unit;
12871 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12872 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12873 titlemessage_first_default.post_delay = title_first_default.post_delay;
12874 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12875 titlemessage_first_default.auto_delay_unit =
12876 title_first_default.auto_delay_unit;
12878 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12879 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12880 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12881 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12882 titlescreen_initial_default.auto_delay_unit =
12883 title_initial_default.auto_delay_unit;
12884 titlescreen_default.fade_mode = title_default.fade_mode;
12885 titlescreen_default.fade_delay = title_default.fade_delay;
12886 titlescreen_default.post_delay = title_default.post_delay;
12887 titlescreen_default.auto_delay = title_default.auto_delay;
12888 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12889 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12890 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12891 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12892 titlemessage_initial_default.auto_delay_unit =
12893 title_initial_default.auto_delay_unit;
12894 titlemessage_default.fade_mode = title_default.fade_mode;
12895 titlemessage_default.fade_delay = title_default.fade_delay;
12896 titlemessage_default.post_delay = title_default.post_delay;
12897 titlemessage_default.auto_delay = title_default.auto_delay;
12898 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12900 // special case: initialize "ARG_DEFAULT" values in static default config
12901 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12902 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12904 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12905 titlescreen_first[i] = titlescreen_first_default;
12906 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12907 titlemessage_first[i] = titlemessage_first_default;
12909 titlescreen_initial[i] = titlescreen_initial_default;
12910 titlescreen[i] = titlescreen_default;
12911 titlemessage_initial[i] = titlemessage_initial_default;
12912 titlemessage[i] = titlemessage_default;
12915 // special case: initialize "ARG_DEFAULT" values in static default config
12916 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12917 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12919 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12922 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12923 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12924 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12927 // special case: initialize "ARG_DEFAULT" values in static default config
12928 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12929 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12931 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12932 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12933 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12935 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12938 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12942 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12946 struct XY *dst, *src;
12948 game_buttons_xy[] =
12950 { &game.button.save, &game.button.stop },
12951 { &game.button.pause2, &game.button.pause },
12952 { &game.button.load, &game.button.play },
12953 { &game.button.undo, &game.button.stop },
12954 { &game.button.redo, &game.button.play },
12960 // special case: initialize later added SETUP list size from LEVELS value
12961 if (menu.list_size[GAME_MODE_SETUP] == -1)
12962 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12964 // set default position for snapshot buttons to stop/pause/play buttons
12965 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12966 if ((*game_buttons_xy[i].dst).x == -1 &&
12967 (*game_buttons_xy[i].dst).y == -1)
12968 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12970 // --------------------------------------------------------------------------
12971 // dynamic viewports (including playfield margins, borders and alignments)
12972 // --------------------------------------------------------------------------
12974 // dynamic viewports currently only supported for landscape mode
12975 int display_width = MAX(video.display_width, video.display_height);
12976 int display_height = MIN(video.display_width, video.display_height);
12978 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12980 struct RectWithBorder *vp_window = &viewport.window[i];
12981 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12982 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12983 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12984 boolean dynamic_window_width = (vp_window->min_width != -1);
12985 boolean dynamic_window_height = (vp_window->min_height != -1);
12986 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12987 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12989 // adjust window size if min/max width/height is specified
12991 if (vp_window->min_width != -1)
12993 int window_width = display_width;
12995 // when using static window height, use aspect ratio of display
12996 if (vp_window->min_height == -1)
12997 window_width = vp_window->height * display_width / display_height;
12999 vp_window->width = MAX(vp_window->min_width, window_width);
13002 if (vp_window->min_height != -1)
13004 int window_height = display_height;
13006 // when using static window width, use aspect ratio of display
13007 if (vp_window->min_width == -1)
13008 window_height = vp_window->width * display_height / display_width;
13010 vp_window->height = MAX(vp_window->min_height, window_height);
13013 if (vp_window->max_width != -1)
13014 vp_window->width = MIN(vp_window->width, vp_window->max_width);
13016 if (vp_window->max_height != -1)
13017 vp_window->height = MIN(vp_window->height, vp_window->max_height);
13019 int playfield_width = vp_window->width;
13020 int playfield_height = vp_window->height;
13022 // adjust playfield size and position according to specified margins
13024 playfield_width -= vp_playfield->margin_left;
13025 playfield_width -= vp_playfield->margin_right;
13027 playfield_height -= vp_playfield->margin_top;
13028 playfield_height -= vp_playfield->margin_bottom;
13030 // adjust playfield size if min/max width/height is specified
13032 if (vp_playfield->min_width != -1)
13033 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13035 if (vp_playfield->min_height != -1)
13036 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13038 if (vp_playfield->max_width != -1)
13039 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13041 if (vp_playfield->max_height != -1)
13042 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13044 // adjust playfield position according to specified alignment
13046 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13047 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13048 else if (vp_playfield->align == ALIGN_CENTER)
13049 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13050 else if (vp_playfield->align == ALIGN_RIGHT)
13051 vp_playfield->x += playfield_width - vp_playfield->width;
13053 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13054 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13055 else if (vp_playfield->valign == VALIGN_MIDDLE)
13056 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13057 else if (vp_playfield->valign == VALIGN_BOTTOM)
13058 vp_playfield->y += playfield_height - vp_playfield->height;
13060 vp_playfield->x += vp_playfield->margin_left;
13061 vp_playfield->y += vp_playfield->margin_top;
13063 // adjust individual playfield borders if only default border is specified
13065 if (vp_playfield->border_left == -1)
13066 vp_playfield->border_left = vp_playfield->border_size;
13067 if (vp_playfield->border_right == -1)
13068 vp_playfield->border_right = vp_playfield->border_size;
13069 if (vp_playfield->border_top == -1)
13070 vp_playfield->border_top = vp_playfield->border_size;
13071 if (vp_playfield->border_bottom == -1)
13072 vp_playfield->border_bottom = vp_playfield->border_size;
13074 // set dynamic playfield borders if borders are specified as undefined
13075 // (but only if window size was dynamic and playfield size was static)
13077 if (dynamic_window_width && !dynamic_playfield_width)
13079 if (vp_playfield->border_left == -1)
13081 vp_playfield->border_left = (vp_playfield->x -
13082 vp_playfield->margin_left);
13083 vp_playfield->x -= vp_playfield->border_left;
13084 vp_playfield->width += vp_playfield->border_left;
13087 if (vp_playfield->border_right == -1)
13089 vp_playfield->border_right = (vp_window->width -
13091 vp_playfield->width -
13092 vp_playfield->margin_right);
13093 vp_playfield->width += vp_playfield->border_right;
13097 if (dynamic_window_height && !dynamic_playfield_height)
13099 if (vp_playfield->border_top == -1)
13101 vp_playfield->border_top = (vp_playfield->y -
13102 vp_playfield->margin_top);
13103 vp_playfield->y -= vp_playfield->border_top;
13104 vp_playfield->height += vp_playfield->border_top;
13107 if (vp_playfield->border_bottom == -1)
13109 vp_playfield->border_bottom = (vp_window->height -
13111 vp_playfield->height -
13112 vp_playfield->margin_bottom);
13113 vp_playfield->height += vp_playfield->border_bottom;
13117 // adjust playfield size to be a multiple of a defined alignment tile size
13119 int align_size = vp_playfield->align_size;
13120 int playfield_xtiles = vp_playfield->width / align_size;
13121 int playfield_ytiles = vp_playfield->height / align_size;
13122 int playfield_width_corrected = playfield_xtiles * align_size;
13123 int playfield_height_corrected = playfield_ytiles * align_size;
13124 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13125 i == GFX_SPECIAL_ARG_EDITOR);
13127 if (is_playfield_mode &&
13128 dynamic_playfield_width &&
13129 vp_playfield->width != playfield_width_corrected)
13131 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13133 vp_playfield->width = playfield_width_corrected;
13135 if (vp_playfield->align == ALIGN_LEFT)
13137 vp_playfield->border_left += playfield_xdiff;
13139 else if (vp_playfield->align == ALIGN_RIGHT)
13141 vp_playfield->border_right += playfield_xdiff;
13143 else if (vp_playfield->align == ALIGN_CENTER)
13145 int border_left_diff = playfield_xdiff / 2;
13146 int border_right_diff = playfield_xdiff - border_left_diff;
13148 vp_playfield->border_left += border_left_diff;
13149 vp_playfield->border_right += border_right_diff;
13153 if (is_playfield_mode &&
13154 dynamic_playfield_height &&
13155 vp_playfield->height != playfield_height_corrected)
13157 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13159 vp_playfield->height = playfield_height_corrected;
13161 if (vp_playfield->valign == VALIGN_TOP)
13163 vp_playfield->border_top += playfield_ydiff;
13165 else if (vp_playfield->align == VALIGN_BOTTOM)
13167 vp_playfield->border_right += playfield_ydiff;
13169 else if (vp_playfield->align == VALIGN_MIDDLE)
13171 int border_top_diff = playfield_ydiff / 2;
13172 int border_bottom_diff = playfield_ydiff - border_top_diff;
13174 vp_playfield->border_top += border_top_diff;
13175 vp_playfield->border_bottom += border_bottom_diff;
13179 // adjust door positions according to specified alignment
13181 for (j = 0; j < 2; j++)
13183 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13185 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13186 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13187 else if (vp_door->align == ALIGN_CENTER)
13188 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13189 else if (vp_door->align == ALIGN_RIGHT)
13190 vp_door->x += vp_window->width - vp_door->width;
13192 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13193 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13194 else if (vp_door->valign == VALIGN_MIDDLE)
13195 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13196 else if (vp_door->valign == VALIGN_BOTTOM)
13197 vp_door->y += vp_window->height - vp_door->height;
13202 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13206 struct XYTileSize *dst, *src;
13209 editor_buttons_xy[] =
13212 &editor.button.element_left, &editor.palette.element_left,
13213 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13216 &editor.button.element_middle, &editor.palette.element_middle,
13217 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13220 &editor.button.element_right, &editor.palette.element_right,
13221 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13228 // set default position for element buttons to element graphics
13229 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13231 if ((*editor_buttons_xy[i].dst).x == -1 &&
13232 (*editor_buttons_xy[i].dst).y == -1)
13234 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13236 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13238 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13242 // adjust editor palette rows and columns if specified to be dynamic
13244 if (editor.palette.cols == -1)
13246 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13247 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13248 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13250 editor.palette.cols = (vp_width - sc_width) / bt_width;
13252 if (editor.palette.x == -1)
13254 int palette_width = editor.palette.cols * bt_width + sc_width;
13256 editor.palette.x = (vp_width - palette_width) / 2;
13260 if (editor.palette.rows == -1)
13262 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13263 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13264 int tx_height = getFontHeight(FONT_TEXT_2);
13266 editor.palette.rows = (vp_height - tx_height) / bt_height;
13268 if (editor.palette.y == -1)
13270 int palette_height = editor.palette.rows * bt_height + tx_height;
13272 editor.palette.y = (vp_height - palette_height) / 2;
13277 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13278 boolean initialize)
13280 // special case: check if network and preview player positions are redefined,
13281 // to compare this later against the main menu level preview being redefined
13282 struct TokenIntPtrInfo menu_config_players[] =
13284 { "main.network_players.x", &menu.main.network_players.redefined },
13285 { "main.network_players.y", &menu.main.network_players.redefined },
13286 { "main.preview_players.x", &menu.main.preview_players.redefined },
13287 { "main.preview_players.y", &menu.main.preview_players.redefined },
13288 { "preview.x", &preview.redefined },
13289 { "preview.y", &preview.redefined }
13295 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13296 *menu_config_players[i].value = FALSE;
13300 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13301 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13302 *menu_config_players[i].value = TRUE;
13306 static void InitMenuDesignSettings_PreviewPlayers(void)
13308 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13311 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13313 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13316 static void LoadMenuDesignSettingsFromFilename(char *filename)
13318 static struct TitleFadingInfo tfi;
13319 static struct TitleMessageInfo tmi;
13320 static struct TokenInfo title_tokens[] =
13322 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13323 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13324 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13325 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13326 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13330 static struct TokenInfo titlemessage_tokens[] =
13332 { TYPE_INTEGER, &tmi.x, ".x" },
13333 { TYPE_INTEGER, &tmi.y, ".y" },
13334 { TYPE_INTEGER, &tmi.width, ".width" },
13335 { TYPE_INTEGER, &tmi.height, ".height" },
13336 { TYPE_INTEGER, &tmi.chars, ".chars" },
13337 { TYPE_INTEGER, &tmi.lines, ".lines" },
13338 { TYPE_INTEGER, &tmi.align, ".align" },
13339 { TYPE_INTEGER, &tmi.valign, ".valign" },
13340 { TYPE_INTEGER, &tmi.font, ".font" },
13341 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13342 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13343 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13344 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13345 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13346 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13347 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13348 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13349 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13355 struct TitleFadingInfo *info;
13360 // initialize first titles from "enter screen" definitions, if defined
13361 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13362 { &title_first_default, "menu.enter_screen.TITLE" },
13364 // initialize title screens from "next screen" definitions, if defined
13365 { &title_initial_default, "menu.next_screen.TITLE" },
13366 { &title_default, "menu.next_screen.TITLE" },
13372 struct TitleMessageInfo *array;
13375 titlemessage_arrays[] =
13377 // initialize first titles from "enter screen" definitions, if defined
13378 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13379 { titlescreen_first, "menu.enter_screen.TITLE" },
13380 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13381 { titlemessage_first, "menu.enter_screen.TITLE" },
13383 // initialize titles from "next screen" definitions, if defined
13384 { titlescreen_initial, "menu.next_screen.TITLE" },
13385 { titlescreen, "menu.next_screen.TITLE" },
13386 { titlemessage_initial, "menu.next_screen.TITLE" },
13387 { titlemessage, "menu.next_screen.TITLE" },
13389 // overwrite titles with title definitions, if defined
13390 { titlescreen_initial_first, "[title_initial]" },
13391 { titlescreen_first, "[title]" },
13392 { titlemessage_initial_first, "[title_initial]" },
13393 { titlemessage_first, "[title]" },
13395 { titlescreen_initial, "[title_initial]" },
13396 { titlescreen, "[title]" },
13397 { titlemessage_initial, "[title_initial]" },
13398 { titlemessage, "[title]" },
13400 // overwrite titles with title screen/message definitions, if defined
13401 { titlescreen_initial_first, "[titlescreen_initial]" },
13402 { titlescreen_first, "[titlescreen]" },
13403 { titlemessage_initial_first, "[titlemessage_initial]" },
13404 { titlemessage_first, "[titlemessage]" },
13406 { titlescreen_initial, "[titlescreen_initial]" },
13407 { titlescreen, "[titlescreen]" },
13408 { titlemessage_initial, "[titlemessage_initial]" },
13409 { titlemessage, "[titlemessage]" },
13413 SetupFileHash *setup_file_hash;
13416 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13419 // the following initializes hierarchical values from dynamic configuration
13421 // special case: initialize with default values that may be overwritten
13422 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13423 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13425 struct TokenIntPtrInfo menu_config[] =
13427 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13428 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13429 { "menu.list_size", &menu.list_size[i] }
13432 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13434 char *token = menu_config[j].token;
13435 char *value = getHashEntry(setup_file_hash, token);
13438 *menu_config[j].value = get_integer_from_string(value);
13442 // special case: initialize with default values that may be overwritten
13443 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13444 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13446 struct TokenIntPtrInfo menu_config[] =
13448 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13449 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13450 { "menu.list_size.INFO", &menu.list_size_info[i] },
13451 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13452 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13455 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13457 char *token = menu_config[j].token;
13458 char *value = getHashEntry(setup_file_hash, token);
13461 *menu_config[j].value = get_integer_from_string(value);
13465 // special case: initialize with default values that may be overwritten
13466 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13467 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13469 struct TokenIntPtrInfo menu_config[] =
13471 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13472 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13475 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13477 char *token = menu_config[j].token;
13478 char *value = getHashEntry(setup_file_hash, token);
13481 *menu_config[j].value = get_integer_from_string(value);
13485 // special case: initialize with default values that may be overwritten
13486 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13487 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13489 struct TokenIntPtrInfo menu_config[] =
13491 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13492 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13493 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13494 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13495 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13496 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13497 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13498 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13499 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13500 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13503 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13505 char *token = menu_config[j].token;
13506 char *value = getHashEntry(setup_file_hash, token);
13509 *menu_config[j].value = get_integer_from_string(value);
13513 // special case: initialize with default values that may be overwritten
13514 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13515 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13517 struct TokenIntPtrInfo menu_config[] =
13519 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13520 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13521 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13522 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13523 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13524 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13525 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13526 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13527 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13530 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13532 char *token = menu_config[j].token;
13533 char *value = getHashEntry(setup_file_hash, token);
13536 *menu_config[j].value = get_token_parameter_value(token, value);
13540 // special case: initialize with default values that may be overwritten
13541 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13542 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13546 char *token_prefix;
13547 struct RectWithBorder *struct_ptr;
13551 { "viewport.window", &viewport.window[i] },
13552 { "viewport.playfield", &viewport.playfield[i] },
13553 { "viewport.door_1", &viewport.door_1[i] },
13554 { "viewport.door_2", &viewport.door_2[i] }
13557 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13559 struct TokenIntPtrInfo vp_config[] =
13561 { ".x", &vp_struct[j].struct_ptr->x },
13562 { ".y", &vp_struct[j].struct_ptr->y },
13563 { ".width", &vp_struct[j].struct_ptr->width },
13564 { ".height", &vp_struct[j].struct_ptr->height },
13565 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13566 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13567 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13568 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13569 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13570 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13571 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13572 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13573 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13574 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13575 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13576 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13577 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13578 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13579 { ".align", &vp_struct[j].struct_ptr->align },
13580 { ".valign", &vp_struct[j].struct_ptr->valign }
13583 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13585 char *token = getStringCat2(vp_struct[j].token_prefix,
13586 vp_config[k].token);
13587 char *value = getHashEntry(setup_file_hash, token);
13590 *vp_config[k].value = get_token_parameter_value(token, value);
13597 // special case: initialize with default values that may be overwritten
13598 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13599 for (i = 0; title_info[i].info != NULL; i++)
13601 struct TitleFadingInfo *info = title_info[i].info;
13602 char *base_token = title_info[i].text;
13604 for (j = 0; title_tokens[j].type != -1; j++)
13606 char *token = getStringCat2(base_token, title_tokens[j].text);
13607 char *value = getHashEntry(setup_file_hash, token);
13611 int parameter_value = get_token_parameter_value(token, value);
13615 *(int *)title_tokens[j].value = (int)parameter_value;
13624 // special case: initialize with default values that may be overwritten
13625 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13626 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13628 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13629 char *base_token = titlemessage_arrays[i].text;
13631 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13633 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13634 char *value = getHashEntry(setup_file_hash, token);
13638 int parameter_value = get_token_parameter_value(token, value);
13640 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13644 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13645 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13647 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13657 // read (and overwrite with) values that may be specified in config file
13658 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13660 // special case: check if network and preview player positions are redefined
13661 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13663 freeSetupFileHash(setup_file_hash);
13666 void LoadMenuDesignSettings(void)
13668 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13670 InitMenuDesignSettings_Static();
13671 InitMenuDesignSettings_SpecialPreProcessing();
13672 InitMenuDesignSettings_PreviewPlayers();
13674 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13676 // first look for special settings configured in level series config
13677 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13679 if (fileExists(filename_base))
13680 LoadMenuDesignSettingsFromFilename(filename_base);
13683 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13685 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13686 LoadMenuDesignSettingsFromFilename(filename_local);
13688 InitMenuDesignSettings_SpecialPostProcessing();
13691 void LoadMenuDesignSettings_AfterGraphics(void)
13693 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13696 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13697 boolean ignore_defaults)
13701 for (i = 0; sound_config_vars[i].token != NULL; i++)
13703 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13705 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13706 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13710 *sound_config_vars[i].value =
13711 get_token_parameter_value(sound_config_vars[i].token, value);
13715 void InitSoundSettings_Static(void)
13717 // always start with reliable default values from static default config
13718 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13721 static void LoadSoundSettingsFromFilename(char *filename)
13723 SetupFileHash *setup_file_hash;
13725 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13728 // read (and overwrite with) values that may be specified in config file
13729 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13731 freeSetupFileHash(setup_file_hash);
13734 void LoadSoundSettings(void)
13736 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13738 InitSoundSettings_Static();
13740 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13742 // first look for special settings configured in level series config
13743 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13745 if (fileExists(filename_base))
13746 LoadSoundSettingsFromFilename(filename_base);
13749 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13751 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13752 LoadSoundSettingsFromFilename(filename_local);
13755 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13757 char *filename = getEditorSetupFilename();
13758 SetupFileList *setup_file_list, *list;
13759 SetupFileHash *element_hash;
13760 int num_unknown_tokens = 0;
13763 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13766 element_hash = newSetupFileHash();
13768 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13769 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13771 // determined size may be larger than needed (due to unknown elements)
13773 for (list = setup_file_list; list != NULL; list = list->next)
13776 // add space for up to 3 more elements for padding that may be needed
13777 *num_elements += 3;
13779 // free memory for old list of elements, if needed
13780 checked_free(*elements);
13782 // allocate memory for new list of elements
13783 *elements = checked_malloc(*num_elements * sizeof(int));
13786 for (list = setup_file_list; list != NULL; list = list->next)
13788 char *value = getHashEntry(element_hash, list->token);
13790 if (value == NULL) // try to find obsolete token mapping
13792 char *mapped_token = get_mapped_token(list->token);
13794 if (mapped_token != NULL)
13796 value = getHashEntry(element_hash, mapped_token);
13798 free(mapped_token);
13804 (*elements)[(*num_elements)++] = atoi(value);
13808 if (num_unknown_tokens == 0)
13811 Warn("unknown token(s) found in config file:");
13812 Warn("- config file: '%s'", filename);
13814 num_unknown_tokens++;
13817 Warn("- token: '%s'", list->token);
13821 if (num_unknown_tokens > 0)
13824 while (*num_elements % 4) // pad with empty elements, if needed
13825 (*elements)[(*num_elements)++] = EL_EMPTY;
13827 freeSetupFileList(setup_file_list);
13828 freeSetupFileHash(element_hash);
13831 for (i = 0; i < *num_elements; i++)
13832 Debug("editor", "element '%s' [%d]\n",
13833 element_info[(*elements)[i]].token_name, (*elements)[i]);
13837 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13840 SetupFileHash *setup_file_hash = NULL;
13841 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13842 char *filename_music, *filename_prefix, *filename_info;
13848 token_to_value_ptr[] =
13850 { "title_header", &tmp_music_file_info.title_header },
13851 { "artist_header", &tmp_music_file_info.artist_header },
13852 { "album_header", &tmp_music_file_info.album_header },
13853 { "year_header", &tmp_music_file_info.year_header },
13854 { "played_header", &tmp_music_file_info.played_header },
13856 { "title", &tmp_music_file_info.title },
13857 { "artist", &tmp_music_file_info.artist },
13858 { "album", &tmp_music_file_info.album },
13859 { "year", &tmp_music_file_info.year },
13860 { "played", &tmp_music_file_info.played },
13866 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13867 getCustomMusicFilename(basename));
13869 if (filename_music == NULL)
13872 // ---------- try to replace file extension ----------
13874 filename_prefix = getStringCopy(filename_music);
13875 if (strrchr(filename_prefix, '.') != NULL)
13876 *strrchr(filename_prefix, '.') = '\0';
13877 filename_info = getStringCat2(filename_prefix, ".txt");
13879 if (fileExists(filename_info))
13880 setup_file_hash = loadSetupFileHash(filename_info);
13882 free(filename_prefix);
13883 free(filename_info);
13885 if (setup_file_hash == NULL)
13887 // ---------- try to add file extension ----------
13889 filename_prefix = getStringCopy(filename_music);
13890 filename_info = getStringCat2(filename_prefix, ".txt");
13892 if (fileExists(filename_info))
13893 setup_file_hash = loadSetupFileHash(filename_info);
13895 free(filename_prefix);
13896 free(filename_info);
13899 if (setup_file_hash == NULL)
13902 // ---------- music file info found ----------
13904 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13906 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13908 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13910 *token_to_value_ptr[i].value_ptr =
13911 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13914 tmp_music_file_info.basename = getStringCopy(basename);
13915 tmp_music_file_info.music = music;
13916 tmp_music_file_info.is_sound = is_sound;
13918 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13919 *new_music_file_info = tmp_music_file_info;
13921 return new_music_file_info;
13924 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13926 return get_music_file_info_ext(basename, music, FALSE);
13929 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13931 return get_music_file_info_ext(basename, sound, TRUE);
13934 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13935 char *basename, boolean is_sound)
13937 for (; list != NULL; list = list->next)
13938 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13944 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13946 return music_info_listed_ext(list, basename, FALSE);
13949 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13951 return music_info_listed_ext(list, basename, TRUE);
13954 void LoadMusicInfo(void)
13956 int num_music_noconf = getMusicListSize_NoConf();
13957 int num_music = getMusicListSize();
13958 int num_sounds = getSoundListSize();
13959 struct FileInfo *music, *sound;
13960 struct MusicFileInfo *next, **new;
13964 while (music_file_info != NULL)
13966 next = music_file_info->next;
13968 checked_free(music_file_info->basename);
13970 checked_free(music_file_info->title_header);
13971 checked_free(music_file_info->artist_header);
13972 checked_free(music_file_info->album_header);
13973 checked_free(music_file_info->year_header);
13974 checked_free(music_file_info->played_header);
13976 checked_free(music_file_info->title);
13977 checked_free(music_file_info->artist);
13978 checked_free(music_file_info->album);
13979 checked_free(music_file_info->year);
13980 checked_free(music_file_info->played);
13982 free(music_file_info);
13984 music_file_info = next;
13987 new = &music_file_info;
13989 // get (configured or unconfigured) music file info for all levels
13990 for (i = leveldir_current->first_level;
13991 i <= leveldir_current->last_level; i++)
13995 if (levelset.music[i] != MUS_UNDEFINED)
13997 // get music file info for configured level music
13998 music_nr = levelset.music[i];
14000 else if (num_music_noconf > 0)
14002 // get music file info for unconfigured level music
14003 int level_pos = i - leveldir_current->first_level;
14005 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14012 char *basename = getMusicInfoEntryFilename(music_nr);
14014 if (basename == NULL)
14017 if (!music_info_listed(music_file_info, basename))
14019 *new = get_music_file_info(basename, music_nr);
14022 new = &(*new)->next;
14026 // get music file info for all remaining configured music files
14027 for (i = 0; i < num_music; i++)
14029 music = getMusicListEntry(i);
14031 if (music->filename == NULL)
14034 if (strEqual(music->filename, UNDEFINED_FILENAME))
14037 // a configured file may be not recognized as music
14038 if (!FileIsMusic(music->filename))
14041 if (!music_info_listed(music_file_info, music->filename))
14043 *new = get_music_file_info(music->filename, i);
14046 new = &(*new)->next;
14050 // get sound file info for all configured sound files
14051 for (i = 0; i < num_sounds; i++)
14053 sound = getSoundListEntry(i);
14055 if (sound->filename == NULL)
14058 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14061 // a configured file may be not recognized as sound
14062 if (!FileIsSound(sound->filename))
14065 if (!sound_info_listed(music_file_info, sound->filename))
14067 *new = get_sound_file_info(sound->filename, i);
14069 new = &(*new)->next;
14073 // add pointers to previous list nodes
14075 struct MusicFileInfo *node = music_file_info;
14077 while (node != NULL)
14080 node->next->prev = node;
14086 static void add_helpanim_entry(int element, int action, int direction,
14087 int delay, int *num_list_entries)
14089 struct HelpAnimInfo *new_list_entry;
14090 (*num_list_entries)++;
14093 checked_realloc(helpanim_info,
14094 *num_list_entries * sizeof(struct HelpAnimInfo));
14095 new_list_entry = &helpanim_info[*num_list_entries - 1];
14097 new_list_entry->element = element;
14098 new_list_entry->action = action;
14099 new_list_entry->direction = direction;
14100 new_list_entry->delay = delay;
14103 static void print_unknown_token(char *filename, char *token, int token_nr)
14108 Warn("unknown token(s) found in config file:");
14109 Warn("- config file: '%s'", filename);
14112 Warn("- token: '%s'", token);
14115 static void print_unknown_token_end(int token_nr)
14121 void LoadHelpAnimInfo(void)
14123 char *filename = getHelpAnimFilename();
14124 SetupFileList *setup_file_list = NULL, *list;
14125 SetupFileHash *element_hash, *action_hash, *direction_hash;
14126 int num_list_entries = 0;
14127 int num_unknown_tokens = 0;
14130 if (fileExists(filename))
14131 setup_file_list = loadSetupFileList(filename);
14133 if (setup_file_list == NULL)
14135 // use reliable default values from static configuration
14136 SetupFileList *insert_ptr;
14138 insert_ptr = setup_file_list =
14139 newSetupFileList(helpanim_config[0].token,
14140 helpanim_config[0].value);
14142 for (i = 1; helpanim_config[i].token; i++)
14143 insert_ptr = addListEntry(insert_ptr,
14144 helpanim_config[i].token,
14145 helpanim_config[i].value);
14148 element_hash = newSetupFileHash();
14149 action_hash = newSetupFileHash();
14150 direction_hash = newSetupFileHash();
14152 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14153 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14155 for (i = 0; i < NUM_ACTIONS; i++)
14156 setHashEntry(action_hash, element_action_info[i].suffix,
14157 i_to_a(element_action_info[i].value));
14159 // do not store direction index (bit) here, but direction value!
14160 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14161 setHashEntry(direction_hash, element_direction_info[i].suffix,
14162 i_to_a(1 << element_direction_info[i].value));
14164 for (list = setup_file_list; list != NULL; list = list->next)
14166 char *element_token, *action_token, *direction_token;
14167 char *element_value, *action_value, *direction_value;
14168 int delay = atoi(list->value);
14170 if (strEqual(list->token, "end"))
14172 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14177 /* first try to break element into element/action/direction parts;
14178 if this does not work, also accept combined "element[.act][.dir]"
14179 elements (like "dynamite.active"), which are unique elements */
14181 if (strchr(list->token, '.') == NULL) // token contains no '.'
14183 element_value = getHashEntry(element_hash, list->token);
14184 if (element_value != NULL) // element found
14185 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14186 &num_list_entries);
14189 // no further suffixes found -- this is not an element
14190 print_unknown_token(filename, list->token, num_unknown_tokens++);
14196 // token has format "<prefix>.<something>"
14198 action_token = strchr(list->token, '.'); // suffix may be action ...
14199 direction_token = action_token; // ... or direction
14201 element_token = getStringCopy(list->token);
14202 *strchr(element_token, '.') = '\0';
14204 element_value = getHashEntry(element_hash, element_token);
14206 if (element_value == NULL) // this is no element
14208 element_value = getHashEntry(element_hash, list->token);
14209 if (element_value != NULL) // combined element found
14210 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14211 &num_list_entries);
14213 print_unknown_token(filename, list->token, num_unknown_tokens++);
14215 free(element_token);
14220 action_value = getHashEntry(action_hash, action_token);
14222 if (action_value != NULL) // action found
14224 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14225 &num_list_entries);
14227 free(element_token);
14232 direction_value = getHashEntry(direction_hash, direction_token);
14234 if (direction_value != NULL) // direction found
14236 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14237 &num_list_entries);
14239 free(element_token);
14244 if (strchr(action_token + 1, '.') == NULL)
14246 // no further suffixes found -- this is not an action nor direction
14248 element_value = getHashEntry(element_hash, list->token);
14249 if (element_value != NULL) // combined element found
14250 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14251 &num_list_entries);
14253 print_unknown_token(filename, list->token, num_unknown_tokens++);
14255 free(element_token);
14260 // token has format "<prefix>.<suffix>.<something>"
14262 direction_token = strchr(action_token + 1, '.');
14264 action_token = getStringCopy(action_token);
14265 *strchr(action_token + 1, '.') = '\0';
14267 action_value = getHashEntry(action_hash, action_token);
14269 if (action_value == NULL) // this is no action
14271 element_value = getHashEntry(element_hash, list->token);
14272 if (element_value != NULL) // combined element found
14273 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14274 &num_list_entries);
14276 print_unknown_token(filename, list->token, num_unknown_tokens++);
14278 free(element_token);
14279 free(action_token);
14284 direction_value = getHashEntry(direction_hash, direction_token);
14286 if (direction_value != NULL) // direction found
14288 add_helpanim_entry(atoi(element_value), atoi(action_value),
14289 atoi(direction_value), delay, &num_list_entries);
14291 free(element_token);
14292 free(action_token);
14297 // this is no direction
14299 element_value = getHashEntry(element_hash, list->token);
14300 if (element_value != NULL) // combined element found
14301 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14302 &num_list_entries);
14304 print_unknown_token(filename, list->token, num_unknown_tokens++);
14306 free(element_token);
14307 free(action_token);
14310 print_unknown_token_end(num_unknown_tokens);
14312 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14313 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14315 freeSetupFileList(setup_file_list);
14316 freeSetupFileHash(element_hash);
14317 freeSetupFileHash(action_hash);
14318 freeSetupFileHash(direction_hash);
14321 for (i = 0; i < num_list_entries; i++)
14322 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14323 EL_NAME(helpanim_info[i].element),
14324 helpanim_info[i].element,
14325 helpanim_info[i].action,
14326 helpanim_info[i].direction,
14327 helpanim_info[i].delay);
14331 void LoadHelpTextInfo(void)
14333 char *filename = getHelpTextFilename();
14336 if (helptext_info != NULL)
14338 freeSetupFileHash(helptext_info);
14339 helptext_info = NULL;
14342 if (fileExists(filename))
14343 helptext_info = loadSetupFileHash(filename);
14345 if (helptext_info == NULL)
14347 // use reliable default values from static configuration
14348 helptext_info = newSetupFileHash();
14350 for (i = 0; helptext_config[i].token; i++)
14351 setHashEntry(helptext_info,
14352 helptext_config[i].token,
14353 helptext_config[i].value);
14357 BEGIN_HASH_ITERATION(helptext_info, itr)
14359 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14360 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14362 END_HASH_ITERATION(hash, itr)
14367 // ----------------------------------------------------------------------------
14369 // ----------------------------------------------------------------------------
14371 #define MAX_NUM_CONVERT_LEVELS 1000
14373 void ConvertLevels(void)
14375 static LevelDirTree *convert_leveldir = NULL;
14376 static int convert_level_nr = -1;
14377 static int num_levels_handled = 0;
14378 static int num_levels_converted = 0;
14379 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14382 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14383 global.convert_leveldir);
14385 if (convert_leveldir == NULL)
14386 Fail("no such level identifier: '%s'", global.convert_leveldir);
14388 leveldir_current = convert_leveldir;
14390 if (global.convert_level_nr != -1)
14392 convert_leveldir->first_level = global.convert_level_nr;
14393 convert_leveldir->last_level = global.convert_level_nr;
14396 convert_level_nr = convert_leveldir->first_level;
14398 PrintLine("=", 79);
14399 Print("Converting levels\n");
14400 PrintLine("-", 79);
14401 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14402 Print("Level series name: '%s'\n", convert_leveldir->name);
14403 Print("Level series author: '%s'\n", convert_leveldir->author);
14404 Print("Number of levels: %d\n", convert_leveldir->levels);
14405 PrintLine("=", 79);
14408 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14409 levels_failed[i] = FALSE;
14411 while (convert_level_nr <= convert_leveldir->last_level)
14413 char *level_filename;
14416 level_nr = convert_level_nr++;
14418 Print("Level %03d: ", level_nr);
14420 LoadLevel(level_nr);
14421 if (level.no_level_file || level.no_valid_file)
14423 Print("(no level)\n");
14427 Print("converting level ... ");
14430 // special case: conversion of some EMC levels as requested by ACME
14431 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14434 level_filename = getDefaultLevelFilename(level_nr);
14435 new_level = !fileExists(level_filename);
14439 SaveLevel(level_nr);
14441 num_levels_converted++;
14443 Print("converted.\n");
14447 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14448 levels_failed[level_nr] = TRUE;
14450 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14453 num_levels_handled++;
14457 PrintLine("=", 79);
14458 Print("Number of levels handled: %d\n", num_levels_handled);
14459 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14460 (num_levels_handled ?
14461 num_levels_converted * 100 / num_levels_handled : 0));
14462 PrintLine("-", 79);
14463 Print("Summary (for automatic parsing by scripts):\n");
14464 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14465 convert_leveldir->identifier, num_levels_converted,
14466 num_levels_handled,
14467 (num_levels_handled ?
14468 num_levels_converted * 100 / num_levels_handled : 0));
14470 if (num_levels_handled != num_levels_converted)
14472 Print(", FAILED:");
14473 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14474 if (levels_failed[i])
14479 PrintLine("=", 79);
14481 CloseAllAndExit(0);
14485 // ----------------------------------------------------------------------------
14486 // create and save images for use in level sketches (raw BMP format)
14487 // ----------------------------------------------------------------------------
14489 void CreateLevelSketchImages(void)
14495 InitElementPropertiesGfxElement();
14497 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14498 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14500 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14502 int element = getMappedElement(i);
14503 char basename1[16];
14504 char basename2[16];
14508 sprintf(basename1, "%04d.bmp", i);
14509 sprintf(basename2, "%04ds.bmp", i);
14511 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14512 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14514 DrawSizedElement(0, 0, element, TILESIZE);
14515 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14517 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14518 Fail("cannot save level sketch image file '%s'", filename1);
14520 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14521 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14523 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14524 Fail("cannot save level sketch image file '%s'", filename2);
14529 // create corresponding SQL statements (for normal and small images)
14532 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14533 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14536 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14537 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14539 // optional: create content for forum level sketch demonstration post
14541 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14544 FreeBitmap(bitmap1);
14545 FreeBitmap(bitmap2);
14548 fprintf(stderr, "\n");
14550 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14552 CloseAllAndExit(0);
14556 // ----------------------------------------------------------------------------
14557 // create and save images for element collecting animations (raw BMP format)
14558 // ----------------------------------------------------------------------------
14560 static boolean createCollectImage(int element)
14562 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14565 void CreateCollectElementImages(void)
14569 int anim_frames = num_steps - 1;
14570 int tile_size = TILESIZE;
14571 int anim_width = tile_size * anim_frames;
14572 int anim_height = tile_size;
14573 int num_collect_images = 0;
14574 int pos_collect_images = 0;
14576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14577 if (createCollectImage(i))
14578 num_collect_images++;
14580 Info("Creating %d element collecting animation images ...",
14581 num_collect_images);
14583 int dst_width = anim_width * 2;
14584 int dst_height = anim_height * num_collect_images / 2;
14585 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14586 char *basename_bmp = "RocksCollect.bmp";
14587 char *basename_png = "RocksCollect.png";
14588 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14589 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14590 int len_filename_bmp = strlen(filename_bmp);
14591 int len_filename_png = strlen(filename_png);
14592 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14593 char cmd_convert[max_command_len];
14595 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14599 // force using RGBA surface for destination bitmap
14600 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14601 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14603 dst_bitmap->surface =
14604 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14606 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14608 if (!createCollectImage(i))
14611 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14612 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14613 int graphic = el2img(i);
14614 char *token_name = element_info[i].token_name;
14615 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14616 Bitmap *src_bitmap;
14619 Info("- creating collecting image for '%s' ...", token_name);
14621 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14623 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14624 tile_size, tile_size, 0, 0);
14626 // force using RGBA surface for temporary bitmap (using transparent black)
14627 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14628 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14630 tmp_bitmap->surface =
14631 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14633 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14635 for (j = 0; j < anim_frames; j++)
14637 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14638 int frame_size = frame_size_final * num_steps;
14639 int offset = (tile_size - frame_size_final) / 2;
14640 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14642 while (frame_size > frame_size_final)
14646 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14648 FreeBitmap(frame_bitmap);
14650 frame_bitmap = half_bitmap;
14653 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14654 frame_size_final, frame_size_final,
14655 dst_x + j * tile_size + offset, dst_y + offset);
14657 FreeBitmap(frame_bitmap);
14660 tmp_bitmap->surface_masked = NULL;
14662 FreeBitmap(tmp_bitmap);
14664 pos_collect_images++;
14667 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14668 Fail("cannot save element collecting image file '%s'", filename_bmp);
14670 FreeBitmap(dst_bitmap);
14672 Info("Converting image file from BMP to PNG ...");
14674 if (system(cmd_convert) != 0)
14675 Fail("converting image file failed");
14677 unlink(filename_bmp);
14681 CloseAllAndExit(0);
14685 // ----------------------------------------------------------------------------
14686 // create and save images for custom and group elements (raw BMP format)
14687 // ----------------------------------------------------------------------------
14689 void CreateCustomElementImages(char *directory)
14691 char *src_basename = "RocksCE-template.ilbm";
14692 char *dst_basename = "RocksCE.bmp";
14693 char *src_filename = getPath2(directory, src_basename);
14694 char *dst_filename = getPath2(directory, dst_basename);
14695 Bitmap *src_bitmap;
14697 int yoffset_ce = 0;
14698 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14701 InitVideoDefaults();
14703 ReCreateBitmap(&backbuffer, video.width, video.height);
14705 src_bitmap = LoadImage(src_filename);
14707 bitmap = CreateBitmap(TILEX * 16 * 2,
14708 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14711 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14718 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14719 TILEX * x, TILEY * y + yoffset_ce);
14721 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14723 TILEX * x + TILEX * 16,
14724 TILEY * y + yoffset_ce);
14726 for (j = 2; j >= 0; j--)
14730 BlitBitmap(src_bitmap, bitmap,
14731 TILEX + c * 7, 0, 6, 10,
14732 TILEX * x + 6 + j * 7,
14733 TILEY * y + 11 + yoffset_ce);
14735 BlitBitmap(src_bitmap, bitmap,
14736 TILEX + c * 8, TILEY, 6, 10,
14737 TILEX * 16 + TILEX * x + 6 + j * 8,
14738 TILEY * y + 10 + yoffset_ce);
14744 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14751 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14752 TILEX * x, TILEY * y + yoffset_ge);
14754 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14756 TILEX * x + TILEX * 16,
14757 TILEY * y + yoffset_ge);
14759 for (j = 1; j >= 0; j--)
14763 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14764 TILEX * x + 6 + j * 10,
14765 TILEY * y + 11 + yoffset_ge);
14767 BlitBitmap(src_bitmap, bitmap,
14768 TILEX + c * 8, TILEY + 12, 6, 10,
14769 TILEX * 16 + TILEX * x + 10 + j * 8,
14770 TILEY * y + 10 + yoffset_ge);
14776 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14777 Fail("cannot save CE graphics file '%s'", dst_filename);
14779 FreeBitmap(bitmap);
14781 CloseAllAndExit(0);