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_EMPTY
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
850 // (the following values are related to various game elements)
854 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
855 &li.score[SC_EMERALD], 10
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
861 &li.score[SC_DIAMOND], 10
866 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
867 &li.score[SC_BUG], 10
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 &li.score[SC_SPACESHIP], 10
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 &li.score[SC_PACMAN], 10
884 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
885 &li.score[SC_NUT], 10
890 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
891 &li.score[SC_DYNAMITE], 10
896 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
897 &li.score[SC_KEY], 10
902 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 &li.score[SC_PEARL], 10
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
909 &li.score[SC_CRYSTAL], 10
912 // (amoeba values used by R'n'D game engine only)
915 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
916 &li.amoeba_content, EL_DIAMOND
920 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
925 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
926 &li.grow_into_diggable, TRUE
928 // (amoeba values used by BD game engine only)
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.bd_amoeba_wait_for_hatching, FALSE
936 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
937 &li.bd_amoeba_start_immediately, TRUE
941 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
942 &li.bd_amoeba_2_explode_by_amoeba, TRUE
946 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
947 &li.bd_amoeba_threshold_too_big, 200
951 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
952 &li.bd_amoeba_slow_growth_time, 200
956 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
957 &li.bd_amoeba_slow_growth_rate, 3
961 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
962 &li.bd_amoeba_fast_growth_rate, 25
966 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
967 &li.bd_amoeba_content_too_big, EL_BD_ROCK
971 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
972 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
977 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
978 &li.bd_amoeba_2_threshold_too_big, 200
982 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
983 &li.bd_amoeba_2_slow_growth_time, 200
987 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
988 &li.bd_amoeba_2_slow_growth_rate, 3
992 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
993 &li.bd_amoeba_2_fast_growth_rate, 25
997 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
998 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
1002 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
1003 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
1007 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1008 &li.bd_amoeba_2_content_exploding, EL_EMPTY
1012 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
1013 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1018 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1019 &li.yamyam_content, EL_ROCK, NULL,
1020 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1025 &li.score[SC_YAMYAM], 10
1030 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1031 &li.score[SC_ROBOT], 10
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1041 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1048 &li.time_magic_wall, 10
1052 EL_GAME_OF_LIFE, -1,
1053 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1054 &li.game_of_life[0], 2
1057 EL_GAME_OF_LIFE, -1,
1058 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1059 &li.game_of_life[1], 3
1062 EL_GAME_OF_LIFE, -1,
1063 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1064 &li.game_of_life[2], 3
1067 EL_GAME_OF_LIFE, -1,
1068 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1069 &li.game_of_life[3], 3
1072 EL_GAME_OF_LIFE, -1,
1073 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1074 &li.use_life_bugs, FALSE
1079 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1084 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1089 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1094 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1099 EL_TIMEGATE_SWITCH, -1,
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1101 &li.time_timegate, 10
1105 EL_LIGHT_SWITCH_ACTIVE, -1,
1106 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 EL_SHIELD_NORMAL, -1,
1112 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1113 &li.shield_normal_time, 10
1116 EL_SHIELD_NORMAL, -1,
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1118 &li.score[SC_SHIELD], 10
1122 EL_SHIELD_DEADLY, -1,
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1124 &li.shield_deadly_time, 10
1127 EL_SHIELD_DEADLY, -1,
1128 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1129 &li.score[SC_SHIELD], 10
1134 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1139 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1140 &li.extra_time_score, 10
1144 EL_TIME_ORB_FULL, -1,
1145 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1146 &li.time_orb_time, 10
1149 EL_TIME_ORB_FULL, -1,
1150 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1151 &li.use_time_orb_bug, FALSE
1156 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1157 &li.use_spring_bug, FALSE
1162 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1163 &li.android_move_time, 10
1167 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1168 &li.android_clone_time, 10
1171 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1172 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1173 &li.android_clone_element[0], EL_EMPTY, NULL,
1174 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1178 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1179 &li.android_clone_element[0], EL_EMPTY, NULL,
1180 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1186 &li.lenses_score, 10
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1195 EL_EMC_MAGNIFIER, -1,
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1197 &li.magnify_score, 10
1200 EL_EMC_MAGNIFIER, -1,
1201 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1202 &li.magnify_time, 10
1206 EL_EMC_MAGIC_BALL, -1,
1207 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1211 EL_EMC_MAGIC_BALL, -1,
1212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1213 &li.ball_random, FALSE
1216 EL_EMC_MAGIC_BALL, -1,
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1218 &li.ball_active_initial, FALSE
1221 EL_EMC_MAGIC_BALL, -1,
1222 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1223 &li.ball_content, EL_EMPTY, NULL,
1224 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1228 EL_SOKOBAN_FIELD_EMPTY, -1,
1229 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1230 &li.sb_fields_needed, TRUE
1234 EL_SOKOBAN_OBJECT, -1,
1235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1236 &li.sb_objects_needed, TRUE
1241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1242 &li.mm_laser_red, FALSE
1246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1247 &li.mm_laser_green, FALSE
1251 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1252 &li.mm_laser_blue, TRUE
1257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1258 &li.df_laser_red, TRUE
1262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1263 &li.df_laser_green, TRUE
1267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1268 &li.df_laser_blue, FALSE
1272 EL_MM_FUSE_ACTIVE, -1,
1273 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1274 &li.mm_time_fuse, 25
1278 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1279 &li.mm_time_bomb, 75
1283 EL_MM_GRAY_BALL, -1,
1284 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1285 &li.mm_time_ball, 75
1288 EL_MM_GRAY_BALL, -1,
1289 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1290 &li.mm_ball_choice_mode, ANIM_RANDOM
1293 EL_MM_GRAY_BALL, -1,
1294 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1295 &li.mm_ball_content, EL_EMPTY, NULL,
1296 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1299 EL_MM_GRAY_BALL, -1,
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1301 &li.rotate_mm_ball_content, TRUE
1304 EL_MM_GRAY_BALL, -1,
1305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1306 &li.explode_mm_ball, FALSE
1310 EL_MM_STEEL_BLOCK, -1,
1311 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1312 &li.mm_time_block, 75
1315 EL_MM_LIGHTBALL, -1,
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1317 &li.score[SC_ELEM_BONUS], 10
1327 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1331 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1332 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1336 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1337 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1342 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1343 &xx_envelope.autowrap, FALSE
1347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1348 &xx_envelope.centered, FALSE
1353 TYPE_STRING, CONF_VALUE_BYTES(1),
1354 &xx_envelope.text, -1, NULL,
1355 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1356 &xx_default_string_empty[0]
1366 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1370 TYPE_STRING, CONF_VALUE_BYTES(1),
1371 &xx_ei.description[0], -1,
1372 &yy_ei.description[0],
1373 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1374 &xx_default_description[0]
1379 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1380 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1381 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1383 #if ENABLE_RESERVED_CODE
1384 // (reserved for later use)
1387 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1388 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1389 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1396 &xx_ei.use_gfx_element, FALSE,
1397 &yy_ei.use_gfx_element
1401 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1402 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1403 &yy_ei.gfx_element_initial
1408 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1409 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1410 &yy_ei.access_direction
1415 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1416 &xx_ei.collect_score_initial, 10,
1417 &yy_ei.collect_score_initial
1421 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1422 &xx_ei.collect_count_initial, 1,
1423 &yy_ei.collect_count_initial
1428 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1429 &xx_ei.ce_value_fixed_initial, 0,
1430 &yy_ei.ce_value_fixed_initial
1434 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1435 &xx_ei.ce_value_random_initial, 0,
1436 &yy_ei.ce_value_random_initial
1440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1441 &xx_ei.use_last_ce_value, FALSE,
1442 &yy_ei.use_last_ce_value
1447 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1448 &xx_ei.push_delay_fixed, 8,
1449 &yy_ei.push_delay_fixed
1453 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1454 &xx_ei.push_delay_random, 8,
1455 &yy_ei.push_delay_random
1459 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1460 &xx_ei.drop_delay_fixed, 0,
1461 &yy_ei.drop_delay_fixed
1465 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1466 &xx_ei.drop_delay_random, 0,
1467 &yy_ei.drop_delay_random
1471 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1472 &xx_ei.move_delay_fixed, 0,
1473 &yy_ei.move_delay_fixed
1477 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1478 &xx_ei.move_delay_random, 0,
1479 &yy_ei.move_delay_random
1483 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1484 &xx_ei.step_delay_fixed, 0,
1485 &yy_ei.step_delay_fixed
1489 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1490 &xx_ei.step_delay_random, 0,
1491 &yy_ei.step_delay_random
1496 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1497 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1502 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1503 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1504 &yy_ei.move_direction_initial
1508 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1509 &xx_ei.move_stepsize, TILEX / 8,
1510 &yy_ei.move_stepsize
1515 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1516 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1517 &yy_ei.move_enter_element
1521 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1522 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1523 &yy_ei.move_leave_element
1527 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1528 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1529 &yy_ei.move_leave_type
1534 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1535 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1536 &yy_ei.slippery_type
1541 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1542 &xx_ei.explosion_type, EXPLODES_3X3,
1543 &yy_ei.explosion_type
1547 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1548 &xx_ei.explosion_delay, 16,
1549 &yy_ei.explosion_delay
1553 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1554 &xx_ei.ignition_delay, 8,
1555 &yy_ei.ignition_delay
1560 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1561 &xx_ei.content, EL_EMPTY_SPACE,
1563 &xx_num_contents, 1, 1
1566 // ---------- "num_change_pages" must be the last entry ---------------------
1569 -1, SAVE_CONF_ALWAYS,
1570 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1571 &xx_ei.num_change_pages, 1,
1572 &yy_ei.num_change_pages
1583 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1585 // ---------- "current_change_page" must be the first entry -----------------
1588 -1, SAVE_CONF_ALWAYS,
1589 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1590 &xx_current_change_page, -1
1593 // ---------- (the remaining entries can be in any order) -------------------
1597 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1598 &xx_change.can_change, FALSE
1603 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1604 &xx_event_bits[0], 0
1608 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1609 &xx_event_bits[1], 0
1614 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1615 &xx_change.trigger_player, CH_PLAYER_ANY
1619 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1620 &xx_change.trigger_side, CH_SIDE_ANY
1624 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1625 &xx_change.trigger_page, CH_PAGE_ANY
1630 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1631 &xx_change.target_element, EL_EMPTY_SPACE
1636 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1637 &xx_change.delay_fixed, 0
1641 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1642 &xx_change.delay_random, 0
1646 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1647 &xx_change.delay_frames, FRAMES_PER_SECOND
1652 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1653 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1658 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1659 &xx_change.explode, FALSE
1663 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1664 &xx_change.use_target_content, FALSE
1668 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1669 &xx_change.only_if_complete, FALSE
1673 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1674 &xx_change.use_random_replace, FALSE
1678 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1679 &xx_change.random_percentage, 100
1683 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1684 &xx_change.replace_when, CP_WHEN_EMPTY
1689 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1690 &xx_change.has_action, FALSE
1694 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1695 &xx_change.action_type, CA_NO_ACTION
1699 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1700 &xx_change.action_mode, CA_MODE_UNDEFINED
1704 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1705 &xx_change.action_arg, CA_ARG_UNDEFINED
1710 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1711 &xx_change.action_element, EL_EMPTY_SPACE
1716 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1717 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1718 &xx_num_contents, 1, 1
1728 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1732 TYPE_STRING, CONF_VALUE_BYTES(1),
1733 &xx_ei.description[0], -1, NULL,
1734 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1735 &xx_default_description[0]
1740 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1741 &xx_ei.use_gfx_element, FALSE
1745 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1746 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1751 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1752 &xx_group.choice_mode, ANIM_RANDOM
1757 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1758 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1759 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1769 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1774 &xx_ei.use_gfx_element, FALSE
1778 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1779 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1789 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1793 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1794 &li.block_snap_field, TRUE
1798 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1799 &li.continuous_snapping, TRUE
1803 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1804 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1808 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1809 &li.use_start_element[0], FALSE
1813 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1814 &li.start_element[0], EL_PLAYER_1
1818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1819 &li.use_artwork_element[0], FALSE
1823 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1824 &li.artwork_element[0], EL_PLAYER_1
1828 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1829 &li.use_explosion_element[0], FALSE
1833 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1834 &li.explosion_element[0], EL_PLAYER_1
1849 filetype_id_list[] =
1851 { LEVEL_FILE_TYPE_RND, "RND" },
1852 { LEVEL_FILE_TYPE_BD, "BD" },
1853 { LEVEL_FILE_TYPE_EM, "EM" },
1854 { LEVEL_FILE_TYPE_SP, "SP" },
1855 { LEVEL_FILE_TYPE_DX, "DX" },
1856 { LEVEL_FILE_TYPE_SB, "SB" },
1857 { LEVEL_FILE_TYPE_DC, "DC" },
1858 { LEVEL_FILE_TYPE_MM, "MM" },
1859 { LEVEL_FILE_TYPE_MM, "DF" },
1864 // ============================================================================
1865 // level file functions
1866 // ============================================================================
1868 static boolean check_special_flags(char *flag)
1870 if (strEqual(options.special_flags, flag) ||
1871 strEqual(leveldir_current->special_flags, flag))
1877 static struct DateInfo getCurrentDate(void)
1879 time_t epoch_seconds = time(NULL);
1880 struct tm *now = localtime(&epoch_seconds);
1881 struct DateInfo date;
1883 date.year = now->tm_year + 1900;
1884 date.month = now->tm_mon + 1;
1885 date.day = now->tm_mday;
1887 date.src = DATE_SRC_CLOCK;
1892 static void resetEventFlags(struct ElementChangeInfo *change)
1896 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1897 change->has_event[i] = FALSE;
1900 static void resetEventBits(void)
1904 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1905 xx_event_bits[i] = 0;
1908 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1912 /* important: only change event flag if corresponding event bit is set
1913 (this is because all xx_event_bits[] values are loaded separately,
1914 and all xx_event_bits[] values are set back to zero before loading
1915 another value xx_event_bits[x] (each value representing 32 flags)) */
1917 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1918 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1919 change->has_event[i] = TRUE;
1922 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1926 /* in contrast to the above function setEventFlagsFromEventBits(), it
1927 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1928 depending on the corresponding change->has_event[i] values here, as
1929 all xx_event_bits[] values are reset in resetEventBits() before */
1931 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1932 if (change->has_event[i])
1933 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1936 static char *getDefaultElementDescription(struct ElementInfo *ei)
1938 static char description[MAX_ELEMENT_NAME_LEN + 1];
1939 char *default_description = (ei->custom_description != NULL ?
1940 ei->custom_description :
1941 ei->editor_description);
1944 // always start with reliable default values
1945 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1946 description[i] = '\0';
1948 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1949 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1951 return &description[0];
1954 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1956 char *default_description = getDefaultElementDescription(ei);
1959 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1960 ei->description[i] = default_description[i];
1963 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1967 for (i = 0; conf[i].data_type != -1; i++)
1969 int default_value = conf[i].default_value;
1970 int data_type = conf[i].data_type;
1971 int conf_type = conf[i].conf_type;
1972 int byte_mask = conf_type & CONF_MASK_BYTES;
1974 if (byte_mask == CONF_MASK_MULTI_BYTES)
1976 int default_num_entities = conf[i].default_num_entities;
1977 int max_num_entities = conf[i].max_num_entities;
1979 *(int *)(conf[i].num_entities) = default_num_entities;
1981 if (data_type == TYPE_STRING)
1983 char *default_string = conf[i].default_string;
1984 char *string = (char *)(conf[i].value);
1986 strncpy(string, default_string, max_num_entities);
1988 else if (data_type == TYPE_ELEMENT_LIST)
1990 int *element_array = (int *)(conf[i].value);
1993 for (j = 0; j < max_num_entities; j++)
1994 element_array[j] = default_value;
1996 else if (data_type == TYPE_CONTENT_LIST)
1998 struct Content *content = (struct Content *)(conf[i].value);
2001 for (c = 0; c < max_num_entities; c++)
2002 for (y = 0; y < 3; y++)
2003 for (x = 0; x < 3; x++)
2004 content[c].e[x][y] = default_value;
2007 else // constant size configuration data (1, 2 or 4 bytes)
2009 if (data_type == TYPE_BOOLEAN)
2010 *(boolean *)(conf[i].value) = default_value;
2012 *(int *) (conf[i].value) = default_value;
2017 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2021 for (i = 0; conf[i].data_type != -1; i++)
2023 int data_type = conf[i].data_type;
2024 int conf_type = conf[i].conf_type;
2025 int byte_mask = conf_type & CONF_MASK_BYTES;
2027 if (byte_mask == CONF_MASK_MULTI_BYTES)
2029 int max_num_entities = conf[i].max_num_entities;
2031 if (data_type == TYPE_STRING)
2033 char *string = (char *)(conf[i].value);
2034 char *string_copy = (char *)(conf[i].value_copy);
2036 strncpy(string_copy, string, max_num_entities);
2038 else if (data_type == TYPE_ELEMENT_LIST)
2040 int *element_array = (int *)(conf[i].value);
2041 int *element_array_copy = (int *)(conf[i].value_copy);
2044 for (j = 0; j < max_num_entities; j++)
2045 element_array_copy[j] = element_array[j];
2047 else if (data_type == TYPE_CONTENT_LIST)
2049 struct Content *content = (struct Content *)(conf[i].value);
2050 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2053 for (c = 0; c < max_num_entities; c++)
2054 for (y = 0; y < 3; y++)
2055 for (x = 0; x < 3; x++)
2056 content_copy[c].e[x][y] = content[c].e[x][y];
2059 else // constant size configuration data (1, 2 or 4 bytes)
2061 if (data_type == TYPE_BOOLEAN)
2062 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2064 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2069 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2073 xx_ei = *ei_from; // copy element data into temporary buffer
2074 yy_ei = *ei_to; // copy element data into temporary buffer
2076 copyConfigFromConfigList(chunk_config_CUSX_base);
2081 // ---------- reinitialize and copy change pages ----------
2083 ei_to->num_change_pages = ei_from->num_change_pages;
2084 ei_to->current_change_page = ei_from->current_change_page;
2086 setElementChangePages(ei_to, ei_to->num_change_pages);
2088 for (i = 0; i < ei_to->num_change_pages; i++)
2089 ei_to->change_page[i] = ei_from->change_page[i];
2091 // ---------- copy group element info ----------
2092 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2093 *ei_to->group = *ei_from->group;
2095 // mark this custom element as modified
2096 ei_to->modified_settings = TRUE;
2099 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2101 int change_page_size = sizeof(struct ElementChangeInfo);
2103 ei->num_change_pages = MAX(1, change_pages);
2106 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2108 if (ei->current_change_page >= ei->num_change_pages)
2109 ei->current_change_page = ei->num_change_pages - 1;
2111 ei->change = &ei->change_page[ei->current_change_page];
2114 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2116 xx_change = *change; // copy change data into temporary buffer
2118 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2120 *change = xx_change;
2122 resetEventFlags(change);
2124 change->direct_action = 0;
2125 change->other_action = 0;
2127 change->pre_change_function = NULL;
2128 change->change_function = NULL;
2129 change->post_change_function = NULL;
2132 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2136 li = *level; // copy level data into temporary buffer
2137 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2138 *level = li; // copy temporary buffer back to level data
2140 setLevelInfoToDefaults_BD();
2141 setLevelInfoToDefaults_EM();
2142 setLevelInfoToDefaults_SP();
2143 setLevelInfoToDefaults_MM();
2145 level->native_bd_level = &native_bd_level;
2146 level->native_em_level = &native_em_level;
2147 level->native_sp_level = &native_sp_level;
2148 level->native_mm_level = &native_mm_level;
2150 level->file_version = FILE_VERSION_ACTUAL;
2151 level->game_version = GAME_VERSION_ACTUAL;
2153 level->creation_date = getCurrentDate();
2155 level->encoding_16bit_field = TRUE;
2156 level->encoding_16bit_yamyam = TRUE;
2157 level->encoding_16bit_amoeba = TRUE;
2159 // clear level name and level author string buffers
2160 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2161 level->name[i] = '\0';
2162 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2163 level->author[i] = '\0';
2165 // set level name and level author to default values
2166 strcpy(level->name, NAMELESS_LEVEL_NAME);
2167 strcpy(level->author, ANONYMOUS_NAME);
2169 // set level playfield to playable default level with player and exit
2170 for (x = 0; x < MAX_LEV_FIELDX; x++)
2171 for (y = 0; y < MAX_LEV_FIELDY; y++)
2172 level->field[x][y] = EL_SAND;
2174 level->field[0][0] = EL_PLAYER_1;
2175 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2177 BorderElement = EL_STEELWALL;
2179 // detect custom elements when loading them
2180 level->file_has_custom_elements = FALSE;
2182 // set all bug compatibility flags to "false" => do not emulate this bug
2183 level->use_action_after_change_bug = FALSE;
2185 if (leveldir_current)
2187 // try to determine better author name than 'anonymous'
2188 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2190 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2191 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2195 switch (LEVELCLASS(leveldir_current))
2197 case LEVELCLASS_TUTORIAL:
2198 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2201 case LEVELCLASS_CONTRIB:
2202 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2203 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2206 case LEVELCLASS_PRIVATE:
2207 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2208 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2212 // keep default value
2219 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2221 static boolean clipboard_elements_initialized = FALSE;
2224 InitElementPropertiesStatic();
2226 li = *level; // copy level data into temporary buffer
2227 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2228 *level = li; // copy temporary buffer back to level data
2230 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2233 struct ElementInfo *ei = &element_info[element];
2235 if (element == EL_MM_GRAY_BALL)
2237 struct LevelInfo_MM *level_mm = level->native_mm_level;
2240 for (j = 0; j < level->num_mm_ball_contents; j++)
2241 level->mm_ball_content[j] =
2242 map_element_MM_to_RND(level_mm->ball_content[j]);
2245 // never initialize clipboard elements after the very first time
2246 // (to be able to use clipboard elements between several levels)
2247 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2250 if (IS_ENVELOPE(element))
2252 int envelope_nr = element - EL_ENVELOPE_1;
2254 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2256 level->envelope[envelope_nr] = xx_envelope;
2259 if (IS_CUSTOM_ELEMENT(element) ||
2260 IS_GROUP_ELEMENT(element) ||
2261 IS_INTERNAL_ELEMENT(element))
2263 xx_ei = *ei; // copy element data into temporary buffer
2265 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2270 setElementChangePages(ei, 1);
2271 setElementChangeInfoToDefaults(ei->change);
2273 if (IS_CUSTOM_ELEMENT(element) ||
2274 IS_GROUP_ELEMENT(element))
2276 setElementDescriptionToDefault(ei);
2278 ei->modified_settings = FALSE;
2281 if (IS_CUSTOM_ELEMENT(element) ||
2282 IS_INTERNAL_ELEMENT(element))
2284 // internal values used in level editor
2286 ei->access_type = 0;
2287 ei->access_layer = 0;
2288 ei->access_protected = 0;
2289 ei->walk_to_action = 0;
2290 ei->smash_targets = 0;
2293 ei->can_explode_by_fire = FALSE;
2294 ei->can_explode_smashed = FALSE;
2295 ei->can_explode_impact = FALSE;
2297 ei->current_change_page = 0;
2300 if (IS_GROUP_ELEMENT(element) ||
2301 IS_INTERNAL_ELEMENT(element))
2303 struct ElementGroupInfo *group;
2305 // initialize memory for list of elements in group
2306 if (ei->group == NULL)
2307 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2311 xx_group = *group; // copy group data into temporary buffer
2313 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2318 if (IS_EMPTY_ELEMENT(element) ||
2319 IS_INTERNAL_ELEMENT(element))
2321 xx_ei = *ei; // copy element data into temporary buffer
2323 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2329 clipboard_elements_initialized = TRUE;
2332 static void setLevelInfoToDefaults(struct LevelInfo *level,
2333 boolean level_info_only,
2334 boolean reset_file_status)
2336 setLevelInfoToDefaults_Level(level);
2338 if (!level_info_only)
2339 setLevelInfoToDefaults_Elements(level);
2341 if (reset_file_status)
2343 level->no_valid_file = FALSE;
2344 level->no_level_file = FALSE;
2347 level->changed = FALSE;
2350 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2352 level_file_info->nr = 0;
2353 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2354 level_file_info->packed = FALSE;
2356 setString(&level_file_info->basename, NULL);
2357 setString(&level_file_info->filename, NULL);
2360 int getMappedElement_SB(int, boolean);
2362 static void ActivateLevelTemplate(void)
2366 if (check_special_flags("load_xsb_to_ces"))
2368 // fill smaller playfields with padding "beyond border wall" elements
2369 if (level.fieldx < level_template.fieldx ||
2370 level.fieldy < level_template.fieldy)
2372 short field[level.fieldx][level.fieldy];
2373 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2374 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2375 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2376 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2378 // copy old playfield (which is smaller than the visible area)
2379 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2380 field[x][y] = level.field[x][y];
2382 // fill new, larger playfield with "beyond border wall" elements
2383 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2384 level.field[x][y] = getMappedElement_SB('_', TRUE);
2386 // copy the old playfield to the middle of the new playfield
2387 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2388 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2390 level.fieldx = new_fieldx;
2391 level.fieldy = new_fieldy;
2395 // Currently there is no special action needed to activate the template
2396 // data, because 'element_info' property settings overwrite the original
2397 // level data, while all other variables do not change.
2399 // Exception: 'from_level_template' elements in the original level playfield
2400 // are overwritten with the corresponding elements at the same position in
2401 // playfield from the level template.
2403 for (x = 0; x < level.fieldx; x++)
2404 for (y = 0; y < level.fieldy; y++)
2405 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2406 level.field[x][y] = level_template.field[x][y];
2408 if (check_special_flags("load_xsb_to_ces"))
2410 struct LevelInfo level_backup = level;
2412 // overwrite all individual level settings from template level settings
2413 level = level_template;
2415 // restore level file info
2416 level.file_info = level_backup.file_info;
2418 // restore playfield size
2419 level.fieldx = level_backup.fieldx;
2420 level.fieldy = level_backup.fieldy;
2422 // restore playfield content
2423 for (x = 0; x < level.fieldx; x++)
2424 for (y = 0; y < level.fieldy; y++)
2425 level.field[x][y] = level_backup.field[x][y];
2427 // restore name and author from individual level
2428 strcpy(level.name, level_backup.name);
2429 strcpy(level.author, level_backup.author);
2431 // restore flag "use_custom_template"
2432 level.use_custom_template = level_backup.use_custom_template;
2436 static boolean checkForPackageFromBasename_BD(char *basename)
2438 // check for native BD level file extensions
2439 if (!strSuffixLower(basename, ".bd") &&
2440 !strSuffixLower(basename, ".bdr") &&
2441 !strSuffixLower(basename, ".brc") &&
2442 !strSuffixLower(basename, ".gds"))
2445 // check for standard single-level BD files (like "001.bd")
2446 if (strSuffixLower(basename, ".bd") &&
2447 strlen(basename) == 6 &&
2448 basename[0] >= '0' && basename[0] <= '9' &&
2449 basename[1] >= '0' && basename[1] <= '9' &&
2450 basename[2] >= '0' && basename[2] <= '9')
2453 // this is a level package in native BD file format
2457 static char *getLevelFilenameFromBasename(char *basename)
2459 static char *filename = NULL;
2461 checked_free(filename);
2463 filename = getPath2(getCurrentLevelDir(), basename);
2468 static int getFileTypeFromBasename(char *basename)
2470 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2472 static char *filename = NULL;
2473 struct stat file_status;
2475 // ---------- try to determine file type from filename ----------
2477 // check for typical filename of a Supaplex level package file
2478 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2479 return LEVEL_FILE_TYPE_SP;
2481 // check for typical filename of a Diamond Caves II level package file
2482 if (strSuffixLower(basename, ".dc") ||
2483 strSuffixLower(basename, ".dc2"))
2484 return LEVEL_FILE_TYPE_DC;
2486 // check for typical filename of a Sokoban level package file
2487 if (strSuffixLower(basename, ".xsb") &&
2488 strchr(basename, '%') == NULL)
2489 return LEVEL_FILE_TYPE_SB;
2491 // check for typical filename of a Boulder Dash (GDash) level package file
2492 if (checkForPackageFromBasename_BD(basename))
2493 return LEVEL_FILE_TYPE_BD;
2495 // ---------- try to determine file type from filesize ----------
2497 checked_free(filename);
2498 filename = getPath2(getCurrentLevelDir(), basename);
2500 if (stat(filename, &file_status) == 0)
2502 // check for typical filesize of a Supaplex level package file
2503 if (file_status.st_size == 170496)
2504 return LEVEL_FILE_TYPE_SP;
2507 return LEVEL_FILE_TYPE_UNKNOWN;
2510 static int getFileTypeFromMagicBytes(char *filename, int type)
2514 if ((file = openFile(filename, MODE_READ)))
2516 char chunk_name[CHUNK_ID_LEN + 1];
2518 getFileChunkBE(file, chunk_name, NULL);
2520 if (strEqual(chunk_name, "MMII") ||
2521 strEqual(chunk_name, "MIRR"))
2522 type = LEVEL_FILE_TYPE_MM;
2530 static boolean checkForPackageFromBasename(char *basename)
2532 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2533 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2535 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2538 static char *getSingleLevelBasenameExt(int nr, char *extension)
2540 static char basename[MAX_FILENAME_LEN];
2543 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2545 sprintf(basename, "%03d.%s", nr, extension);
2550 static char *getSingleLevelBasename(int nr)
2552 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2555 static char *getPackedLevelBasename(int type)
2557 static char basename[MAX_FILENAME_LEN];
2558 char *directory = getCurrentLevelDir();
2560 DirectoryEntry *dir_entry;
2562 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2564 if ((dir = openDirectory(directory)) == NULL)
2566 Warn("cannot read current level directory '%s'", directory);
2571 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2573 char *entry_basename = dir_entry->basename;
2574 int entry_type = getFileTypeFromBasename(entry_basename);
2576 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2578 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2581 strcpy(basename, entry_basename);
2588 closeDirectory(dir);
2593 static char *getSingleLevelFilename(int nr)
2595 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2598 #if ENABLE_UNUSED_CODE
2599 static char *getPackedLevelFilename(int type)
2601 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2605 char *getDefaultLevelFilename(int nr)
2607 return getSingleLevelFilename(nr);
2610 #if ENABLE_UNUSED_CODE
2611 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2615 lfi->packed = FALSE;
2617 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2618 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2622 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2623 int type, char *format, ...)
2625 static char basename[MAX_FILENAME_LEN];
2628 va_start(ap, format);
2629 vsprintf(basename, format, ap);
2633 lfi->packed = FALSE;
2635 setString(&lfi->basename, basename);
2636 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2639 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2645 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2646 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2649 static int getFiletypeFromID(char *filetype_id)
2651 char *filetype_id_lower;
2652 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2655 if (filetype_id == NULL)
2656 return LEVEL_FILE_TYPE_UNKNOWN;
2658 filetype_id_lower = getStringToLower(filetype_id);
2660 for (i = 0; filetype_id_list[i].id != NULL; i++)
2662 char *id_lower = getStringToLower(filetype_id_list[i].id);
2664 if (strEqual(filetype_id_lower, id_lower))
2665 filetype = filetype_id_list[i].filetype;
2669 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2673 free(filetype_id_lower);
2678 char *getLocalLevelTemplateFilename(void)
2680 return getDefaultLevelFilename(-1);
2683 char *getGlobalLevelTemplateFilename(void)
2685 // global variable "leveldir_current" must be modified in the loop below
2686 LevelDirTree *leveldir_current_last = leveldir_current;
2687 char *filename = NULL;
2689 // check for template level in path from current to topmost tree node
2691 while (leveldir_current != NULL)
2693 filename = getDefaultLevelFilename(-1);
2695 if (fileExists(filename))
2698 leveldir_current = leveldir_current->node_parent;
2701 // restore global variable "leveldir_current" modified in above loop
2702 leveldir_current = leveldir_current_last;
2707 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2711 // special case: level number is negative => check for level template file
2714 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2715 getSingleLevelBasename(-1));
2717 // replace local level template filename with global template filename
2718 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2720 // no fallback if template file not existing
2724 // special case: check for file name/pattern specified in "levelinfo.conf"
2725 if (leveldir_current->level_filename != NULL)
2727 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2729 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2730 leveldir_current->level_filename, nr);
2732 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2734 if (fileExists(lfi->filename))
2737 else if (leveldir_current->level_filetype != NULL)
2739 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2741 // check for specified native level file with standard file name
2742 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2743 "%03d.%s", nr, LEVELFILE_EXTENSION);
2744 if (fileExists(lfi->filename))
2748 // check for native Rocks'n'Diamonds level file
2749 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2750 "%03d.%s", nr, LEVELFILE_EXTENSION);
2751 if (fileExists(lfi->filename))
2754 // check for native Boulder Dash level file
2755 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2756 if (fileExists(lfi->filename))
2759 // check for Emerald Mine level file (V1)
2760 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2761 'a' + (nr / 10) % 26, '0' + nr % 10);
2762 if (fileExists(lfi->filename))
2764 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2765 'A' + (nr / 10) % 26, '0' + nr % 10);
2766 if (fileExists(lfi->filename))
2769 // check for Emerald Mine level file (V2 to V5)
2770 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2771 if (fileExists(lfi->filename))
2774 // check for Emerald Mine level file (V6 / single mode)
2775 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2776 if (fileExists(lfi->filename))
2778 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2779 if (fileExists(lfi->filename))
2782 // check for Emerald Mine level file (V6 / teamwork mode)
2783 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2784 if (fileExists(lfi->filename))
2786 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2787 if (fileExists(lfi->filename))
2790 // check for various packed level file formats
2791 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2792 if (fileExists(lfi->filename))
2795 // no known level file found -- use default values (and fail later)
2796 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2797 "%03d.%s", nr, LEVELFILE_EXTENSION);
2800 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2802 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2803 lfi->type = getFileTypeFromBasename(lfi->basename);
2805 if (lfi->type == LEVEL_FILE_TYPE_RND)
2806 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2809 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2811 // always start with reliable default values
2812 setFileInfoToDefaults(level_file_info);
2814 level_file_info->nr = nr; // set requested level number
2816 determineLevelFileInfo_Filename(level_file_info);
2817 determineLevelFileInfo_Filetype(level_file_info);
2820 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2821 struct LevelFileInfo *lfi_to)
2823 lfi_to->nr = lfi_from->nr;
2824 lfi_to->type = lfi_from->type;
2825 lfi_to->packed = lfi_from->packed;
2827 setString(&lfi_to->basename, lfi_from->basename);
2828 setString(&lfi_to->filename, lfi_from->filename);
2831 // ----------------------------------------------------------------------------
2832 // functions for loading R'n'D level
2833 // ----------------------------------------------------------------------------
2835 int getMappedElement(int element)
2837 // remap some (historic, now obsolete) elements
2841 case EL_PLAYER_OBSOLETE:
2842 element = EL_PLAYER_1;
2845 case EL_KEY_OBSOLETE:
2849 case EL_EM_KEY_1_FILE_OBSOLETE:
2850 element = EL_EM_KEY_1;
2853 case EL_EM_KEY_2_FILE_OBSOLETE:
2854 element = EL_EM_KEY_2;
2857 case EL_EM_KEY_3_FILE_OBSOLETE:
2858 element = EL_EM_KEY_3;
2861 case EL_EM_KEY_4_FILE_OBSOLETE:
2862 element = EL_EM_KEY_4;
2865 case EL_ENVELOPE_OBSOLETE:
2866 element = EL_ENVELOPE_1;
2874 if (element >= NUM_FILE_ELEMENTS)
2876 Warn("invalid level element %d", element);
2878 element = EL_UNKNOWN;
2886 static int getMappedElementByVersion(int element, int game_version)
2888 // remap some elements due to certain game version
2890 if (game_version <= VERSION_IDENT(2,2,0,0))
2892 // map game font elements
2893 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2894 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2895 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2896 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2899 if (game_version < VERSION_IDENT(3,0,0,0))
2901 // map Supaplex gravity tube elements
2902 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2903 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2904 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2905 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2912 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2914 level->file_version = getFileVersion(file);
2915 level->game_version = getFileVersion(file);
2920 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2922 level->creation_date.year = getFile16BitBE(file);
2923 level->creation_date.month = getFile8Bit(file);
2924 level->creation_date.day = getFile8Bit(file);
2926 level->creation_date.src = DATE_SRC_LEVELFILE;
2931 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2933 int initial_player_stepsize;
2934 int initial_player_gravity;
2937 level->fieldx = getFile8Bit(file);
2938 level->fieldy = getFile8Bit(file);
2940 level->time = getFile16BitBE(file);
2941 level->gems_needed = getFile16BitBE(file);
2943 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2944 level->name[i] = getFile8Bit(file);
2945 level->name[MAX_LEVEL_NAME_LEN] = 0;
2947 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2948 level->score[i] = getFile8Bit(file);
2950 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2951 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2952 for (y = 0; y < 3; y++)
2953 for (x = 0; x < 3; x++)
2954 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2956 level->amoeba_speed = getFile8Bit(file);
2957 level->time_magic_wall = getFile8Bit(file);
2958 level->time_wheel = getFile8Bit(file);
2959 level->amoeba_content = getMappedElement(getFile8Bit(file));
2961 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2964 for (i = 0; i < MAX_PLAYERS; i++)
2965 level->initial_player_stepsize[i] = initial_player_stepsize;
2967 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2969 for (i = 0; i < MAX_PLAYERS; i++)
2970 level->initial_player_gravity[i] = initial_player_gravity;
2972 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2973 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2975 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2977 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2978 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2979 level->can_move_into_acid_bits = getFile32BitBE(file);
2980 level->dont_collide_with_bits = getFile8Bit(file);
2982 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2983 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2985 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2986 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2987 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2989 level->game_engine_type = getFile8Bit(file);
2991 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2996 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3000 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3001 level->name[i] = getFile8Bit(file);
3002 level->name[MAX_LEVEL_NAME_LEN] = 0;
3007 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3011 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3012 level->author[i] = getFile8Bit(file);
3013 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3018 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3021 int chunk_size_expected = level->fieldx * level->fieldy;
3023 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3024 stored with 16-bit encoding (and should be twice as big then).
3025 Even worse, playfield data was stored 16-bit when only yamyam content
3026 contained 16-bit elements and vice versa. */
3028 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3029 chunk_size_expected *= 2;
3031 if (chunk_size_expected != chunk_size)
3033 ReadUnusedBytesFromFile(file, chunk_size);
3034 return chunk_size_expected;
3037 for (y = 0; y < level->fieldy; y++)
3038 for (x = 0; x < level->fieldx; x++)
3039 level->field[x][y] =
3040 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3045 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3048 int header_size = 4;
3049 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3050 int chunk_size_expected = header_size + content_size;
3052 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3053 stored with 16-bit encoding (and should be twice as big then).
3054 Even worse, playfield data was stored 16-bit when only yamyam content
3055 contained 16-bit elements and vice versa. */
3057 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3058 chunk_size_expected += content_size;
3060 if (chunk_size_expected != chunk_size)
3062 ReadUnusedBytesFromFile(file, chunk_size);
3063 return chunk_size_expected;
3067 level->num_yamyam_contents = getFile8Bit(file);
3071 // correct invalid number of content fields -- should never happen
3072 if (level->num_yamyam_contents < 1 ||
3073 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3074 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3076 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3077 for (y = 0; y < 3; y++)
3078 for (x = 0; x < 3; x++)
3079 level->yamyam_content[i].e[x][y] =
3080 getMappedElement(level->encoding_16bit_field ?
3081 getFile16BitBE(file) : getFile8Bit(file));
3085 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3090 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3092 element = getMappedElement(getFile16BitBE(file));
3093 num_contents = getFile8Bit(file);
3095 getFile8Bit(file); // content x size (unused)
3096 getFile8Bit(file); // content y size (unused)
3098 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3100 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3101 for (y = 0; y < 3; y++)
3102 for (x = 0; x < 3; x++)
3103 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3105 // correct invalid number of content fields -- should never happen
3106 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3107 num_contents = STD_ELEMENT_CONTENTS;
3109 if (element == EL_YAMYAM)
3111 level->num_yamyam_contents = num_contents;
3113 for (i = 0; i < num_contents; i++)
3114 for (y = 0; y < 3; y++)
3115 for (x = 0; x < 3; x++)
3116 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3118 else if (element == EL_BD_AMOEBA)
3120 level->amoeba_content = content_array[0][0][0];
3124 Warn("cannot load content for element '%d'", element);
3130 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3136 int chunk_size_expected;
3138 element = getMappedElement(getFile16BitBE(file));
3139 if (!IS_ENVELOPE(element))
3140 element = EL_ENVELOPE_1;
3142 envelope_nr = element - EL_ENVELOPE_1;
3144 envelope_len = getFile16BitBE(file);
3146 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3147 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3149 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3151 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3152 if (chunk_size_expected != chunk_size)
3154 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3155 return chunk_size_expected;
3158 for (i = 0; i < envelope_len; i++)
3159 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3164 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3166 int num_changed_custom_elements = getFile16BitBE(file);
3167 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3170 if (chunk_size_expected != chunk_size)
3172 ReadUnusedBytesFromFile(file, chunk_size - 2);
3173 return chunk_size_expected;
3176 for (i = 0; i < num_changed_custom_elements; i++)
3178 int element = getMappedElement(getFile16BitBE(file));
3179 int properties = getFile32BitBE(file);
3181 if (IS_CUSTOM_ELEMENT(element))
3182 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3184 Warn("invalid custom element number %d", element);
3186 // older game versions that wrote level files with CUS1 chunks used
3187 // different default push delay values (not yet stored in level file)
3188 element_info[element].push_delay_fixed = 2;
3189 element_info[element].push_delay_random = 8;
3192 level->file_has_custom_elements = TRUE;
3197 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3199 int num_changed_custom_elements = getFile16BitBE(file);
3200 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3203 if (chunk_size_expected != chunk_size)
3205 ReadUnusedBytesFromFile(file, chunk_size - 2);
3206 return chunk_size_expected;
3209 for (i = 0; i < num_changed_custom_elements; i++)
3211 int element = getMappedElement(getFile16BitBE(file));
3212 int custom_target_element = getMappedElement(getFile16BitBE(file));
3214 if (IS_CUSTOM_ELEMENT(element))
3215 element_info[element].change->target_element = custom_target_element;
3217 Warn("invalid custom element number %d", element);
3220 level->file_has_custom_elements = TRUE;
3225 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3227 int num_changed_custom_elements = getFile16BitBE(file);
3228 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3231 if (chunk_size_expected != chunk_size)
3233 ReadUnusedBytesFromFile(file, chunk_size - 2);
3234 return chunk_size_expected;
3237 for (i = 0; i < num_changed_custom_elements; i++)
3239 int element = getMappedElement(getFile16BitBE(file));
3240 struct ElementInfo *ei = &element_info[element];
3241 unsigned int event_bits;
3243 if (!IS_CUSTOM_ELEMENT(element))
3245 Warn("invalid custom element number %d", element);
3247 element = EL_INTERNAL_DUMMY;
3250 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3251 ei->description[j] = getFile8Bit(file);
3252 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3254 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3256 // some free bytes for future properties and padding
3257 ReadUnusedBytesFromFile(file, 7);
3259 ei->use_gfx_element = getFile8Bit(file);
3260 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3262 ei->collect_score_initial = getFile8Bit(file);
3263 ei->collect_count_initial = getFile8Bit(file);
3265 ei->push_delay_fixed = getFile16BitBE(file);
3266 ei->push_delay_random = getFile16BitBE(file);
3267 ei->move_delay_fixed = getFile16BitBE(file);
3268 ei->move_delay_random = getFile16BitBE(file);
3270 ei->move_pattern = getFile16BitBE(file);
3271 ei->move_direction_initial = getFile8Bit(file);
3272 ei->move_stepsize = getFile8Bit(file);
3274 for (y = 0; y < 3; y++)
3275 for (x = 0; x < 3; x++)
3276 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3278 // bits 0 - 31 of "has_event[]"
3279 event_bits = getFile32BitBE(file);
3280 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3281 if (event_bits & (1u << j))
3282 ei->change->has_event[j] = TRUE;
3284 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3286 ei->change->delay_fixed = getFile16BitBE(file);
3287 ei->change->delay_random = getFile16BitBE(file);
3288 ei->change->delay_frames = getFile16BitBE(file);
3290 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3292 ei->change->explode = getFile8Bit(file);
3293 ei->change->use_target_content = getFile8Bit(file);
3294 ei->change->only_if_complete = getFile8Bit(file);
3295 ei->change->use_random_replace = getFile8Bit(file);
3297 ei->change->random_percentage = getFile8Bit(file);
3298 ei->change->replace_when = getFile8Bit(file);
3300 for (y = 0; y < 3; y++)
3301 for (x = 0; x < 3; x++)
3302 ei->change->target_content.e[x][y] =
3303 getMappedElement(getFile16BitBE(file));
3305 ei->slippery_type = getFile8Bit(file);
3307 // some free bytes for future properties and padding
3308 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3310 // mark that this custom element has been modified
3311 ei->modified_settings = TRUE;
3314 level->file_has_custom_elements = TRUE;
3319 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3321 struct ElementInfo *ei;
3322 int chunk_size_expected;
3326 // ---------- custom element base property values (96 bytes) ----------------
3328 element = getMappedElement(getFile16BitBE(file));
3330 if (!IS_CUSTOM_ELEMENT(element))
3332 Warn("invalid custom element number %d", element);
3334 ReadUnusedBytesFromFile(file, chunk_size - 2);
3339 ei = &element_info[element];
3341 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3342 ei->description[i] = getFile8Bit(file);
3343 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3345 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3347 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3349 ei->num_change_pages = getFile8Bit(file);
3351 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3352 if (chunk_size_expected != chunk_size)
3354 ReadUnusedBytesFromFile(file, chunk_size - 43);
3355 return chunk_size_expected;
3358 ei->ce_value_fixed_initial = getFile16BitBE(file);
3359 ei->ce_value_random_initial = getFile16BitBE(file);
3360 ei->use_last_ce_value = getFile8Bit(file);
3362 ei->use_gfx_element = getFile8Bit(file);
3363 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3365 ei->collect_score_initial = getFile8Bit(file);
3366 ei->collect_count_initial = getFile8Bit(file);
3368 ei->drop_delay_fixed = getFile8Bit(file);
3369 ei->push_delay_fixed = getFile8Bit(file);
3370 ei->drop_delay_random = getFile8Bit(file);
3371 ei->push_delay_random = getFile8Bit(file);
3372 ei->move_delay_fixed = getFile16BitBE(file);
3373 ei->move_delay_random = getFile16BitBE(file);
3375 // bits 0 - 15 of "move_pattern" ...
3376 ei->move_pattern = getFile16BitBE(file);
3377 ei->move_direction_initial = getFile8Bit(file);
3378 ei->move_stepsize = getFile8Bit(file);
3380 ei->slippery_type = getFile8Bit(file);
3382 for (y = 0; y < 3; y++)
3383 for (x = 0; x < 3; x++)
3384 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3386 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3387 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3388 ei->move_leave_type = getFile8Bit(file);
3390 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3391 ei->move_pattern |= (getFile16BitBE(file) << 16);
3393 ei->access_direction = getFile8Bit(file);
3395 ei->explosion_delay = getFile8Bit(file);
3396 ei->ignition_delay = getFile8Bit(file);
3397 ei->explosion_type = getFile8Bit(file);
3399 // some free bytes for future custom property values and padding
3400 ReadUnusedBytesFromFile(file, 1);
3402 // ---------- change page property values (48 bytes) ------------------------
3404 setElementChangePages(ei, ei->num_change_pages);
3406 for (i = 0; i < ei->num_change_pages; i++)
3408 struct ElementChangeInfo *change = &ei->change_page[i];
3409 unsigned int event_bits;
3411 // always start with reliable default values
3412 setElementChangeInfoToDefaults(change);
3414 // bits 0 - 31 of "has_event[]" ...
3415 event_bits = getFile32BitBE(file);
3416 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3417 if (event_bits & (1u << j))
3418 change->has_event[j] = TRUE;
3420 change->target_element = getMappedElement(getFile16BitBE(file));
3422 change->delay_fixed = getFile16BitBE(file);
3423 change->delay_random = getFile16BitBE(file);
3424 change->delay_frames = getFile16BitBE(file);
3426 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3428 change->explode = getFile8Bit(file);
3429 change->use_target_content = getFile8Bit(file);
3430 change->only_if_complete = getFile8Bit(file);
3431 change->use_random_replace = getFile8Bit(file);
3433 change->random_percentage = getFile8Bit(file);
3434 change->replace_when = getFile8Bit(file);
3436 for (y = 0; y < 3; y++)
3437 for (x = 0; x < 3; x++)
3438 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3440 change->can_change = getFile8Bit(file);
3442 change->trigger_side = getFile8Bit(file);
3444 change->trigger_player = getFile8Bit(file);
3445 change->trigger_page = getFile8Bit(file);
3447 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3448 CH_PAGE_ANY : (1 << change->trigger_page));
3450 change->has_action = getFile8Bit(file);
3451 change->action_type = getFile8Bit(file);
3452 change->action_mode = getFile8Bit(file);
3453 change->action_arg = getFile16BitBE(file);
3455 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3456 event_bits = getFile8Bit(file);
3457 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3458 if (event_bits & (1u << (j - 32)))
3459 change->has_event[j] = TRUE;
3462 // mark this custom element as modified
3463 ei->modified_settings = TRUE;
3465 level->file_has_custom_elements = TRUE;
3470 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3472 struct ElementInfo *ei;
3473 struct ElementGroupInfo *group;
3477 element = getMappedElement(getFile16BitBE(file));
3479 if (!IS_GROUP_ELEMENT(element))
3481 Warn("invalid group element number %d", element);
3483 ReadUnusedBytesFromFile(file, chunk_size - 2);
3488 ei = &element_info[element];
3490 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3491 ei->description[i] = getFile8Bit(file);
3492 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3494 group = element_info[element].group;
3496 group->num_elements = getFile8Bit(file);
3498 ei->use_gfx_element = getFile8Bit(file);
3499 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3501 group->choice_mode = getFile8Bit(file);
3503 // some free bytes for future values and padding
3504 ReadUnusedBytesFromFile(file, 3);
3506 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3507 group->element[i] = getMappedElement(getFile16BitBE(file));
3509 // mark this group element as modified
3510 element_info[element].modified_settings = TRUE;
3512 level->file_has_custom_elements = TRUE;
3517 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3518 int element, int real_element)
3520 int micro_chunk_size = 0;
3521 int conf_type = getFile8Bit(file);
3522 int byte_mask = conf_type & CONF_MASK_BYTES;
3523 boolean element_found = FALSE;
3526 micro_chunk_size += 1;
3528 if (byte_mask == CONF_MASK_MULTI_BYTES)
3530 int num_bytes = getFile16BitBE(file);
3531 byte *buffer = checked_malloc(num_bytes);
3533 ReadBytesFromFile(file, buffer, num_bytes);
3535 for (i = 0; conf[i].data_type != -1; i++)
3537 if (conf[i].element == element &&
3538 conf[i].conf_type == conf_type)
3540 int data_type = conf[i].data_type;
3541 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3542 int max_num_entities = conf[i].max_num_entities;
3544 if (num_entities > max_num_entities)
3546 Warn("truncating number of entities for element %d from %d to %d",
3547 element, num_entities, max_num_entities);
3549 num_entities = max_num_entities;
3552 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3553 data_type == TYPE_CONTENT_LIST))
3555 // for element and content lists, zero entities are not allowed
3556 Warn("found empty list of entities for element %d", element);
3558 // do not set "num_entities" here to prevent reading behind buffer
3560 *(int *)(conf[i].num_entities) = 1; // at least one is required
3564 *(int *)(conf[i].num_entities) = num_entities;
3567 element_found = TRUE;
3569 if (data_type == TYPE_STRING)
3571 char *string = (char *)(conf[i].value);
3574 for (j = 0; j < max_num_entities; j++)
3575 string[j] = (j < num_entities ? buffer[j] : '\0');
3577 else if (data_type == TYPE_ELEMENT_LIST)
3579 int *element_array = (int *)(conf[i].value);
3582 for (j = 0; j < num_entities; j++)
3584 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3586 else if (data_type == TYPE_CONTENT_LIST)
3588 struct Content *content= (struct Content *)(conf[i].value);
3591 for (c = 0; c < num_entities; c++)
3592 for (y = 0; y < 3; y++)
3593 for (x = 0; x < 3; x++)
3594 content[c].e[x][y] =
3595 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3598 element_found = FALSE;
3604 checked_free(buffer);
3606 micro_chunk_size += 2 + num_bytes;
3608 else // constant size configuration data (1, 2 or 4 bytes)
3610 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3611 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3612 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3614 for (i = 0; conf[i].data_type != -1; i++)
3616 if (conf[i].element == element &&
3617 conf[i].conf_type == conf_type)
3619 int data_type = conf[i].data_type;
3621 if (data_type == TYPE_ELEMENT)
3622 value = getMappedElement(value);
3624 if (data_type == TYPE_BOOLEAN)
3625 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3627 *(int *) (conf[i].value) = value;
3629 element_found = TRUE;
3635 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3640 char *error_conf_chunk_bytes =
3641 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3642 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3643 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3644 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3645 int error_element = real_element;
3647 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3648 error_conf_chunk_bytes, error_conf_chunk_token,
3649 error_element, EL_NAME(error_element));
3652 return micro_chunk_size;
3655 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3657 int real_chunk_size = 0;
3659 li = *level; // copy level data into temporary buffer
3661 while (!checkEndOfFile(file))
3663 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3665 if (real_chunk_size >= chunk_size)
3669 *level = li; // copy temporary buffer back to level data
3671 return real_chunk_size;
3674 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3676 int real_chunk_size = 0;
3678 li = *level; // copy level data into temporary buffer
3680 while (!checkEndOfFile(file))
3682 int element = getMappedElement(getFile16BitBE(file));
3684 real_chunk_size += 2;
3685 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3687 if (real_chunk_size >= chunk_size)
3691 *level = li; // copy temporary buffer back to level data
3693 return real_chunk_size;
3696 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3698 int real_chunk_size = 0;
3700 li = *level; // copy level data into temporary buffer
3702 while (!checkEndOfFile(file))
3704 int element = getMappedElement(getFile16BitBE(file));
3706 real_chunk_size += 2;
3707 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3709 if (real_chunk_size >= chunk_size)
3713 *level = li; // copy temporary buffer back to level data
3715 return real_chunk_size;
3718 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3720 int element = getMappedElement(getFile16BitBE(file));
3721 int envelope_nr = element - EL_ENVELOPE_1;
3722 int real_chunk_size = 2;
3724 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3726 while (!checkEndOfFile(file))
3728 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3731 if (real_chunk_size >= chunk_size)
3735 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3737 return real_chunk_size;
3740 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3742 int element = getMappedElement(getFile16BitBE(file));
3743 int real_chunk_size = 2;
3744 struct ElementInfo *ei = &element_info[element];
3747 xx_ei = *ei; // copy element data into temporary buffer
3749 xx_ei.num_change_pages = -1;
3751 while (!checkEndOfFile(file))
3753 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3755 if (xx_ei.num_change_pages != -1)
3758 if (real_chunk_size >= chunk_size)
3764 if (ei->num_change_pages == -1)
3766 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3769 ei->num_change_pages = 1;
3771 setElementChangePages(ei, 1);
3772 setElementChangeInfoToDefaults(ei->change);
3774 return real_chunk_size;
3777 // initialize number of change pages stored for this custom element
3778 setElementChangePages(ei, ei->num_change_pages);
3779 for (i = 0; i < ei->num_change_pages; i++)
3780 setElementChangeInfoToDefaults(&ei->change_page[i]);
3782 // start with reading properties for the first change page
3783 xx_current_change_page = 0;
3785 while (!checkEndOfFile(file))
3787 // level file might contain invalid change page number
3788 if (xx_current_change_page >= ei->num_change_pages)
3791 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3793 xx_change = *change; // copy change data into temporary buffer
3795 resetEventBits(); // reset bits; change page might have changed
3797 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3800 *change = xx_change;
3802 setEventFlagsFromEventBits(change);
3804 if (real_chunk_size >= chunk_size)
3808 level->file_has_custom_elements = TRUE;
3810 return real_chunk_size;
3813 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3815 int element = getMappedElement(getFile16BitBE(file));
3816 int real_chunk_size = 2;
3817 struct ElementInfo *ei = &element_info[element];
3818 struct ElementGroupInfo *group = ei->group;
3823 xx_ei = *ei; // copy element data into temporary buffer
3824 xx_group = *group; // copy group data into temporary buffer
3826 while (!checkEndOfFile(file))
3828 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3831 if (real_chunk_size >= chunk_size)
3838 level->file_has_custom_elements = TRUE;
3840 return real_chunk_size;
3843 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3845 int element = getMappedElement(getFile16BitBE(file));
3846 int real_chunk_size = 2;
3847 struct ElementInfo *ei = &element_info[element];
3849 xx_ei = *ei; // copy element data into temporary buffer
3851 while (!checkEndOfFile(file))
3853 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3856 if (real_chunk_size >= chunk_size)
3862 level->file_has_custom_elements = TRUE;
3864 return real_chunk_size;
3867 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3868 struct LevelFileInfo *level_file_info,
3869 boolean level_info_only)
3871 char *filename = level_file_info->filename;
3872 char cookie[MAX_LINE_LEN];
3873 char chunk_name[CHUNK_ID_LEN + 1];
3877 if (!(file = openFile(filename, MODE_READ)))
3879 level->no_valid_file = TRUE;
3880 level->no_level_file = TRUE;
3882 if (level_info_only)
3885 Warn("cannot read level '%s' -- using empty level", filename);
3887 if (!setup.editor.use_template_for_new_levels)
3890 // if level file not found, try to initialize level data from template
3891 filename = getGlobalLevelTemplateFilename();
3893 if (!(file = openFile(filename, MODE_READ)))
3896 // default: for empty levels, use level template for custom elements
3897 level->use_custom_template = TRUE;
3899 level->no_valid_file = FALSE;
3902 getFileChunkBE(file, chunk_name, NULL);
3903 if (strEqual(chunk_name, "RND1"))
3905 getFile32BitBE(file); // not used
3907 getFileChunkBE(file, chunk_name, NULL);
3908 if (!strEqual(chunk_name, "CAVE"))
3910 level->no_valid_file = TRUE;
3912 Warn("unknown format of level file '%s'", filename);
3919 else // check for pre-2.0 file format with cookie string
3921 strcpy(cookie, chunk_name);
3922 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3924 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3925 cookie[strlen(cookie) - 1] = '\0';
3927 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3929 level->no_valid_file = TRUE;
3931 Warn("unknown format of level file '%s'", filename);
3938 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3940 level->no_valid_file = TRUE;
3942 Warn("unsupported version of level file '%s'", filename);
3949 // pre-2.0 level files have no game version, so use file version here
3950 level->game_version = level->file_version;
3953 if (level->file_version < FILE_VERSION_1_2)
3955 // level files from versions before 1.2.0 without chunk structure
3956 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3957 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3965 int (*loader)(File *, int, struct LevelInfo *);
3969 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3970 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3971 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3972 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3973 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3974 { "INFO", -1, LoadLevel_INFO },
3975 { "BODY", -1, LoadLevel_BODY },
3976 { "CONT", -1, LoadLevel_CONT },
3977 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3978 { "CNT3", -1, LoadLevel_CNT3 },
3979 { "CUS1", -1, LoadLevel_CUS1 },
3980 { "CUS2", -1, LoadLevel_CUS2 },
3981 { "CUS3", -1, LoadLevel_CUS3 },
3982 { "CUS4", -1, LoadLevel_CUS4 },
3983 { "GRP1", -1, LoadLevel_GRP1 },
3984 { "CONF", -1, LoadLevel_CONF },
3985 { "ELEM", -1, LoadLevel_ELEM },
3986 { "NOTE", -1, LoadLevel_NOTE },
3987 { "CUSX", -1, LoadLevel_CUSX },
3988 { "GRPX", -1, LoadLevel_GRPX },
3989 { "EMPX", -1, LoadLevel_EMPX },
3994 while (getFileChunkBE(file, chunk_name, &chunk_size))
3998 while (chunk_info[i].name != NULL &&
3999 !strEqual(chunk_name, chunk_info[i].name))
4002 if (chunk_info[i].name == NULL)
4004 Warn("unknown chunk '%s' in level file '%s'",
4005 chunk_name, filename);
4007 ReadUnusedBytesFromFile(file, chunk_size);
4009 else if (chunk_info[i].size != -1 &&
4010 chunk_info[i].size != chunk_size)
4012 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4013 chunk_size, chunk_name, filename);
4015 ReadUnusedBytesFromFile(file, chunk_size);
4019 // call function to load this level chunk
4020 int chunk_size_expected =
4021 (chunk_info[i].loader)(file, chunk_size, level);
4023 if (chunk_size_expected < 0)
4025 Warn("error reading chunk '%s' in level file '%s'",
4026 chunk_name, filename);
4031 // the size of some chunks cannot be checked before reading other
4032 // chunks first (like "HEAD" and "BODY") that contain some header
4033 // information, so check them here
4034 if (chunk_size_expected != chunk_size)
4036 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4037 chunk_size, chunk_name, filename);
4049 // ----------------------------------------------------------------------------
4050 // functions for loading BD level
4051 // ----------------------------------------------------------------------------
4053 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4055 struct LevelInfo_BD *level_bd = level->native_bd_level;
4056 GdCave *cave = NULL; // will be changed below
4057 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4058 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4061 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4063 // cave and map newly allocated when set to defaults above
4064 cave = level_bd->cave;
4067 cave->intermission = level->bd_intermission;
4070 cave->level_time[0] = level->time;
4071 cave->level_diamonds[0] = level->gems_needed;
4074 cave->scheduling = level->bd_scheduling_type;
4075 cave->pal_timing = level->bd_pal_timing;
4076 cave->level_speed[0] = level->bd_cycle_delay_ms;
4077 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4078 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4079 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4082 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4083 cave->diamond_value = level->score[SC_EMERALD];
4084 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4086 // compatibility settings
4087 cave->lineshift = level->bd_line_shifting_borders;
4088 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4089 cave->short_explosions = level->bd_short_explosions;
4090 cave->gravity_affects_all = level->bd_gravity_affects_all;
4092 // player properties
4093 cave->diagonal_movements = level->bd_diagonal_movements;
4094 cave->active_is_first_found = level->bd_topmost_player_active;
4095 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4096 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4097 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4098 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4100 // element properties
4101 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4102 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4103 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4104 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4105 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4106 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4107 cave->level_magic_wall_time[0] = level->time_magic_wall;
4108 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4109 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4111 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4112 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4113 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4114 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4115 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4116 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4117 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4119 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4120 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4121 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4122 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4123 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4124 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4125 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4126 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4127 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4128 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4129 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4131 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4132 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4133 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4134 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4135 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4136 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4138 cave->slime_predictable = level->bd_slime_is_predictable;
4139 cave->slime_correct_random = level->bd_slime_correct_random;
4140 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4141 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4142 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4143 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4144 cave->slime_eats_1 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4145 cave->slime_converts_1 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4146 cave->slime_eats_2 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4147 cave->slime_converts_2 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4148 cave->slime_eats_3 = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4149 cave->slime_converts_3 = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4151 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4152 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4153 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4155 cave->biter_delay_frame = level->bd_biter_move_delay;
4156 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4158 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4160 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4162 cave->replicators_active = level->bd_replicators_active;
4163 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4165 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4166 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4168 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4170 cave->nut_turns_to_when_crushed = map_element_RND_to_BD_cave(level->bd_nut_content);
4173 strncpy(cave->name, level->name, sizeof(GdString));
4174 cave->name[sizeof(GdString) - 1] = '\0';
4176 // playfield elements
4177 for (x = 0; x < cave->w; x++)
4178 for (y = 0; y < cave->h; y++)
4179 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4182 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4184 struct LevelInfo_BD *level_bd = level->native_bd_level;
4185 GdCave *cave = level_bd->cave;
4186 int bd_level_nr = level_bd->level_nr;
4189 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4190 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4193 level->bd_intermission = cave->intermission;
4196 level->time = cave->level_time[bd_level_nr];
4197 level->gems_needed = cave->level_diamonds[bd_level_nr];
4200 level->bd_scheduling_type = cave->scheduling;
4201 level->bd_pal_timing = cave->pal_timing;
4202 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4203 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4204 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4205 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4208 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4209 level->score[SC_EMERALD] = cave->diamond_value;
4210 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4212 // compatibility settings
4213 level->bd_line_shifting_borders = cave->lineshift;
4214 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4215 level->bd_short_explosions = cave->short_explosions;
4216 level->bd_gravity_affects_all = cave->gravity_affects_all;
4218 // player properties
4219 level->bd_diagonal_movements = cave->diagonal_movements;
4220 level->bd_topmost_player_active = cave->active_is_first_found;
4221 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4222 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4223 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4224 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4226 // element properties
4227 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4228 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4229 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4230 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4231 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4232 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4233 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4234 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4235 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4237 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4238 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4239 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4240 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4241 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4242 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4243 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4245 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4246 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4247 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4248 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4249 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4250 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4251 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4252 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4253 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4254 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4255 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4257 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4258 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4259 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4260 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4261 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4262 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4264 level->bd_slime_is_predictable = cave->slime_predictable;
4265 level->bd_slime_correct_random = cave->slime_correct_random;
4266 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4267 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4268 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4269 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4270 level->bd_slime_eats_element_1 = map_element_BD_to_RND_cave(cave->slime_eats_1);
4271 level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4272 level->bd_slime_eats_element_2 = map_element_BD_to_RND_cave(cave->slime_eats_2);
4273 level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4274 level->bd_slime_eats_element_3 = map_element_BD_to_RND_cave(cave->slime_eats_3);
4275 level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4277 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4278 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4279 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4281 level->bd_biter_move_delay = cave->biter_delay_frame;
4282 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4284 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4286 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4288 level->bd_replicators_active = cave->replicators_active;
4289 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4291 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4292 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4294 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4296 level->bd_nut_content = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4299 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4301 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4302 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4304 // playfield elements
4305 for (x = 0; x < level->fieldx; x++)
4306 for (y = 0; y < level->fieldy; y++)
4307 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4309 checked_free(cave_name);
4312 static void setTapeInfoToDefaults(void);
4314 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4316 struct LevelInfo_BD *level_bd = level->native_bd_level;
4317 GdCave *cave = level_bd->cave;
4318 GdReplay *replay = level_bd->replay;
4324 // always start with reliable default values
4325 setTapeInfoToDefaults();
4327 tape.level_nr = level_nr; // (currently not used)
4328 tape.random_seed = replay->seed;
4330 TapeSetDateFromIsoDateString(replay->date);
4333 tape.pos[tape.counter].delay = 0;
4335 tape.bd_replay = TRUE;
4337 // all time calculations only used to display approximate tape time
4338 int cave_speed = cave->speed;
4339 int milliseconds_game = 0;
4340 int milliseconds_elapsed = 20;
4342 for (i = 0; i < replay->movements->len; i++)
4344 int replay_action = replay->movements->data[i];
4345 int tape_action = map_action_BD_to_RND(replay_action);
4346 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4347 boolean success = 0;
4351 success = TapeAddAction(action);
4353 milliseconds_game += milliseconds_elapsed;
4355 if (milliseconds_game >= cave_speed)
4357 milliseconds_game -= cave_speed;
4364 tape.pos[tape.counter].delay = 0;
4365 tape.pos[tape.counter].action[0] = 0;
4369 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4375 TapeHaltRecording();
4379 // ----------------------------------------------------------------------------
4380 // functions for loading EM level
4381 // ----------------------------------------------------------------------------
4383 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4385 static int ball_xy[8][2] =
4396 struct LevelInfo_EM *level_em = level->native_em_level;
4397 struct CAVE *cav = level_em->cav;
4400 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4401 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4403 cav->time_seconds = level->time;
4404 cav->gems_needed = level->gems_needed;
4406 cav->emerald_score = level->score[SC_EMERALD];
4407 cav->diamond_score = level->score[SC_DIAMOND];
4408 cav->alien_score = level->score[SC_ROBOT];
4409 cav->tank_score = level->score[SC_SPACESHIP];
4410 cav->bug_score = level->score[SC_BUG];
4411 cav->eater_score = level->score[SC_YAMYAM];
4412 cav->nut_score = level->score[SC_NUT];
4413 cav->dynamite_score = level->score[SC_DYNAMITE];
4414 cav->key_score = level->score[SC_KEY];
4415 cav->exit_score = level->score[SC_TIME_BONUS];
4417 cav->num_eater_arrays = level->num_yamyam_contents;
4419 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4420 for (y = 0; y < 3; y++)
4421 for (x = 0; x < 3; x++)
4422 cav->eater_array[i][y * 3 + x] =
4423 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4425 cav->amoeba_time = level->amoeba_speed;
4426 cav->wonderwall_time = level->time_magic_wall;
4427 cav->wheel_time = level->time_wheel;
4429 cav->android_move_time = level->android_move_time;
4430 cav->android_clone_time = level->android_clone_time;
4431 cav->ball_random = level->ball_random;
4432 cav->ball_active = level->ball_active_initial;
4433 cav->ball_time = level->ball_time;
4434 cav->num_ball_arrays = level->num_ball_contents;
4436 cav->lenses_score = level->lenses_score;
4437 cav->magnify_score = level->magnify_score;
4438 cav->slurp_score = level->slurp_score;
4440 cav->lenses_time = level->lenses_time;
4441 cav->magnify_time = level->magnify_time;
4443 cav->wind_time = 9999;
4444 cav->wind_direction =
4445 map_direction_RND_to_EM(level->wind_direction_initial);
4447 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4448 for (j = 0; j < 8; j++)
4449 cav->ball_array[i][j] =
4450 map_element_RND_to_EM_cave(level->ball_content[i].
4451 e[ball_xy[j][0]][ball_xy[j][1]]);
4453 map_android_clone_elements_RND_to_EM(level);
4455 // first fill the complete playfield with the empty space element
4456 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4457 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4458 cav->cave[x][y] = Cblank;
4460 // then copy the real level contents from level file into the playfield
4461 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4463 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4465 if (level->field[x][y] == EL_AMOEBA_DEAD)
4466 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4468 cav->cave[x][y] = new_element;
4471 for (i = 0; i < MAX_PLAYERS; i++)
4473 cav->player_x[i] = -1;
4474 cav->player_y[i] = -1;
4477 // initialize player positions and delete players from the playfield
4478 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4480 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4482 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4484 cav->player_x[player_nr] = x;
4485 cav->player_y[player_nr] = y;
4487 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4492 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4494 static int ball_xy[8][2] =
4505 struct LevelInfo_EM *level_em = level->native_em_level;
4506 struct CAVE *cav = level_em->cav;
4509 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4510 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4512 level->time = cav->time_seconds;
4513 level->gems_needed = cav->gems_needed;
4515 sprintf(level->name, "Level %d", level->file_info.nr);
4517 level->score[SC_EMERALD] = cav->emerald_score;
4518 level->score[SC_DIAMOND] = cav->diamond_score;
4519 level->score[SC_ROBOT] = cav->alien_score;
4520 level->score[SC_SPACESHIP] = cav->tank_score;
4521 level->score[SC_BUG] = cav->bug_score;
4522 level->score[SC_YAMYAM] = cav->eater_score;
4523 level->score[SC_NUT] = cav->nut_score;
4524 level->score[SC_DYNAMITE] = cav->dynamite_score;
4525 level->score[SC_KEY] = cav->key_score;
4526 level->score[SC_TIME_BONUS] = cav->exit_score;
4528 level->num_yamyam_contents = cav->num_eater_arrays;
4530 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4531 for (y = 0; y < 3; y++)
4532 for (x = 0; x < 3; x++)
4533 level->yamyam_content[i].e[x][y] =
4534 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4536 level->amoeba_speed = cav->amoeba_time;
4537 level->time_magic_wall = cav->wonderwall_time;
4538 level->time_wheel = cav->wheel_time;
4540 level->android_move_time = cav->android_move_time;
4541 level->android_clone_time = cav->android_clone_time;
4542 level->ball_random = cav->ball_random;
4543 level->ball_active_initial = cav->ball_active;
4544 level->ball_time = cav->ball_time;
4545 level->num_ball_contents = cav->num_ball_arrays;
4547 level->lenses_score = cav->lenses_score;
4548 level->magnify_score = cav->magnify_score;
4549 level->slurp_score = cav->slurp_score;
4551 level->lenses_time = cav->lenses_time;
4552 level->magnify_time = cav->magnify_time;
4554 level->wind_direction_initial =
4555 map_direction_EM_to_RND(cav->wind_direction);
4557 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4558 for (j = 0; j < 8; j++)
4559 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4560 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4562 map_android_clone_elements_EM_to_RND(level);
4564 // convert the playfield (some elements need special treatment)
4565 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4567 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4569 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4570 new_element = EL_AMOEBA_DEAD;
4572 level->field[x][y] = new_element;
4575 for (i = 0; i < MAX_PLAYERS; i++)
4577 // in case of all players set to the same field, use the first player
4578 int nr = MAX_PLAYERS - i - 1;
4579 int jx = cav->player_x[nr];
4580 int jy = cav->player_y[nr];
4582 if (jx != -1 && jy != -1)
4583 level->field[jx][jy] = EL_PLAYER_1 + nr;
4586 // time score is counted for each 10 seconds left in Emerald Mine levels
4587 level->time_score_base = 10;
4591 // ----------------------------------------------------------------------------
4592 // functions for loading SP level
4593 // ----------------------------------------------------------------------------
4595 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4597 struct LevelInfo_SP *level_sp = level->native_sp_level;
4598 LevelInfoType *header = &level_sp->header;
4601 level_sp->width = level->fieldx;
4602 level_sp->height = level->fieldy;
4604 for (x = 0; x < level->fieldx; x++)
4605 for (y = 0; y < level->fieldy; y++)
4606 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4608 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4610 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4611 header->LevelTitle[i] = level->name[i];
4612 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4614 header->InfotronsNeeded = level->gems_needed;
4616 header->SpecialPortCount = 0;
4618 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4620 boolean gravity_port_found = FALSE;
4621 boolean gravity_port_valid = FALSE;
4622 int gravity_port_flag;
4623 int gravity_port_base_element;
4624 int element = level->field[x][y];
4626 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4627 element <= EL_SP_GRAVITY_ON_PORT_UP)
4629 gravity_port_found = TRUE;
4630 gravity_port_valid = TRUE;
4631 gravity_port_flag = 1;
4632 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4634 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4635 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4637 gravity_port_found = TRUE;
4638 gravity_port_valid = TRUE;
4639 gravity_port_flag = 0;
4640 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4642 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4643 element <= EL_SP_GRAVITY_PORT_UP)
4645 // change R'n'D style gravity inverting special port to normal port
4646 // (there are no gravity inverting ports in native Supaplex engine)
4648 gravity_port_found = TRUE;
4649 gravity_port_valid = FALSE;
4650 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4653 if (gravity_port_found)
4655 if (gravity_port_valid &&
4656 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4658 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4660 port->PortLocation = (y * level->fieldx + x) * 2;
4661 port->Gravity = gravity_port_flag;
4663 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4665 header->SpecialPortCount++;
4669 // change special gravity port to normal port
4671 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4674 level_sp->playfield[x][y] = element - EL_SP_START;
4679 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4681 struct LevelInfo_SP *level_sp = level->native_sp_level;
4682 LevelInfoType *header = &level_sp->header;
4683 boolean num_invalid_elements = 0;
4686 level->fieldx = level_sp->width;
4687 level->fieldy = level_sp->height;
4689 for (x = 0; x < level->fieldx; x++)
4691 for (y = 0; y < level->fieldy; y++)
4693 int element_old = level_sp->playfield[x][y];
4694 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4696 if (element_new == EL_UNKNOWN)
4698 num_invalid_elements++;
4700 Debug("level:native:SP", "invalid element %d at position %d, %d",
4704 level->field[x][y] = element_new;
4708 if (num_invalid_elements > 0)
4709 Warn("found %d invalid elements%s", num_invalid_elements,
4710 (!options.debug ? " (use '--debug' for more details)" : ""));
4712 for (i = 0; i < MAX_PLAYERS; i++)
4713 level->initial_player_gravity[i] =
4714 (header->InitialGravity == 1 ? TRUE : FALSE);
4716 // skip leading spaces
4717 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4718 if (header->LevelTitle[i] != ' ')
4722 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4723 level->name[j] = header->LevelTitle[i];
4724 level->name[j] = '\0';
4726 // cut trailing spaces
4728 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4729 level->name[j - 1] = '\0';
4731 level->gems_needed = header->InfotronsNeeded;
4733 for (i = 0; i < header->SpecialPortCount; i++)
4735 SpecialPortType *port = &header->SpecialPort[i];
4736 int port_location = port->PortLocation;
4737 int gravity = port->Gravity;
4738 int port_x, port_y, port_element;
4740 port_x = (port_location / 2) % level->fieldx;
4741 port_y = (port_location / 2) / level->fieldx;
4743 if (port_x < 0 || port_x >= level->fieldx ||
4744 port_y < 0 || port_y >= level->fieldy)
4746 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4751 port_element = level->field[port_x][port_y];
4753 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4754 port_element > EL_SP_GRAVITY_PORT_UP)
4756 Warn("no special port at position (%d, %d)", port_x, port_y);
4761 // change previous (wrong) gravity inverting special port to either
4762 // gravity enabling special port or gravity disabling special port
4763 level->field[port_x][port_y] +=
4764 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4765 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4768 // change special gravity ports without database entries to normal ports
4769 for (x = 0; x < level->fieldx; x++)
4770 for (y = 0; y < level->fieldy; y++)
4771 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4772 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4773 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4775 level->time = 0; // no time limit
4776 level->amoeba_speed = 0;
4777 level->time_magic_wall = 0;
4778 level->time_wheel = 0;
4779 level->amoeba_content = EL_EMPTY;
4781 // original Supaplex does not use score values -- rate by playing time
4782 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4783 level->score[i] = 0;
4785 level->rate_time_over_score = TRUE;
4787 // there are no yamyams in supaplex levels
4788 for (i = 0; i < level->num_yamyam_contents; i++)
4789 for (x = 0; x < 3; x++)
4790 for (y = 0; y < 3; y++)
4791 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4794 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4796 struct LevelInfo_SP *level_sp = level->native_sp_level;
4797 struct DemoInfo_SP *demo = &level_sp->demo;
4800 // always start with reliable default values
4801 demo->is_available = FALSE;
4804 if (TAPE_IS_EMPTY(tape))
4807 demo->level_nr = tape.level_nr; // (currently not used)
4809 level_sp->header.DemoRandomSeed = tape.random_seed;
4813 for (i = 0; i < tape.length; i++)
4815 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4816 int demo_repeat = tape.pos[i].delay;
4817 int demo_entries = (demo_repeat + 15) / 16;
4819 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4821 Warn("tape truncated: size exceeds maximum SP demo size %d",
4827 for (j = 0; j < demo_repeat / 16; j++)
4828 demo->data[demo->length++] = 0xf0 | demo_action;
4830 if (demo_repeat % 16)
4831 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4834 demo->is_available = TRUE;
4837 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4839 struct LevelInfo_SP *level_sp = level->native_sp_level;
4840 struct DemoInfo_SP *demo = &level_sp->demo;
4841 char *filename = level->file_info.filename;
4844 // always start with reliable default values
4845 setTapeInfoToDefaults();
4847 if (!demo->is_available)
4850 tape.level_nr = demo->level_nr; // (currently not used)
4851 tape.random_seed = level_sp->header.DemoRandomSeed;
4853 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4856 tape.pos[tape.counter].delay = 0;
4858 for (i = 0; i < demo->length; i++)
4860 int demo_action = demo->data[i] & 0x0f;
4861 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4862 int tape_action = map_key_SP_to_RND(demo_action);
4863 int tape_repeat = demo_repeat + 1;
4864 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4865 boolean success = 0;
4868 for (j = 0; j < tape_repeat; j++)
4869 success = TapeAddAction(action);
4873 Warn("SP demo truncated: size exceeds maximum tape size %d",
4880 TapeHaltRecording();
4884 // ----------------------------------------------------------------------------
4885 // functions for loading MM level
4886 // ----------------------------------------------------------------------------
4888 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4890 struct LevelInfo_MM *level_mm = level->native_mm_level;
4893 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4894 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4896 level_mm->time = level->time;
4897 level_mm->kettles_needed = level->gems_needed;
4898 level_mm->auto_count_kettles = level->auto_count_gems;
4900 level_mm->mm_laser_red = level->mm_laser_red;
4901 level_mm->mm_laser_green = level->mm_laser_green;
4902 level_mm->mm_laser_blue = level->mm_laser_blue;
4904 level_mm->df_laser_red = level->df_laser_red;
4905 level_mm->df_laser_green = level->df_laser_green;
4906 level_mm->df_laser_blue = level->df_laser_blue;
4908 strcpy(level_mm->name, level->name);
4909 strcpy(level_mm->author, level->author);
4911 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4912 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4913 level_mm->score[SC_KEY] = level->score[SC_KEY];
4914 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4915 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4917 level_mm->amoeba_speed = level->amoeba_speed;
4918 level_mm->time_fuse = level->mm_time_fuse;
4919 level_mm->time_bomb = level->mm_time_bomb;
4920 level_mm->time_ball = level->mm_time_ball;
4921 level_mm->time_block = level->mm_time_block;
4923 level_mm->num_ball_contents = level->num_mm_ball_contents;
4924 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4925 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4926 level_mm->explode_ball = level->explode_mm_ball;
4928 for (i = 0; i < level->num_mm_ball_contents; i++)
4929 level_mm->ball_content[i] =
4930 map_element_RND_to_MM(level->mm_ball_content[i]);
4932 for (x = 0; x < level->fieldx; x++)
4933 for (y = 0; y < level->fieldy; y++)
4935 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4938 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4940 struct LevelInfo_MM *level_mm = level->native_mm_level;
4943 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4944 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4946 level->time = level_mm->time;
4947 level->gems_needed = level_mm->kettles_needed;
4948 level->auto_count_gems = level_mm->auto_count_kettles;
4950 level->mm_laser_red = level_mm->mm_laser_red;
4951 level->mm_laser_green = level_mm->mm_laser_green;
4952 level->mm_laser_blue = level_mm->mm_laser_blue;
4954 level->df_laser_red = level_mm->df_laser_red;
4955 level->df_laser_green = level_mm->df_laser_green;
4956 level->df_laser_blue = level_mm->df_laser_blue;
4958 strcpy(level->name, level_mm->name);
4960 // only overwrite author from 'levelinfo.conf' if author defined in level
4961 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4962 strcpy(level->author, level_mm->author);
4964 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4965 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4966 level->score[SC_KEY] = level_mm->score[SC_KEY];
4967 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4968 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4970 level->amoeba_speed = level_mm->amoeba_speed;
4971 level->mm_time_fuse = level_mm->time_fuse;
4972 level->mm_time_bomb = level_mm->time_bomb;
4973 level->mm_time_ball = level_mm->time_ball;
4974 level->mm_time_block = level_mm->time_block;
4976 level->num_mm_ball_contents = level_mm->num_ball_contents;
4977 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4978 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4979 level->explode_mm_ball = level_mm->explode_ball;
4981 for (i = 0; i < level->num_mm_ball_contents; i++)
4982 level->mm_ball_content[i] =
4983 map_element_MM_to_RND(level_mm->ball_content[i]);
4985 for (x = 0; x < level->fieldx; x++)
4986 for (y = 0; y < level->fieldy; y++)
4987 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4991 // ----------------------------------------------------------------------------
4992 // functions for loading DC level
4993 // ----------------------------------------------------------------------------
4995 #define DC_LEVEL_HEADER_SIZE 344
4997 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5000 static int last_data_encoded;
5004 int diff_hi, diff_lo;
5005 int data_hi, data_lo;
5006 unsigned short data_decoded;
5010 last_data_encoded = 0;
5017 diff = data_encoded - last_data_encoded;
5018 diff_hi = diff & ~0xff;
5019 diff_lo = diff & 0xff;
5023 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5024 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5025 data_hi = data_hi & 0xff00;
5027 data_decoded = data_hi | data_lo;
5029 last_data_encoded = data_encoded;
5031 offset1 = (offset1 + 1) % 31;
5032 offset2 = offset2 & 0xff;
5034 return data_decoded;
5037 static int getMappedElement_DC(int element)
5045 // 0x0117 - 0x036e: (?)
5048 // 0x042d - 0x0684: (?)
5064 element = EL_CRYSTAL;
5067 case 0x0e77: // quicksand (boulder)
5068 element = EL_QUICKSAND_FAST_FULL;
5071 case 0x0e99: // slow quicksand (boulder)
5072 element = EL_QUICKSAND_FULL;
5076 element = EL_EM_EXIT_OPEN;
5080 element = EL_EM_EXIT_CLOSED;
5084 element = EL_EM_STEEL_EXIT_OPEN;
5088 element = EL_EM_STEEL_EXIT_CLOSED;
5091 case 0x0f4f: // dynamite (lit 1)
5092 element = EL_EM_DYNAMITE_ACTIVE;
5095 case 0x0f57: // dynamite (lit 2)
5096 element = EL_EM_DYNAMITE_ACTIVE;
5099 case 0x0f5f: // dynamite (lit 3)
5100 element = EL_EM_DYNAMITE_ACTIVE;
5103 case 0x0f67: // dynamite (lit 4)
5104 element = EL_EM_DYNAMITE_ACTIVE;
5111 element = EL_AMOEBA_WET;
5115 element = EL_AMOEBA_DROP;
5119 element = EL_DC_MAGIC_WALL;
5123 element = EL_SPACESHIP_UP;
5127 element = EL_SPACESHIP_DOWN;
5131 element = EL_SPACESHIP_LEFT;
5135 element = EL_SPACESHIP_RIGHT;
5139 element = EL_BUG_UP;
5143 element = EL_BUG_DOWN;
5147 element = EL_BUG_LEFT;
5151 element = EL_BUG_RIGHT;
5155 element = EL_MOLE_UP;
5159 element = EL_MOLE_DOWN;
5163 element = EL_MOLE_LEFT;
5167 element = EL_MOLE_RIGHT;
5175 element = EL_YAMYAM_UP;
5179 element = EL_SWITCHGATE_OPEN;
5183 element = EL_SWITCHGATE_CLOSED;
5187 element = EL_DC_SWITCHGATE_SWITCH_UP;
5191 element = EL_TIMEGATE_CLOSED;
5194 case 0x144c: // conveyor belt switch (green)
5195 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5198 case 0x144f: // conveyor belt switch (red)
5199 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5202 case 0x1452: // conveyor belt switch (blue)
5203 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5207 element = EL_CONVEYOR_BELT_3_MIDDLE;
5211 element = EL_CONVEYOR_BELT_3_LEFT;
5215 element = EL_CONVEYOR_BELT_3_RIGHT;
5219 element = EL_CONVEYOR_BELT_1_MIDDLE;
5223 element = EL_CONVEYOR_BELT_1_LEFT;
5227 element = EL_CONVEYOR_BELT_1_RIGHT;
5231 element = EL_CONVEYOR_BELT_4_MIDDLE;
5235 element = EL_CONVEYOR_BELT_4_LEFT;
5239 element = EL_CONVEYOR_BELT_4_RIGHT;
5243 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5247 element = EL_EXPANDABLE_WALL_VERTICAL;
5251 element = EL_EXPANDABLE_WALL_ANY;
5254 case 0x14ce: // growing steel wall (left/right)
5255 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5258 case 0x14df: // growing steel wall (up/down)
5259 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5262 case 0x14e8: // growing steel wall (up/down/left/right)
5263 element = EL_EXPANDABLE_STEELWALL_ANY;
5267 element = EL_SHIELD_DEADLY;
5271 element = EL_EXTRA_TIME;
5279 element = EL_EMPTY_SPACE;
5282 case 0x1578: // quicksand (empty)
5283 element = EL_QUICKSAND_FAST_EMPTY;
5286 case 0x1579: // slow quicksand (empty)
5287 element = EL_QUICKSAND_EMPTY;
5297 element = EL_EM_DYNAMITE;
5300 case 0x15a1: // key (red)
5301 element = EL_EM_KEY_1;
5304 case 0x15a2: // key (yellow)
5305 element = EL_EM_KEY_2;
5308 case 0x15a3: // key (blue)
5309 element = EL_EM_KEY_4;
5312 case 0x15a4: // key (green)
5313 element = EL_EM_KEY_3;
5316 case 0x15a5: // key (white)
5317 element = EL_DC_KEY_WHITE;
5321 element = EL_WALL_SLIPPERY;
5328 case 0x15a8: // wall (not round)
5332 case 0x15a9: // (blue)
5333 element = EL_CHAR_A;
5336 case 0x15aa: // (blue)
5337 element = EL_CHAR_B;
5340 case 0x15ab: // (blue)
5341 element = EL_CHAR_C;
5344 case 0x15ac: // (blue)
5345 element = EL_CHAR_D;
5348 case 0x15ad: // (blue)
5349 element = EL_CHAR_E;
5352 case 0x15ae: // (blue)
5353 element = EL_CHAR_F;
5356 case 0x15af: // (blue)
5357 element = EL_CHAR_G;
5360 case 0x15b0: // (blue)
5361 element = EL_CHAR_H;
5364 case 0x15b1: // (blue)
5365 element = EL_CHAR_I;
5368 case 0x15b2: // (blue)
5369 element = EL_CHAR_J;
5372 case 0x15b3: // (blue)
5373 element = EL_CHAR_K;
5376 case 0x15b4: // (blue)
5377 element = EL_CHAR_L;
5380 case 0x15b5: // (blue)
5381 element = EL_CHAR_M;
5384 case 0x15b6: // (blue)
5385 element = EL_CHAR_N;
5388 case 0x15b7: // (blue)
5389 element = EL_CHAR_O;
5392 case 0x15b8: // (blue)
5393 element = EL_CHAR_P;
5396 case 0x15b9: // (blue)
5397 element = EL_CHAR_Q;
5400 case 0x15ba: // (blue)
5401 element = EL_CHAR_R;
5404 case 0x15bb: // (blue)
5405 element = EL_CHAR_S;
5408 case 0x15bc: // (blue)
5409 element = EL_CHAR_T;
5412 case 0x15bd: // (blue)
5413 element = EL_CHAR_U;
5416 case 0x15be: // (blue)
5417 element = EL_CHAR_V;
5420 case 0x15bf: // (blue)
5421 element = EL_CHAR_W;
5424 case 0x15c0: // (blue)
5425 element = EL_CHAR_X;
5428 case 0x15c1: // (blue)
5429 element = EL_CHAR_Y;
5432 case 0x15c2: // (blue)
5433 element = EL_CHAR_Z;
5436 case 0x15c3: // (blue)
5437 element = EL_CHAR_AUMLAUT;
5440 case 0x15c4: // (blue)
5441 element = EL_CHAR_OUMLAUT;
5444 case 0x15c5: // (blue)
5445 element = EL_CHAR_UUMLAUT;
5448 case 0x15c6: // (blue)
5449 element = EL_CHAR_0;
5452 case 0x15c7: // (blue)
5453 element = EL_CHAR_1;
5456 case 0x15c8: // (blue)
5457 element = EL_CHAR_2;
5460 case 0x15c9: // (blue)
5461 element = EL_CHAR_3;
5464 case 0x15ca: // (blue)
5465 element = EL_CHAR_4;
5468 case 0x15cb: // (blue)
5469 element = EL_CHAR_5;
5472 case 0x15cc: // (blue)
5473 element = EL_CHAR_6;
5476 case 0x15cd: // (blue)
5477 element = EL_CHAR_7;
5480 case 0x15ce: // (blue)
5481 element = EL_CHAR_8;
5484 case 0x15cf: // (blue)
5485 element = EL_CHAR_9;
5488 case 0x15d0: // (blue)
5489 element = EL_CHAR_PERIOD;
5492 case 0x15d1: // (blue)
5493 element = EL_CHAR_EXCLAM;
5496 case 0x15d2: // (blue)
5497 element = EL_CHAR_COLON;
5500 case 0x15d3: // (blue)
5501 element = EL_CHAR_LESS;
5504 case 0x15d4: // (blue)
5505 element = EL_CHAR_GREATER;
5508 case 0x15d5: // (blue)
5509 element = EL_CHAR_QUESTION;
5512 case 0x15d6: // (blue)
5513 element = EL_CHAR_COPYRIGHT;
5516 case 0x15d7: // (blue)
5517 element = EL_CHAR_UP;
5520 case 0x15d8: // (blue)
5521 element = EL_CHAR_DOWN;
5524 case 0x15d9: // (blue)
5525 element = EL_CHAR_BUTTON;
5528 case 0x15da: // (blue)
5529 element = EL_CHAR_PLUS;
5532 case 0x15db: // (blue)
5533 element = EL_CHAR_MINUS;
5536 case 0x15dc: // (blue)
5537 element = EL_CHAR_APOSTROPHE;
5540 case 0x15dd: // (blue)
5541 element = EL_CHAR_PARENLEFT;
5544 case 0x15de: // (blue)
5545 element = EL_CHAR_PARENRIGHT;
5548 case 0x15df: // (green)
5549 element = EL_CHAR_A;
5552 case 0x15e0: // (green)
5553 element = EL_CHAR_B;
5556 case 0x15e1: // (green)
5557 element = EL_CHAR_C;
5560 case 0x15e2: // (green)
5561 element = EL_CHAR_D;
5564 case 0x15e3: // (green)
5565 element = EL_CHAR_E;
5568 case 0x15e4: // (green)
5569 element = EL_CHAR_F;
5572 case 0x15e5: // (green)
5573 element = EL_CHAR_G;
5576 case 0x15e6: // (green)
5577 element = EL_CHAR_H;
5580 case 0x15e7: // (green)
5581 element = EL_CHAR_I;
5584 case 0x15e8: // (green)
5585 element = EL_CHAR_J;
5588 case 0x15e9: // (green)
5589 element = EL_CHAR_K;
5592 case 0x15ea: // (green)
5593 element = EL_CHAR_L;
5596 case 0x15eb: // (green)
5597 element = EL_CHAR_M;
5600 case 0x15ec: // (green)
5601 element = EL_CHAR_N;
5604 case 0x15ed: // (green)
5605 element = EL_CHAR_O;
5608 case 0x15ee: // (green)
5609 element = EL_CHAR_P;
5612 case 0x15ef: // (green)
5613 element = EL_CHAR_Q;
5616 case 0x15f0: // (green)
5617 element = EL_CHAR_R;
5620 case 0x15f1: // (green)
5621 element = EL_CHAR_S;
5624 case 0x15f2: // (green)
5625 element = EL_CHAR_T;
5628 case 0x15f3: // (green)
5629 element = EL_CHAR_U;
5632 case 0x15f4: // (green)
5633 element = EL_CHAR_V;
5636 case 0x15f5: // (green)
5637 element = EL_CHAR_W;
5640 case 0x15f6: // (green)
5641 element = EL_CHAR_X;
5644 case 0x15f7: // (green)
5645 element = EL_CHAR_Y;
5648 case 0x15f8: // (green)
5649 element = EL_CHAR_Z;
5652 case 0x15f9: // (green)
5653 element = EL_CHAR_AUMLAUT;
5656 case 0x15fa: // (green)
5657 element = EL_CHAR_OUMLAUT;
5660 case 0x15fb: // (green)
5661 element = EL_CHAR_UUMLAUT;
5664 case 0x15fc: // (green)
5665 element = EL_CHAR_0;
5668 case 0x15fd: // (green)
5669 element = EL_CHAR_1;
5672 case 0x15fe: // (green)
5673 element = EL_CHAR_2;
5676 case 0x15ff: // (green)
5677 element = EL_CHAR_3;
5680 case 0x1600: // (green)
5681 element = EL_CHAR_4;
5684 case 0x1601: // (green)
5685 element = EL_CHAR_5;
5688 case 0x1602: // (green)
5689 element = EL_CHAR_6;
5692 case 0x1603: // (green)
5693 element = EL_CHAR_7;
5696 case 0x1604: // (green)
5697 element = EL_CHAR_8;
5700 case 0x1605: // (green)
5701 element = EL_CHAR_9;
5704 case 0x1606: // (green)
5705 element = EL_CHAR_PERIOD;
5708 case 0x1607: // (green)
5709 element = EL_CHAR_EXCLAM;
5712 case 0x1608: // (green)
5713 element = EL_CHAR_COLON;
5716 case 0x1609: // (green)
5717 element = EL_CHAR_LESS;
5720 case 0x160a: // (green)
5721 element = EL_CHAR_GREATER;
5724 case 0x160b: // (green)
5725 element = EL_CHAR_QUESTION;
5728 case 0x160c: // (green)
5729 element = EL_CHAR_COPYRIGHT;
5732 case 0x160d: // (green)
5733 element = EL_CHAR_UP;
5736 case 0x160e: // (green)
5737 element = EL_CHAR_DOWN;
5740 case 0x160f: // (green)
5741 element = EL_CHAR_BUTTON;
5744 case 0x1610: // (green)
5745 element = EL_CHAR_PLUS;
5748 case 0x1611: // (green)
5749 element = EL_CHAR_MINUS;
5752 case 0x1612: // (green)
5753 element = EL_CHAR_APOSTROPHE;
5756 case 0x1613: // (green)
5757 element = EL_CHAR_PARENLEFT;
5760 case 0x1614: // (green)
5761 element = EL_CHAR_PARENRIGHT;
5764 case 0x1615: // (blue steel)
5765 element = EL_STEEL_CHAR_A;
5768 case 0x1616: // (blue steel)
5769 element = EL_STEEL_CHAR_B;
5772 case 0x1617: // (blue steel)
5773 element = EL_STEEL_CHAR_C;
5776 case 0x1618: // (blue steel)
5777 element = EL_STEEL_CHAR_D;
5780 case 0x1619: // (blue steel)
5781 element = EL_STEEL_CHAR_E;
5784 case 0x161a: // (blue steel)
5785 element = EL_STEEL_CHAR_F;
5788 case 0x161b: // (blue steel)
5789 element = EL_STEEL_CHAR_G;
5792 case 0x161c: // (blue steel)
5793 element = EL_STEEL_CHAR_H;
5796 case 0x161d: // (blue steel)
5797 element = EL_STEEL_CHAR_I;
5800 case 0x161e: // (blue steel)
5801 element = EL_STEEL_CHAR_J;
5804 case 0x161f: // (blue steel)
5805 element = EL_STEEL_CHAR_K;
5808 case 0x1620: // (blue steel)
5809 element = EL_STEEL_CHAR_L;
5812 case 0x1621: // (blue steel)
5813 element = EL_STEEL_CHAR_M;
5816 case 0x1622: // (blue steel)
5817 element = EL_STEEL_CHAR_N;
5820 case 0x1623: // (blue steel)
5821 element = EL_STEEL_CHAR_O;
5824 case 0x1624: // (blue steel)
5825 element = EL_STEEL_CHAR_P;
5828 case 0x1625: // (blue steel)
5829 element = EL_STEEL_CHAR_Q;
5832 case 0x1626: // (blue steel)
5833 element = EL_STEEL_CHAR_R;
5836 case 0x1627: // (blue steel)
5837 element = EL_STEEL_CHAR_S;
5840 case 0x1628: // (blue steel)
5841 element = EL_STEEL_CHAR_T;
5844 case 0x1629: // (blue steel)
5845 element = EL_STEEL_CHAR_U;
5848 case 0x162a: // (blue steel)
5849 element = EL_STEEL_CHAR_V;
5852 case 0x162b: // (blue steel)
5853 element = EL_STEEL_CHAR_W;
5856 case 0x162c: // (blue steel)
5857 element = EL_STEEL_CHAR_X;
5860 case 0x162d: // (blue steel)
5861 element = EL_STEEL_CHAR_Y;
5864 case 0x162e: // (blue steel)
5865 element = EL_STEEL_CHAR_Z;
5868 case 0x162f: // (blue steel)
5869 element = EL_STEEL_CHAR_AUMLAUT;
5872 case 0x1630: // (blue steel)
5873 element = EL_STEEL_CHAR_OUMLAUT;
5876 case 0x1631: // (blue steel)
5877 element = EL_STEEL_CHAR_UUMLAUT;
5880 case 0x1632: // (blue steel)
5881 element = EL_STEEL_CHAR_0;
5884 case 0x1633: // (blue steel)
5885 element = EL_STEEL_CHAR_1;
5888 case 0x1634: // (blue steel)
5889 element = EL_STEEL_CHAR_2;
5892 case 0x1635: // (blue steel)
5893 element = EL_STEEL_CHAR_3;
5896 case 0x1636: // (blue steel)
5897 element = EL_STEEL_CHAR_4;
5900 case 0x1637: // (blue steel)
5901 element = EL_STEEL_CHAR_5;
5904 case 0x1638: // (blue steel)
5905 element = EL_STEEL_CHAR_6;
5908 case 0x1639: // (blue steel)
5909 element = EL_STEEL_CHAR_7;
5912 case 0x163a: // (blue steel)
5913 element = EL_STEEL_CHAR_8;
5916 case 0x163b: // (blue steel)
5917 element = EL_STEEL_CHAR_9;
5920 case 0x163c: // (blue steel)
5921 element = EL_STEEL_CHAR_PERIOD;
5924 case 0x163d: // (blue steel)
5925 element = EL_STEEL_CHAR_EXCLAM;
5928 case 0x163e: // (blue steel)
5929 element = EL_STEEL_CHAR_COLON;
5932 case 0x163f: // (blue steel)
5933 element = EL_STEEL_CHAR_LESS;
5936 case 0x1640: // (blue steel)
5937 element = EL_STEEL_CHAR_GREATER;
5940 case 0x1641: // (blue steel)
5941 element = EL_STEEL_CHAR_QUESTION;
5944 case 0x1642: // (blue steel)
5945 element = EL_STEEL_CHAR_COPYRIGHT;
5948 case 0x1643: // (blue steel)
5949 element = EL_STEEL_CHAR_UP;
5952 case 0x1644: // (blue steel)
5953 element = EL_STEEL_CHAR_DOWN;
5956 case 0x1645: // (blue steel)
5957 element = EL_STEEL_CHAR_BUTTON;
5960 case 0x1646: // (blue steel)
5961 element = EL_STEEL_CHAR_PLUS;
5964 case 0x1647: // (blue steel)
5965 element = EL_STEEL_CHAR_MINUS;
5968 case 0x1648: // (blue steel)
5969 element = EL_STEEL_CHAR_APOSTROPHE;
5972 case 0x1649: // (blue steel)
5973 element = EL_STEEL_CHAR_PARENLEFT;
5976 case 0x164a: // (blue steel)
5977 element = EL_STEEL_CHAR_PARENRIGHT;
5980 case 0x164b: // (green steel)
5981 element = EL_STEEL_CHAR_A;
5984 case 0x164c: // (green steel)
5985 element = EL_STEEL_CHAR_B;
5988 case 0x164d: // (green steel)
5989 element = EL_STEEL_CHAR_C;
5992 case 0x164e: // (green steel)
5993 element = EL_STEEL_CHAR_D;
5996 case 0x164f: // (green steel)
5997 element = EL_STEEL_CHAR_E;
6000 case 0x1650: // (green steel)
6001 element = EL_STEEL_CHAR_F;
6004 case 0x1651: // (green steel)
6005 element = EL_STEEL_CHAR_G;
6008 case 0x1652: // (green steel)
6009 element = EL_STEEL_CHAR_H;
6012 case 0x1653: // (green steel)
6013 element = EL_STEEL_CHAR_I;
6016 case 0x1654: // (green steel)
6017 element = EL_STEEL_CHAR_J;
6020 case 0x1655: // (green steel)
6021 element = EL_STEEL_CHAR_K;
6024 case 0x1656: // (green steel)
6025 element = EL_STEEL_CHAR_L;
6028 case 0x1657: // (green steel)
6029 element = EL_STEEL_CHAR_M;
6032 case 0x1658: // (green steel)
6033 element = EL_STEEL_CHAR_N;
6036 case 0x1659: // (green steel)
6037 element = EL_STEEL_CHAR_O;
6040 case 0x165a: // (green steel)
6041 element = EL_STEEL_CHAR_P;
6044 case 0x165b: // (green steel)
6045 element = EL_STEEL_CHAR_Q;
6048 case 0x165c: // (green steel)
6049 element = EL_STEEL_CHAR_R;
6052 case 0x165d: // (green steel)
6053 element = EL_STEEL_CHAR_S;
6056 case 0x165e: // (green steel)
6057 element = EL_STEEL_CHAR_T;
6060 case 0x165f: // (green steel)
6061 element = EL_STEEL_CHAR_U;
6064 case 0x1660: // (green steel)
6065 element = EL_STEEL_CHAR_V;
6068 case 0x1661: // (green steel)
6069 element = EL_STEEL_CHAR_W;
6072 case 0x1662: // (green steel)
6073 element = EL_STEEL_CHAR_X;
6076 case 0x1663: // (green steel)
6077 element = EL_STEEL_CHAR_Y;
6080 case 0x1664: // (green steel)
6081 element = EL_STEEL_CHAR_Z;
6084 case 0x1665: // (green steel)
6085 element = EL_STEEL_CHAR_AUMLAUT;
6088 case 0x1666: // (green steel)
6089 element = EL_STEEL_CHAR_OUMLAUT;
6092 case 0x1667: // (green steel)
6093 element = EL_STEEL_CHAR_UUMLAUT;
6096 case 0x1668: // (green steel)
6097 element = EL_STEEL_CHAR_0;
6100 case 0x1669: // (green steel)
6101 element = EL_STEEL_CHAR_1;
6104 case 0x166a: // (green steel)
6105 element = EL_STEEL_CHAR_2;
6108 case 0x166b: // (green steel)
6109 element = EL_STEEL_CHAR_3;
6112 case 0x166c: // (green steel)
6113 element = EL_STEEL_CHAR_4;
6116 case 0x166d: // (green steel)
6117 element = EL_STEEL_CHAR_5;
6120 case 0x166e: // (green steel)
6121 element = EL_STEEL_CHAR_6;
6124 case 0x166f: // (green steel)
6125 element = EL_STEEL_CHAR_7;
6128 case 0x1670: // (green steel)
6129 element = EL_STEEL_CHAR_8;
6132 case 0x1671: // (green steel)
6133 element = EL_STEEL_CHAR_9;
6136 case 0x1672: // (green steel)
6137 element = EL_STEEL_CHAR_PERIOD;
6140 case 0x1673: // (green steel)
6141 element = EL_STEEL_CHAR_EXCLAM;
6144 case 0x1674: // (green steel)
6145 element = EL_STEEL_CHAR_COLON;
6148 case 0x1675: // (green steel)
6149 element = EL_STEEL_CHAR_LESS;
6152 case 0x1676: // (green steel)
6153 element = EL_STEEL_CHAR_GREATER;
6156 case 0x1677: // (green steel)
6157 element = EL_STEEL_CHAR_QUESTION;
6160 case 0x1678: // (green steel)
6161 element = EL_STEEL_CHAR_COPYRIGHT;
6164 case 0x1679: // (green steel)
6165 element = EL_STEEL_CHAR_UP;
6168 case 0x167a: // (green steel)
6169 element = EL_STEEL_CHAR_DOWN;
6172 case 0x167b: // (green steel)
6173 element = EL_STEEL_CHAR_BUTTON;
6176 case 0x167c: // (green steel)
6177 element = EL_STEEL_CHAR_PLUS;
6180 case 0x167d: // (green steel)
6181 element = EL_STEEL_CHAR_MINUS;
6184 case 0x167e: // (green steel)
6185 element = EL_STEEL_CHAR_APOSTROPHE;
6188 case 0x167f: // (green steel)
6189 element = EL_STEEL_CHAR_PARENLEFT;
6192 case 0x1680: // (green steel)
6193 element = EL_STEEL_CHAR_PARENRIGHT;
6196 case 0x1681: // gate (red)
6197 element = EL_EM_GATE_1;
6200 case 0x1682: // secret gate (red)
6201 element = EL_EM_GATE_1_GRAY;
6204 case 0x1683: // gate (yellow)
6205 element = EL_EM_GATE_2;
6208 case 0x1684: // secret gate (yellow)
6209 element = EL_EM_GATE_2_GRAY;
6212 case 0x1685: // gate (blue)
6213 element = EL_EM_GATE_4;
6216 case 0x1686: // secret gate (blue)
6217 element = EL_EM_GATE_4_GRAY;
6220 case 0x1687: // gate (green)
6221 element = EL_EM_GATE_3;
6224 case 0x1688: // secret gate (green)
6225 element = EL_EM_GATE_3_GRAY;
6228 case 0x1689: // gate (white)
6229 element = EL_DC_GATE_WHITE;
6232 case 0x168a: // secret gate (white)
6233 element = EL_DC_GATE_WHITE_GRAY;
6236 case 0x168b: // secret gate (no key)
6237 element = EL_DC_GATE_FAKE_GRAY;
6241 element = EL_ROBOT_WHEEL;
6245 element = EL_DC_TIMEGATE_SWITCH;
6249 element = EL_ACID_POOL_BOTTOM;
6253 element = EL_ACID_POOL_TOPLEFT;
6257 element = EL_ACID_POOL_TOPRIGHT;
6261 element = EL_ACID_POOL_BOTTOMLEFT;
6265 element = EL_ACID_POOL_BOTTOMRIGHT;
6269 element = EL_STEELWALL;
6273 element = EL_STEELWALL_SLIPPERY;
6276 case 0x1695: // steel wall (not round)
6277 element = EL_STEELWALL;
6280 case 0x1696: // steel wall (left)
6281 element = EL_DC_STEELWALL_1_LEFT;
6284 case 0x1697: // steel wall (bottom)
6285 element = EL_DC_STEELWALL_1_BOTTOM;
6288 case 0x1698: // steel wall (right)
6289 element = EL_DC_STEELWALL_1_RIGHT;
6292 case 0x1699: // steel wall (top)
6293 element = EL_DC_STEELWALL_1_TOP;
6296 case 0x169a: // steel wall (left/bottom)
6297 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6300 case 0x169b: // steel wall (right/bottom)
6301 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6304 case 0x169c: // steel wall (right/top)
6305 element = EL_DC_STEELWALL_1_TOPRIGHT;
6308 case 0x169d: // steel wall (left/top)
6309 element = EL_DC_STEELWALL_1_TOPLEFT;
6312 case 0x169e: // steel wall (right/bottom small)
6313 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6316 case 0x169f: // steel wall (left/bottom small)
6317 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6320 case 0x16a0: // steel wall (right/top small)
6321 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6324 case 0x16a1: // steel wall (left/top small)
6325 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6328 case 0x16a2: // steel wall (left/right)
6329 element = EL_DC_STEELWALL_1_VERTICAL;
6332 case 0x16a3: // steel wall (top/bottom)
6333 element = EL_DC_STEELWALL_1_HORIZONTAL;
6336 case 0x16a4: // steel wall 2 (left end)
6337 element = EL_DC_STEELWALL_2_LEFT;
6340 case 0x16a5: // steel wall 2 (right end)
6341 element = EL_DC_STEELWALL_2_RIGHT;
6344 case 0x16a6: // steel wall 2 (top end)
6345 element = EL_DC_STEELWALL_2_TOP;
6348 case 0x16a7: // steel wall 2 (bottom end)
6349 element = EL_DC_STEELWALL_2_BOTTOM;
6352 case 0x16a8: // steel wall 2 (left/right)
6353 element = EL_DC_STEELWALL_2_HORIZONTAL;
6356 case 0x16a9: // steel wall 2 (up/down)
6357 element = EL_DC_STEELWALL_2_VERTICAL;
6360 case 0x16aa: // steel wall 2 (mid)
6361 element = EL_DC_STEELWALL_2_MIDDLE;
6365 element = EL_SIGN_EXCLAMATION;
6369 element = EL_SIGN_RADIOACTIVITY;
6373 element = EL_SIGN_STOP;
6377 element = EL_SIGN_WHEELCHAIR;
6381 element = EL_SIGN_PARKING;
6385 element = EL_SIGN_NO_ENTRY;
6389 element = EL_SIGN_HEART;
6393 element = EL_SIGN_GIVE_WAY;
6397 element = EL_SIGN_ENTRY_FORBIDDEN;
6401 element = EL_SIGN_EMERGENCY_EXIT;
6405 element = EL_SIGN_YIN_YANG;
6409 element = EL_WALL_EMERALD;
6413 element = EL_WALL_DIAMOND;
6417 element = EL_WALL_PEARL;
6421 element = EL_WALL_CRYSTAL;
6425 element = EL_INVISIBLE_WALL;
6429 element = EL_INVISIBLE_STEELWALL;
6433 // EL_INVISIBLE_SAND
6436 element = EL_LIGHT_SWITCH;
6440 element = EL_ENVELOPE_1;
6444 if (element >= 0x0117 && element <= 0x036e) // (?)
6445 element = EL_DIAMOND;
6446 else if (element >= 0x042d && element <= 0x0684) // (?)
6447 element = EL_EMERALD;
6448 else if (element >= 0x157c && element <= 0x158b)
6450 else if (element >= 0x1590 && element <= 0x159f)
6451 element = EL_DC_LANDMINE;
6452 else if (element >= 0x16bc && element <= 0x16cb)
6453 element = EL_INVISIBLE_SAND;
6456 Warn("unknown Diamond Caves element 0x%04x", element);
6458 element = EL_UNKNOWN;
6463 return getMappedElement(element);
6466 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6468 byte header[DC_LEVEL_HEADER_SIZE];
6470 int envelope_header_pos = 62;
6471 int envelope_content_pos = 94;
6472 int level_name_pos = 251;
6473 int level_author_pos = 292;
6474 int envelope_header_len;
6475 int envelope_content_len;
6477 int level_author_len;
6479 int num_yamyam_contents;
6482 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6484 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6486 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6488 header[i * 2 + 0] = header_word >> 8;
6489 header[i * 2 + 1] = header_word & 0xff;
6492 // read some values from level header to check level decoding integrity
6493 fieldx = header[6] | (header[7] << 8);
6494 fieldy = header[8] | (header[9] << 8);
6495 num_yamyam_contents = header[60] | (header[61] << 8);
6497 // do some simple sanity checks to ensure that level was correctly decoded
6498 if (fieldx < 1 || fieldx > 256 ||
6499 fieldy < 1 || fieldy > 256 ||
6500 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6502 level->no_valid_file = TRUE;
6504 Warn("cannot decode level from stream -- using empty level");
6509 // maximum envelope header size is 31 bytes
6510 envelope_header_len = header[envelope_header_pos];
6511 // maximum envelope content size is 110 (156?) bytes
6512 envelope_content_len = header[envelope_content_pos];
6514 // maximum level title size is 40 bytes
6515 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6516 // maximum level author size is 30 (51?) bytes
6517 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6521 for (i = 0; i < envelope_header_len; i++)
6522 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6523 level->envelope[0].text[envelope_size++] =
6524 header[envelope_header_pos + 1 + i];
6526 if (envelope_header_len > 0 && envelope_content_len > 0)
6528 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6529 level->envelope[0].text[envelope_size++] = '\n';
6530 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6531 level->envelope[0].text[envelope_size++] = '\n';
6534 for (i = 0; i < envelope_content_len; i++)
6535 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6536 level->envelope[0].text[envelope_size++] =
6537 header[envelope_content_pos + 1 + i];
6539 level->envelope[0].text[envelope_size] = '\0';
6541 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6542 level->envelope[0].ysize = 10;
6543 level->envelope[0].autowrap = TRUE;
6544 level->envelope[0].centered = TRUE;
6546 for (i = 0; i < level_name_len; i++)
6547 level->name[i] = header[level_name_pos + 1 + i];
6548 level->name[level_name_len] = '\0';
6550 for (i = 0; i < level_author_len; i++)
6551 level->author[i] = header[level_author_pos + 1 + i];
6552 level->author[level_author_len] = '\0';
6554 num_yamyam_contents = header[60] | (header[61] << 8);
6555 level->num_yamyam_contents =
6556 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6558 for (i = 0; i < num_yamyam_contents; i++)
6560 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6562 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6563 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6565 if (i < MAX_ELEMENT_CONTENTS)
6566 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6570 fieldx = header[6] | (header[7] << 8);
6571 fieldy = header[8] | (header[9] << 8);
6572 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6573 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6575 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6577 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6578 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6580 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6581 level->field[x][y] = getMappedElement_DC(element_dc);
6584 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6585 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6586 level->field[x][y] = EL_PLAYER_1;
6588 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6589 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6590 level->field[x][y] = EL_PLAYER_2;
6592 level->gems_needed = header[18] | (header[19] << 8);
6594 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6595 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6596 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6597 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6598 level->score[SC_NUT] = header[28] | (header[29] << 8);
6599 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6600 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6601 level->score[SC_BUG] = header[34] | (header[35] << 8);
6602 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6603 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6604 level->score[SC_KEY] = header[40] | (header[41] << 8);
6605 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6607 level->time = header[44] | (header[45] << 8);
6609 level->amoeba_speed = header[46] | (header[47] << 8);
6610 level->time_light = header[48] | (header[49] << 8);
6611 level->time_timegate = header[50] | (header[51] << 8);
6612 level->time_wheel = header[52] | (header[53] << 8);
6613 level->time_magic_wall = header[54] | (header[55] << 8);
6614 level->extra_time = header[56] | (header[57] << 8);
6615 level->shield_normal_time = header[58] | (header[59] << 8);
6617 // shield and extra time elements do not have a score
6618 level->score[SC_SHIELD] = 0;
6619 level->extra_time_score = 0;
6621 // set time for normal and deadly shields to the same value
6622 level->shield_deadly_time = level->shield_normal_time;
6624 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6625 // can slip down from flat walls, like normal walls and steel walls
6626 level->em_slippery_gems = TRUE;
6628 // time score is counted for each 10 seconds left in Diamond Caves levels
6629 level->time_score_base = 10;
6632 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6633 struct LevelFileInfo *level_file_info,
6634 boolean level_info_only)
6636 char *filename = level_file_info->filename;
6638 int num_magic_bytes = 8;
6639 char magic_bytes[num_magic_bytes + 1];
6640 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6642 if (!(file = openFile(filename, MODE_READ)))
6644 level->no_valid_file = TRUE;
6646 if (!level_info_only)
6647 Warn("cannot read level '%s' -- using empty level", filename);
6652 // fseek(file, 0x0000, SEEK_SET);
6654 if (level_file_info->packed)
6656 // read "magic bytes" from start of file
6657 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6658 magic_bytes[0] = '\0';
6660 // check "magic bytes" for correct file format
6661 if (!strPrefix(magic_bytes, "DC2"))
6663 level->no_valid_file = TRUE;
6665 Warn("unknown DC level file '%s' -- using empty level", filename);
6670 if (strPrefix(magic_bytes, "DC2Win95") ||
6671 strPrefix(magic_bytes, "DC2Win98"))
6673 int position_first_level = 0x00fa;
6674 int extra_bytes = 4;
6677 // advance file stream to first level inside the level package
6678 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6680 // each block of level data is followed by block of non-level data
6681 num_levels_to_skip *= 2;
6683 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6684 while (num_levels_to_skip >= 0)
6686 // advance file stream to next level inside the level package
6687 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6689 level->no_valid_file = TRUE;
6691 Warn("cannot fseek in file '%s' -- using empty level", filename);
6696 // skip apparently unused extra bytes following each level
6697 ReadUnusedBytesFromFile(file, extra_bytes);
6699 // read size of next level in level package
6700 skip_bytes = getFile32BitLE(file);
6702 num_levels_to_skip--;
6707 level->no_valid_file = TRUE;
6709 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6715 LoadLevelFromFileStream_DC(file, level);
6721 // ----------------------------------------------------------------------------
6722 // functions for loading SB level
6723 // ----------------------------------------------------------------------------
6725 int getMappedElement_SB(int element_ascii, boolean use_ces)
6733 sb_element_mapping[] =
6735 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6736 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6737 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6738 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6739 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6740 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6741 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6742 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6749 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6750 if (element_ascii == sb_element_mapping[i].ascii)
6751 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6753 return EL_UNDEFINED;
6756 static void SetLevelSettings_SB(struct LevelInfo *level)
6760 level->use_step_counter = TRUE;
6763 level->score[SC_TIME_BONUS] = 0;
6764 level->time_score_base = 1;
6765 level->rate_time_over_score = TRUE;
6768 level->auto_exit_sokoban = TRUE;
6771 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6772 struct LevelFileInfo *level_file_info,
6773 boolean level_info_only)
6775 char *filename = level_file_info->filename;
6776 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6777 char last_comment[MAX_LINE_LEN];
6778 char level_name[MAX_LINE_LEN];
6781 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6782 boolean read_continued_line = FALSE;
6783 boolean reading_playfield = FALSE;
6784 boolean got_valid_playfield_line = FALSE;
6785 boolean invalid_playfield_char = FALSE;
6786 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6787 int file_level_nr = 0;
6788 int x = 0, y = 0; // initialized to make compilers happy
6790 last_comment[0] = '\0';
6791 level_name[0] = '\0';
6793 if (!(file = openFile(filename, MODE_READ)))
6795 level->no_valid_file = TRUE;
6797 if (!level_info_only)
6798 Warn("cannot read level '%s' -- using empty level", filename);
6803 while (!checkEndOfFile(file))
6805 // level successfully read, but next level may follow here
6806 if (!got_valid_playfield_line && reading_playfield)
6808 // read playfield from single level file -- skip remaining file
6809 if (!level_file_info->packed)
6812 if (file_level_nr >= num_levels_to_skip)
6817 last_comment[0] = '\0';
6818 level_name[0] = '\0';
6820 reading_playfield = FALSE;
6823 got_valid_playfield_line = FALSE;
6825 // read next line of input file
6826 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6829 // cut trailing line break (this can be newline and/or carriage return)
6830 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6831 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6834 // copy raw input line for later use (mainly debugging output)
6835 strcpy(line_raw, line);
6837 if (read_continued_line)
6839 // append new line to existing line, if there is enough space
6840 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6841 strcat(previous_line, line_ptr);
6843 strcpy(line, previous_line); // copy storage buffer to line
6845 read_continued_line = FALSE;
6848 // if the last character is '\', continue at next line
6849 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6851 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6852 strcpy(previous_line, line); // copy line to storage buffer
6854 read_continued_line = TRUE;
6860 if (line[0] == '\0')
6863 // extract comment text from comment line
6866 for (line_ptr = line; *line_ptr; line_ptr++)
6867 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6870 strcpy(last_comment, line_ptr);
6875 // extract level title text from line containing level title
6876 if (line[0] == '\'')
6878 strcpy(level_name, &line[1]);
6880 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6881 level_name[strlen(level_name) - 1] = '\0';
6886 // skip lines containing only spaces (or empty lines)
6887 for (line_ptr = line; *line_ptr; line_ptr++)
6888 if (*line_ptr != ' ')
6890 if (*line_ptr == '\0')
6893 // at this point, we have found a line containing part of a playfield
6895 got_valid_playfield_line = TRUE;
6897 if (!reading_playfield)
6899 reading_playfield = TRUE;
6900 invalid_playfield_char = FALSE;
6902 for (x = 0; x < MAX_LEV_FIELDX; x++)
6903 for (y = 0; y < MAX_LEV_FIELDY; y++)
6904 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6909 // start with topmost tile row
6913 // skip playfield line if larger row than allowed
6914 if (y >= MAX_LEV_FIELDY)
6917 // start with leftmost tile column
6920 // read playfield elements from line
6921 for (line_ptr = line; *line_ptr; line_ptr++)
6923 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6925 // stop parsing playfield line if larger column than allowed
6926 if (x >= MAX_LEV_FIELDX)
6929 if (mapped_sb_element == EL_UNDEFINED)
6931 invalid_playfield_char = TRUE;
6936 level->field[x][y] = mapped_sb_element;
6938 // continue with next tile column
6941 level->fieldx = MAX(x, level->fieldx);
6944 if (invalid_playfield_char)
6946 // if first playfield line, treat invalid lines as comment lines
6948 reading_playfield = FALSE;
6953 // continue with next tile row
6961 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6962 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6964 if (!reading_playfield)
6966 level->no_valid_file = TRUE;
6968 Warn("cannot read level '%s' -- using empty level", filename);
6973 if (*level_name != '\0')
6975 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6976 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6978 else if (*last_comment != '\0')
6980 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6981 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6985 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6988 // set all empty fields beyond the border walls to invisible steel wall
6989 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6991 if ((x == 0 || x == level->fieldx - 1 ||
6992 y == 0 || y == level->fieldy - 1) &&
6993 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6994 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6995 level->field, level->fieldx, level->fieldy);
6998 // set special level settings for Sokoban levels
6999 SetLevelSettings_SB(level);
7001 if (load_xsb_to_ces)
7003 // special global settings can now be set in level template
7004 level->use_custom_template = TRUE;
7009 // -------------------------------------------------------------------------
7010 // functions for handling native levels
7011 // -------------------------------------------------------------------------
7013 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7014 struct LevelFileInfo *level_file_info,
7015 boolean level_info_only)
7019 // determine position of requested level inside level package
7020 if (level_file_info->packed)
7021 pos = level_file_info->nr - leveldir_current->first_level;
7023 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7024 level->no_valid_file = TRUE;
7027 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7028 struct LevelFileInfo *level_file_info,
7029 boolean level_info_only)
7031 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7032 level->no_valid_file = TRUE;
7035 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7036 struct LevelFileInfo *level_file_info,
7037 boolean level_info_only)
7041 // determine position of requested level inside level package
7042 if (level_file_info->packed)
7043 pos = level_file_info->nr - leveldir_current->first_level;
7045 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7046 level->no_valid_file = TRUE;
7049 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7050 struct LevelFileInfo *level_file_info,
7051 boolean level_info_only)
7053 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7054 level->no_valid_file = TRUE;
7057 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7059 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7060 CopyNativeLevel_RND_to_BD(level);
7061 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7062 CopyNativeLevel_RND_to_EM(level);
7063 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7064 CopyNativeLevel_RND_to_SP(level);
7065 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7066 CopyNativeLevel_RND_to_MM(level);
7069 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7071 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7072 CopyNativeLevel_BD_to_RND(level);
7073 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7074 CopyNativeLevel_EM_to_RND(level);
7075 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7076 CopyNativeLevel_SP_to_RND(level);
7077 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7078 CopyNativeLevel_MM_to_RND(level);
7081 void SaveNativeLevel(struct LevelInfo *level)
7083 // saving native level files only supported for some game engines
7084 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7085 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7088 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7089 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7090 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7091 char *filename = getLevelFilenameFromBasename(basename);
7093 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7096 boolean success = FALSE;
7098 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7100 CopyNativeLevel_RND_to_BD(level);
7101 // CopyNativeTape_RND_to_BD(level);
7103 success = SaveNativeLevel_BD(filename);
7105 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7107 CopyNativeLevel_RND_to_SP(level);
7108 CopyNativeTape_RND_to_SP(level);
7110 success = SaveNativeLevel_SP(filename);
7114 Request("Native level file saved!", REQ_CONFIRM);
7116 Request("Failed to save native level file!", REQ_CONFIRM);
7120 // ----------------------------------------------------------------------------
7121 // functions for loading generic level
7122 // ----------------------------------------------------------------------------
7124 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7125 struct LevelFileInfo *level_file_info,
7126 boolean level_info_only)
7128 // always start with reliable default values
7129 setLevelInfoToDefaults(level, level_info_only, TRUE);
7131 switch (level_file_info->type)
7133 case LEVEL_FILE_TYPE_RND:
7134 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7137 case LEVEL_FILE_TYPE_BD:
7138 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7139 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7142 case LEVEL_FILE_TYPE_EM:
7143 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7144 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7147 case LEVEL_FILE_TYPE_SP:
7148 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7149 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7152 case LEVEL_FILE_TYPE_MM:
7153 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7154 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7157 case LEVEL_FILE_TYPE_DC:
7158 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7161 case LEVEL_FILE_TYPE_SB:
7162 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7166 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7170 // if level file is invalid, restore level structure to default values
7171 if (level->no_valid_file)
7172 setLevelInfoToDefaults(level, level_info_only, FALSE);
7174 if (check_special_flags("use_native_bd_game_engine"))
7175 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7177 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7178 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7180 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7181 CopyNativeLevel_Native_to_RND(level);
7184 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7186 static struct LevelFileInfo level_file_info;
7188 // always start with reliable default values
7189 setFileInfoToDefaults(&level_file_info);
7191 level_file_info.nr = 0; // unknown level number
7192 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7194 setString(&level_file_info.filename, filename);
7196 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7199 static void LoadLevel_InitVersion(struct LevelInfo *level)
7203 if (leveldir_current == NULL) // only when dumping level
7206 // all engine modifications also valid for levels which use latest engine
7207 if (level->game_version < VERSION_IDENT(3,2,0,5))
7209 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7210 level->time_score_base = 10;
7213 if (leveldir_current->latest_engine)
7215 // ---------- use latest game engine --------------------------------------
7217 /* For all levels which are forced to use the latest game engine version
7218 (normally all but user contributed, private and undefined levels), set
7219 the game engine version to the actual version; this allows for actual
7220 corrections in the game engine to take effect for existing, converted
7221 levels (from "classic" or other existing games) to make the emulation
7222 of the corresponding game more accurate, while (hopefully) not breaking
7223 existing levels created from other players. */
7225 level->game_version = GAME_VERSION_ACTUAL;
7227 /* Set special EM style gems behaviour: EM style gems slip down from
7228 normal, steel and growing wall. As this is a more fundamental change,
7229 it seems better to set the default behaviour to "off" (as it is more
7230 natural) and make it configurable in the level editor (as a property
7231 of gem style elements). Already existing converted levels (neither
7232 private nor contributed levels) are changed to the new behaviour. */
7234 if (level->file_version < FILE_VERSION_2_0)
7235 level->em_slippery_gems = TRUE;
7240 // ---------- use game engine the level was created with --------------------
7242 /* For all levels which are not forced to use the latest game engine
7243 version (normally user contributed, private and undefined levels),
7244 use the version of the game engine the levels were created for.
7246 Since 2.0.1, the game engine version is now directly stored
7247 in the level file (chunk "VERS"), so there is no need anymore
7248 to set the game version from the file version (except for old,
7249 pre-2.0 levels, where the game version is still taken from the
7250 file format version used to store the level -- see above). */
7252 // player was faster than enemies in 1.0.0 and before
7253 if (level->file_version == FILE_VERSION_1_0)
7254 for (i = 0; i < MAX_PLAYERS; i++)
7255 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7257 // default behaviour for EM style gems was "slippery" only in 2.0.1
7258 if (level->game_version == VERSION_IDENT(2,0,1,0))
7259 level->em_slippery_gems = TRUE;
7261 // springs could be pushed over pits before (pre-release version) 2.2.0
7262 if (level->game_version < VERSION_IDENT(2,2,0,0))
7263 level->use_spring_bug = TRUE;
7265 if (level->game_version < VERSION_IDENT(3,2,0,5))
7267 // time orb caused limited time in endless time levels before 3.2.0-5
7268 level->use_time_orb_bug = TRUE;
7270 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7271 level->block_snap_field = FALSE;
7273 // extra time score was same value as time left score before 3.2.0-5
7274 level->extra_time_score = level->score[SC_TIME_BONUS];
7277 if (level->game_version < VERSION_IDENT(3,2,0,7))
7279 // default behaviour for snapping was "not continuous" before 3.2.0-7
7280 level->continuous_snapping = FALSE;
7283 // only few elements were able to actively move into acid before 3.1.0
7284 // trigger settings did not exist before 3.1.0; set to default "any"
7285 if (level->game_version < VERSION_IDENT(3,1,0,0))
7287 // correct "can move into acid" settings (all zero in old levels)
7289 level->can_move_into_acid_bits = 0; // nothing can move into acid
7290 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7292 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7293 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7294 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7295 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7297 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7298 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7300 // correct trigger settings (stored as zero == "none" in old levels)
7302 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7304 int element = EL_CUSTOM_START + i;
7305 struct ElementInfo *ei = &element_info[element];
7307 for (j = 0; j < ei->num_change_pages; j++)
7309 struct ElementChangeInfo *change = &ei->change_page[j];
7311 change->trigger_player = CH_PLAYER_ANY;
7312 change->trigger_page = CH_PAGE_ANY;
7317 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7319 int element = EL_CUSTOM_256;
7320 struct ElementInfo *ei = &element_info[element];
7321 struct ElementChangeInfo *change = &ei->change_page[0];
7323 /* This is needed to fix a problem that was caused by a bugfix in function
7324 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7325 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7326 not replace walkable elements, but instead just placed the player on it,
7327 without placing the Sokoban field under the player). Unfortunately, this
7328 breaks "Snake Bite" style levels when the snake is halfway through a door
7329 that just closes (the snake head is still alive and can be moved in this
7330 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7331 player (without Sokoban element) which then gets killed as designed). */
7333 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7334 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7335 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7336 change->target_element = EL_PLAYER_1;
7339 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7340 if (level->game_version < VERSION_IDENT(3,2,5,0))
7342 /* This is needed to fix a problem that was caused by a bugfix in function
7343 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7344 corrects the behaviour when a custom element changes to another custom
7345 element with a higher element number that has change actions defined.
7346 Normally, only one change per frame is allowed for custom elements.
7347 Therefore, it is checked if a custom element already changed in the
7348 current frame; if it did, subsequent changes are suppressed.
7349 Unfortunately, this is only checked for element changes, but not for
7350 change actions, which are still executed. As the function above loops
7351 through all custom elements from lower to higher, an element change
7352 resulting in a lower CE number won't be checked again, while a target
7353 element with a higher number will also be checked, and potential change
7354 actions will get executed for this CE, too (which is wrong), while
7355 further changes are ignored (which is correct). As this bugfix breaks
7356 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7357 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7358 behaviour for existing levels and tapes that make use of this bug */
7360 level->use_action_after_change_bug = TRUE;
7363 // not centering level after relocating player was default only in 3.2.3
7364 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7365 level->shifted_relocation = TRUE;
7367 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7368 if (level->game_version < VERSION_IDENT(3,2,6,0))
7369 level->em_explodes_by_fire = TRUE;
7371 // levels were solved by the first player entering an exit up to 4.1.0.0
7372 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7373 level->solved_by_one_player = TRUE;
7375 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7376 if (level->game_version < VERSION_IDENT(4,1,1,1))
7377 level->use_life_bugs = TRUE;
7379 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7380 if (level->game_version < VERSION_IDENT(4,1,1,1))
7381 level->sb_objects_needed = FALSE;
7383 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7384 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7385 level->finish_dig_collect = FALSE;
7387 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7388 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7389 level->keep_walkable_ce = TRUE;
7392 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7394 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7397 // check if this level is (not) a Sokoban level
7398 for (y = 0; y < level->fieldy; y++)
7399 for (x = 0; x < level->fieldx; x++)
7400 if (!IS_SB_ELEMENT(Tile[x][y]))
7401 is_sokoban_level = FALSE;
7403 if (is_sokoban_level)
7405 // set special level settings for Sokoban levels
7406 SetLevelSettings_SB(level);
7410 static void LoadLevel_InitSettings(struct LevelInfo *level)
7412 // adjust level settings for (non-native) Sokoban-style levels
7413 LoadLevel_InitSettings_SB(level);
7415 // rename levels with title "nameless level" or if renaming is forced
7416 if (leveldir_current->empty_level_name != NULL &&
7417 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7418 leveldir_current->force_level_name))
7419 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7420 leveldir_current->empty_level_name, level_nr);
7423 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7427 // map elements that have changed in newer versions
7428 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7429 level->game_version);
7430 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7431 for (x = 0; x < 3; x++)
7432 for (y = 0; y < 3; y++)
7433 level->yamyam_content[i].e[x][y] =
7434 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7435 level->game_version);
7439 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7443 // map custom element change events that have changed in newer versions
7444 // (these following values were accidentally changed in version 3.0.1)
7445 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7446 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7448 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7450 int element = EL_CUSTOM_START + i;
7452 // order of checking and copying events to be mapped is important
7453 // (do not change the start and end value -- they are constant)
7454 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7456 if (HAS_CHANGE_EVENT(element, j - 2))
7458 SET_CHANGE_EVENT(element, j - 2, FALSE);
7459 SET_CHANGE_EVENT(element, j, TRUE);
7463 // order of checking and copying events to be mapped is important
7464 // (do not change the start and end value -- they are constant)
7465 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7467 if (HAS_CHANGE_EVENT(element, j - 1))
7469 SET_CHANGE_EVENT(element, j - 1, FALSE);
7470 SET_CHANGE_EVENT(element, j, TRUE);
7476 // initialize "can_change" field for old levels with only one change page
7477 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7479 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7481 int element = EL_CUSTOM_START + i;
7483 if (CAN_CHANGE(element))
7484 element_info[element].change->can_change = TRUE;
7488 // correct custom element values (for old levels without these options)
7489 if (level->game_version < VERSION_IDENT(3,1,1,0))
7491 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7493 int element = EL_CUSTOM_START + i;
7494 struct ElementInfo *ei = &element_info[element];
7496 if (ei->access_direction == MV_NO_DIRECTION)
7497 ei->access_direction = MV_ALL_DIRECTIONS;
7501 // correct custom element values (fix invalid values for all versions)
7504 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7506 int element = EL_CUSTOM_START + i;
7507 struct ElementInfo *ei = &element_info[element];
7509 for (j = 0; j < ei->num_change_pages; j++)
7511 struct ElementChangeInfo *change = &ei->change_page[j];
7513 if (change->trigger_player == CH_PLAYER_NONE)
7514 change->trigger_player = CH_PLAYER_ANY;
7516 if (change->trigger_side == CH_SIDE_NONE)
7517 change->trigger_side = CH_SIDE_ANY;
7522 // initialize "can_explode" field for old levels which did not store this
7523 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7524 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7526 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7528 int element = EL_CUSTOM_START + i;
7530 if (EXPLODES_1X1_OLD(element))
7531 element_info[element].explosion_type = EXPLODES_1X1;
7533 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7534 EXPLODES_SMASHED(element) ||
7535 EXPLODES_IMPACT(element)));
7539 // correct previously hard-coded move delay values for maze runner style
7540 if (level->game_version < VERSION_IDENT(3,1,1,0))
7542 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7544 int element = EL_CUSTOM_START + i;
7546 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7548 // previously hard-coded and therefore ignored
7549 element_info[element].move_delay_fixed = 9;
7550 element_info[element].move_delay_random = 0;
7555 // set some other uninitialized values of custom elements in older levels
7556 if (level->game_version < VERSION_IDENT(3,1,0,0))
7558 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7560 int element = EL_CUSTOM_START + i;
7562 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7564 element_info[element].explosion_delay = 17;
7565 element_info[element].ignition_delay = 8;
7569 // set mouse click change events to work for left/middle/right mouse button
7570 if (level->game_version < VERSION_IDENT(4,2,3,0))
7572 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7574 int element = EL_CUSTOM_START + i;
7575 struct ElementInfo *ei = &element_info[element];
7577 for (j = 0; j < ei->num_change_pages; j++)
7579 struct ElementChangeInfo *change = &ei->change_page[j];
7581 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7582 change->has_event[CE_PRESSED_BY_MOUSE] ||
7583 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7584 change->has_event[CE_MOUSE_PRESSED_ON_X])
7585 change->trigger_side = CH_SIDE_ANY;
7591 static void LoadLevel_InitElements(struct LevelInfo *level)
7593 LoadLevel_InitStandardElements(level);
7595 if (level->file_has_custom_elements)
7596 LoadLevel_InitCustomElements(level);
7598 // initialize element properties for level editor etc.
7599 InitElementPropertiesEngine(level->game_version);
7600 InitElementPropertiesGfxElement();
7603 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7607 // map elements that have changed in newer versions
7608 for (y = 0; y < level->fieldy; y++)
7609 for (x = 0; x < level->fieldx; x++)
7610 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7611 level->game_version);
7613 // clear unused playfield data (nicer if level gets resized in editor)
7614 for (x = 0; x < MAX_LEV_FIELDX; x++)
7615 for (y = 0; y < MAX_LEV_FIELDY; y++)
7616 if (x >= level->fieldx || y >= level->fieldy)
7617 level->field[x][y] = EL_EMPTY;
7619 // copy elements to runtime playfield array
7620 for (x = 0; x < MAX_LEV_FIELDX; x++)
7621 for (y = 0; y < MAX_LEV_FIELDY; y++)
7622 Tile[x][y] = level->field[x][y];
7624 // initialize level size variables for faster access
7625 lev_fieldx = level->fieldx;
7626 lev_fieldy = level->fieldy;
7628 // determine border element for this level
7629 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7630 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7635 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7637 struct LevelFileInfo *level_file_info = &level->file_info;
7639 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7640 CopyNativeLevel_RND_to_Native(level);
7643 static void LoadLevelTemplate_LoadAndInit(void)
7645 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7647 LoadLevel_InitVersion(&level_template);
7648 LoadLevel_InitElements(&level_template);
7649 LoadLevel_InitSettings(&level_template);
7651 ActivateLevelTemplate();
7654 void LoadLevelTemplate(int nr)
7656 if (!fileExists(getGlobalLevelTemplateFilename()))
7658 Warn("no level template found for this level");
7663 setLevelFileInfo(&level_template.file_info, nr);
7665 LoadLevelTemplate_LoadAndInit();
7668 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7670 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7672 LoadLevelTemplate_LoadAndInit();
7675 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7677 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7679 if (level.use_custom_template)
7681 if (network_level != NULL)
7682 LoadNetworkLevelTemplate(network_level);
7684 LoadLevelTemplate(-1);
7687 LoadLevel_InitVersion(&level);
7688 LoadLevel_InitElements(&level);
7689 LoadLevel_InitPlayfield(&level);
7690 LoadLevel_InitSettings(&level);
7692 LoadLevel_InitNativeEngines(&level);
7695 void LoadLevel(int nr)
7697 SetLevelSetInfo(leveldir_current->identifier, nr);
7699 setLevelFileInfo(&level.file_info, nr);
7701 LoadLevel_LoadAndInit(NULL);
7704 void LoadLevelInfoOnly(int nr)
7706 setLevelFileInfo(&level.file_info, nr);
7708 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7711 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7713 SetLevelSetInfo(network_level->leveldir_identifier,
7714 network_level->file_info.nr);
7716 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7718 LoadLevel_LoadAndInit(network_level);
7721 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7725 chunk_size += putFileVersion(file, level->file_version);
7726 chunk_size += putFileVersion(file, level->game_version);
7731 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7735 chunk_size += putFile16BitBE(file, level->creation_date.year);
7736 chunk_size += putFile8Bit(file, level->creation_date.month);
7737 chunk_size += putFile8Bit(file, level->creation_date.day);
7742 #if ENABLE_HISTORIC_CHUNKS
7743 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7747 putFile8Bit(file, level->fieldx);
7748 putFile8Bit(file, level->fieldy);
7750 putFile16BitBE(file, level->time);
7751 putFile16BitBE(file, level->gems_needed);
7753 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7754 putFile8Bit(file, level->name[i]);
7756 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7757 putFile8Bit(file, level->score[i]);
7759 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7760 for (y = 0; y < 3; y++)
7761 for (x = 0; x < 3; x++)
7762 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7763 level->yamyam_content[i].e[x][y]));
7764 putFile8Bit(file, level->amoeba_speed);
7765 putFile8Bit(file, level->time_magic_wall);
7766 putFile8Bit(file, level->time_wheel);
7767 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7768 level->amoeba_content));
7769 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7770 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7771 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7772 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7774 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7776 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7777 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7778 putFile32BitBE(file, level->can_move_into_acid_bits);
7779 putFile8Bit(file, level->dont_collide_with_bits);
7781 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7782 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7784 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7785 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7786 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7788 putFile8Bit(file, level->game_engine_type);
7790 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7794 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7799 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7800 chunk_size += putFile8Bit(file, level->name[i]);
7805 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7810 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7811 chunk_size += putFile8Bit(file, level->author[i]);
7816 #if ENABLE_HISTORIC_CHUNKS
7817 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7822 for (y = 0; y < level->fieldy; y++)
7823 for (x = 0; x < level->fieldx; x++)
7824 if (level->encoding_16bit_field)
7825 chunk_size += putFile16BitBE(file, level->field[x][y]);
7827 chunk_size += putFile8Bit(file, level->field[x][y]);
7833 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7838 for (y = 0; y < level->fieldy; y++)
7839 for (x = 0; x < level->fieldx; x++)
7840 chunk_size += putFile16BitBE(file, level->field[x][y]);
7845 #if ENABLE_HISTORIC_CHUNKS
7846 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7850 putFile8Bit(file, EL_YAMYAM);
7851 putFile8Bit(file, level->num_yamyam_contents);
7852 putFile8Bit(file, 0);
7853 putFile8Bit(file, 0);
7855 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7856 for (y = 0; y < 3; y++)
7857 for (x = 0; x < 3; x++)
7858 if (level->encoding_16bit_field)
7859 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7861 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7865 #if ENABLE_HISTORIC_CHUNKS
7866 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7869 int num_contents, content_xsize, content_ysize;
7870 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7872 if (element == EL_YAMYAM)
7874 num_contents = level->num_yamyam_contents;
7878 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7879 for (y = 0; y < 3; y++)
7880 for (x = 0; x < 3; x++)
7881 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7883 else if (element == EL_BD_AMOEBA)
7889 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7890 for (y = 0; y < 3; y++)
7891 for (x = 0; x < 3; x++)
7892 content_array[i][x][y] = EL_EMPTY;
7893 content_array[0][0][0] = level->amoeba_content;
7897 // chunk header already written -- write empty chunk data
7898 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7900 Warn("cannot save content for element '%d'", element);
7905 putFile16BitBE(file, element);
7906 putFile8Bit(file, num_contents);
7907 putFile8Bit(file, content_xsize);
7908 putFile8Bit(file, content_ysize);
7910 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7912 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7913 for (y = 0; y < 3; y++)
7914 for (x = 0; x < 3; x++)
7915 putFile16BitBE(file, content_array[i][x][y]);
7919 #if ENABLE_HISTORIC_CHUNKS
7920 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7922 int envelope_nr = element - EL_ENVELOPE_1;
7923 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7927 chunk_size += putFile16BitBE(file, element);
7928 chunk_size += putFile16BitBE(file, envelope_len);
7929 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7930 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7932 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7933 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7935 for (i = 0; i < envelope_len; i++)
7936 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7942 #if ENABLE_HISTORIC_CHUNKS
7943 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7944 int num_changed_custom_elements)
7948 putFile16BitBE(file, num_changed_custom_elements);
7950 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7952 int element = EL_CUSTOM_START + i;
7954 struct ElementInfo *ei = &element_info[element];
7956 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7958 if (check < num_changed_custom_elements)
7960 putFile16BitBE(file, element);
7961 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7968 if (check != num_changed_custom_elements) // should not happen
7969 Warn("inconsistent number of custom element properties");
7973 #if ENABLE_HISTORIC_CHUNKS
7974 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7975 int num_changed_custom_elements)
7979 putFile16BitBE(file, num_changed_custom_elements);
7981 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7983 int element = EL_CUSTOM_START + i;
7985 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7987 if (check < num_changed_custom_elements)
7989 putFile16BitBE(file, element);
7990 putFile16BitBE(file, element_info[element].change->target_element);
7997 if (check != num_changed_custom_elements) // should not happen
7998 Warn("inconsistent number of custom target elements");
8002 #if ENABLE_HISTORIC_CHUNKS
8003 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8004 int num_changed_custom_elements)
8006 int i, j, x, y, check = 0;
8008 putFile16BitBE(file, num_changed_custom_elements);
8010 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8012 int element = EL_CUSTOM_START + i;
8013 struct ElementInfo *ei = &element_info[element];
8015 if (ei->modified_settings)
8017 if (check < num_changed_custom_elements)
8019 putFile16BitBE(file, element);
8021 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8022 putFile8Bit(file, ei->description[j]);
8024 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8026 // some free bytes for future properties and padding
8027 WriteUnusedBytesToFile(file, 7);
8029 putFile8Bit(file, ei->use_gfx_element);
8030 putFile16BitBE(file, ei->gfx_element_initial);
8032 putFile8Bit(file, ei->collect_score_initial);
8033 putFile8Bit(file, ei->collect_count_initial);
8035 putFile16BitBE(file, ei->push_delay_fixed);
8036 putFile16BitBE(file, ei->push_delay_random);
8037 putFile16BitBE(file, ei->move_delay_fixed);
8038 putFile16BitBE(file, ei->move_delay_random);
8040 putFile16BitBE(file, ei->move_pattern);
8041 putFile8Bit(file, ei->move_direction_initial);
8042 putFile8Bit(file, ei->move_stepsize);
8044 for (y = 0; y < 3; y++)
8045 for (x = 0; x < 3; x++)
8046 putFile16BitBE(file, ei->content.e[x][y]);
8048 putFile32BitBE(file, ei->change->events);
8050 putFile16BitBE(file, ei->change->target_element);
8052 putFile16BitBE(file, ei->change->delay_fixed);
8053 putFile16BitBE(file, ei->change->delay_random);
8054 putFile16BitBE(file, ei->change->delay_frames);
8056 putFile16BitBE(file, ei->change->initial_trigger_element);
8058 putFile8Bit(file, ei->change->explode);
8059 putFile8Bit(file, ei->change->use_target_content);
8060 putFile8Bit(file, ei->change->only_if_complete);
8061 putFile8Bit(file, ei->change->use_random_replace);
8063 putFile8Bit(file, ei->change->random_percentage);
8064 putFile8Bit(file, ei->change->replace_when);
8066 for (y = 0; y < 3; y++)
8067 for (x = 0; x < 3; x++)
8068 putFile16BitBE(file, ei->change->content.e[x][y]);
8070 putFile8Bit(file, ei->slippery_type);
8072 // some free bytes for future properties and padding
8073 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8080 if (check != num_changed_custom_elements) // should not happen
8081 Warn("inconsistent number of custom element properties");
8085 #if ENABLE_HISTORIC_CHUNKS
8086 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8088 struct ElementInfo *ei = &element_info[element];
8091 // ---------- custom element base property values (96 bytes) ----------------
8093 putFile16BitBE(file, element);
8095 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8096 putFile8Bit(file, ei->description[i]);
8098 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8100 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8102 putFile8Bit(file, ei->num_change_pages);
8104 putFile16BitBE(file, ei->ce_value_fixed_initial);
8105 putFile16BitBE(file, ei->ce_value_random_initial);
8106 putFile8Bit(file, ei->use_last_ce_value);
8108 putFile8Bit(file, ei->use_gfx_element);
8109 putFile16BitBE(file, ei->gfx_element_initial);
8111 putFile8Bit(file, ei->collect_score_initial);
8112 putFile8Bit(file, ei->collect_count_initial);
8114 putFile8Bit(file, ei->drop_delay_fixed);
8115 putFile8Bit(file, ei->push_delay_fixed);
8116 putFile8Bit(file, ei->drop_delay_random);
8117 putFile8Bit(file, ei->push_delay_random);
8118 putFile16BitBE(file, ei->move_delay_fixed);
8119 putFile16BitBE(file, ei->move_delay_random);
8121 // bits 0 - 15 of "move_pattern" ...
8122 putFile16BitBE(file, ei->move_pattern & 0xffff);
8123 putFile8Bit(file, ei->move_direction_initial);
8124 putFile8Bit(file, ei->move_stepsize);
8126 putFile8Bit(file, ei->slippery_type);
8128 for (y = 0; y < 3; y++)
8129 for (x = 0; x < 3; x++)
8130 putFile16BitBE(file, ei->content.e[x][y]);
8132 putFile16BitBE(file, ei->move_enter_element);
8133 putFile16BitBE(file, ei->move_leave_element);
8134 putFile8Bit(file, ei->move_leave_type);
8136 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8137 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8139 putFile8Bit(file, ei->access_direction);
8141 putFile8Bit(file, ei->explosion_delay);
8142 putFile8Bit(file, ei->ignition_delay);
8143 putFile8Bit(file, ei->explosion_type);
8145 // some free bytes for future custom property values and padding
8146 WriteUnusedBytesToFile(file, 1);
8148 // ---------- change page property values (48 bytes) ------------------------
8150 for (i = 0; i < ei->num_change_pages; i++)
8152 struct ElementChangeInfo *change = &ei->change_page[i];
8153 unsigned int event_bits;
8155 // bits 0 - 31 of "has_event[]" ...
8157 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8158 if (change->has_event[j])
8159 event_bits |= (1u << j);
8160 putFile32BitBE(file, event_bits);
8162 putFile16BitBE(file, change->target_element);
8164 putFile16BitBE(file, change->delay_fixed);
8165 putFile16BitBE(file, change->delay_random);
8166 putFile16BitBE(file, change->delay_frames);
8168 putFile16BitBE(file, change->initial_trigger_element);
8170 putFile8Bit(file, change->explode);
8171 putFile8Bit(file, change->use_target_content);
8172 putFile8Bit(file, change->only_if_complete);
8173 putFile8Bit(file, change->use_random_replace);
8175 putFile8Bit(file, change->random_percentage);
8176 putFile8Bit(file, change->replace_when);
8178 for (y = 0; y < 3; y++)
8179 for (x = 0; x < 3; x++)
8180 putFile16BitBE(file, change->target_content.e[x][y]);
8182 putFile8Bit(file, change->can_change);
8184 putFile8Bit(file, change->trigger_side);
8186 putFile8Bit(file, change->trigger_player);
8187 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8188 log_2(change->trigger_page)));
8190 putFile8Bit(file, change->has_action);
8191 putFile8Bit(file, change->action_type);
8192 putFile8Bit(file, change->action_mode);
8193 putFile16BitBE(file, change->action_arg);
8195 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8197 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8198 if (change->has_event[j])
8199 event_bits |= (1u << (j - 32));
8200 putFile8Bit(file, event_bits);
8205 #if ENABLE_HISTORIC_CHUNKS
8206 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8208 struct ElementInfo *ei = &element_info[element];
8209 struct ElementGroupInfo *group = ei->group;
8212 putFile16BitBE(file, element);
8214 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8215 putFile8Bit(file, ei->description[i]);
8217 putFile8Bit(file, group->num_elements);
8219 putFile8Bit(file, ei->use_gfx_element);
8220 putFile16BitBE(file, ei->gfx_element_initial);
8222 putFile8Bit(file, group->choice_mode);
8224 // some free bytes for future values and padding
8225 WriteUnusedBytesToFile(file, 3);
8227 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8228 putFile16BitBE(file, group->element[i]);
8232 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8233 boolean write_element)
8235 int save_type = entry->save_type;
8236 int data_type = entry->data_type;
8237 int conf_type = entry->conf_type;
8238 int byte_mask = conf_type & CONF_MASK_BYTES;
8239 int element = entry->element;
8240 int default_value = entry->default_value;
8242 boolean modified = FALSE;
8244 if (byte_mask != CONF_MASK_MULTI_BYTES)
8246 void *value_ptr = entry->value;
8247 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8250 // check if any settings have been modified before saving them
8251 if (value != default_value)
8254 // do not save if explicitly told or if unmodified default settings
8255 if ((save_type == SAVE_CONF_NEVER) ||
8256 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8260 num_bytes += putFile16BitBE(file, element);
8262 num_bytes += putFile8Bit(file, conf_type);
8263 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8264 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8265 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8268 else if (data_type == TYPE_STRING)
8270 char *default_string = entry->default_string;
8271 char *string = (char *)(entry->value);
8272 int string_length = strlen(string);
8275 // check if any settings have been modified before saving them
8276 if (!strEqual(string, default_string))
8279 // do not save if explicitly told or if unmodified default settings
8280 if ((save_type == SAVE_CONF_NEVER) ||
8281 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8285 num_bytes += putFile16BitBE(file, element);
8287 num_bytes += putFile8Bit(file, conf_type);
8288 num_bytes += putFile16BitBE(file, string_length);
8290 for (i = 0; i < string_length; i++)
8291 num_bytes += putFile8Bit(file, string[i]);
8293 else if (data_type == TYPE_ELEMENT_LIST)
8295 int *element_array = (int *)(entry->value);
8296 int num_elements = *(int *)(entry->num_entities);
8299 // check if any settings have been modified before saving them
8300 for (i = 0; i < num_elements; i++)
8301 if (element_array[i] != default_value)
8304 // do not save if explicitly told or if unmodified default settings
8305 if ((save_type == SAVE_CONF_NEVER) ||
8306 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8310 num_bytes += putFile16BitBE(file, element);
8312 num_bytes += putFile8Bit(file, conf_type);
8313 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8315 for (i = 0; i < num_elements; i++)
8316 num_bytes += putFile16BitBE(file, element_array[i]);
8318 else if (data_type == TYPE_CONTENT_LIST)
8320 struct Content *content = (struct Content *)(entry->value);
8321 int num_contents = *(int *)(entry->num_entities);
8324 // check if any settings have been modified before saving them
8325 for (i = 0; i < num_contents; i++)
8326 for (y = 0; y < 3; y++)
8327 for (x = 0; x < 3; x++)
8328 if (content[i].e[x][y] != default_value)
8331 // do not save if explicitly told or if unmodified default settings
8332 if ((save_type == SAVE_CONF_NEVER) ||
8333 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8337 num_bytes += putFile16BitBE(file, element);
8339 num_bytes += putFile8Bit(file, conf_type);
8340 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8342 for (i = 0; i < num_contents; i++)
8343 for (y = 0; y < 3; y++)
8344 for (x = 0; x < 3; x++)
8345 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8351 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8356 li = *level; // copy level data into temporary buffer
8358 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8359 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8364 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8369 li = *level; // copy level data into temporary buffer
8371 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8372 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8377 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8379 int envelope_nr = element - EL_ENVELOPE_1;
8383 chunk_size += putFile16BitBE(file, element);
8385 // copy envelope data into temporary buffer
8386 xx_envelope = level->envelope[envelope_nr];
8388 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8389 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8394 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8396 struct ElementInfo *ei = &element_info[element];
8400 chunk_size += putFile16BitBE(file, element);
8402 xx_ei = *ei; // copy element data into temporary buffer
8404 // set default description string for this specific element
8405 strcpy(xx_default_description, getDefaultElementDescription(ei));
8407 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8408 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8410 for (i = 0; i < ei->num_change_pages; i++)
8412 struct ElementChangeInfo *change = &ei->change_page[i];
8414 xx_current_change_page = i;
8416 xx_change = *change; // copy change data into temporary buffer
8419 setEventBitsFromEventFlags(change);
8421 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8422 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8429 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8431 struct ElementInfo *ei = &element_info[element];
8432 struct ElementGroupInfo *group = ei->group;
8436 chunk_size += putFile16BitBE(file, element);
8438 xx_ei = *ei; // copy element data into temporary buffer
8439 xx_group = *group; // copy group data into temporary buffer
8441 // set default description string for this specific element
8442 strcpy(xx_default_description, getDefaultElementDescription(ei));
8444 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8445 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8450 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8452 struct ElementInfo *ei = &element_info[element];
8456 chunk_size += putFile16BitBE(file, element);
8458 xx_ei = *ei; // copy element data into temporary buffer
8460 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8461 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8466 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8467 boolean save_as_template)
8473 if (!(file = fopen(filename, MODE_WRITE)))
8475 Warn("cannot save level file '%s'", filename);
8480 level->file_version = FILE_VERSION_ACTUAL;
8481 level->game_version = GAME_VERSION_ACTUAL;
8483 level->creation_date = getCurrentDate();
8485 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8486 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8488 chunk_size = SaveLevel_VERS(NULL, level);
8489 putFileChunkBE(file, "VERS", chunk_size);
8490 SaveLevel_VERS(file, level);
8492 chunk_size = SaveLevel_DATE(NULL, level);
8493 putFileChunkBE(file, "DATE", chunk_size);
8494 SaveLevel_DATE(file, level);
8496 chunk_size = SaveLevel_NAME(NULL, level);
8497 putFileChunkBE(file, "NAME", chunk_size);
8498 SaveLevel_NAME(file, level);
8500 chunk_size = SaveLevel_AUTH(NULL, level);
8501 putFileChunkBE(file, "AUTH", chunk_size);
8502 SaveLevel_AUTH(file, level);
8504 chunk_size = SaveLevel_INFO(NULL, level);
8505 putFileChunkBE(file, "INFO", chunk_size);
8506 SaveLevel_INFO(file, level);
8508 chunk_size = SaveLevel_BODY(NULL, level);
8509 putFileChunkBE(file, "BODY", chunk_size);
8510 SaveLevel_BODY(file, level);
8512 chunk_size = SaveLevel_ELEM(NULL, level);
8513 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8515 putFileChunkBE(file, "ELEM", chunk_size);
8516 SaveLevel_ELEM(file, level);
8519 for (i = 0; i < NUM_ENVELOPES; i++)
8521 int element = EL_ENVELOPE_1 + i;
8523 chunk_size = SaveLevel_NOTE(NULL, level, element);
8524 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8526 putFileChunkBE(file, "NOTE", chunk_size);
8527 SaveLevel_NOTE(file, level, element);
8531 // if not using template level, check for non-default custom/group elements
8532 if (!level->use_custom_template || save_as_template)
8534 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8536 int element = EL_CUSTOM_START + i;
8538 chunk_size = SaveLevel_CUSX(NULL, level, element);
8539 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8541 putFileChunkBE(file, "CUSX", chunk_size);
8542 SaveLevel_CUSX(file, level, element);
8546 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8548 int element = EL_GROUP_START + i;
8550 chunk_size = SaveLevel_GRPX(NULL, level, element);
8551 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8553 putFileChunkBE(file, "GRPX", chunk_size);
8554 SaveLevel_GRPX(file, level, element);
8558 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8560 int element = GET_EMPTY_ELEMENT(i);
8562 chunk_size = SaveLevel_EMPX(NULL, level, element);
8563 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8565 putFileChunkBE(file, "EMPX", chunk_size);
8566 SaveLevel_EMPX(file, level, element);
8573 SetFilePermissions(filename, PERMS_PRIVATE);
8576 void SaveLevel(int nr)
8578 char *filename = getDefaultLevelFilename(nr);
8580 SaveLevelFromFilename(&level, filename, FALSE);
8583 void SaveLevelTemplate(void)
8585 char *filename = getLocalLevelTemplateFilename();
8587 SaveLevelFromFilename(&level, filename, TRUE);
8590 boolean SaveLevelChecked(int nr)
8592 char *filename = getDefaultLevelFilename(nr);
8593 boolean new_level = !fileExists(filename);
8594 boolean level_saved = FALSE;
8596 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8601 Request("Level saved!", REQ_CONFIRM);
8609 void DumpLevel(struct LevelInfo *level)
8611 if (level->no_level_file || level->no_valid_file)
8613 Warn("cannot dump -- no valid level file found");
8619 Print("Level xxx (file version %08d, game version %08d)\n",
8620 level->file_version, level->game_version);
8623 Print("Level author: '%s'\n", level->author);
8624 Print("Level title: '%s'\n", level->name);
8626 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8628 Print("Level time: %d seconds\n", level->time);
8629 Print("Gems needed: %d\n", level->gems_needed);
8631 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8632 Print("Time for wheel: %d seconds\n", level->time_wheel);
8633 Print("Time for light: %d seconds\n", level->time_light);
8634 Print("Time for timegate: %d seconds\n", level->time_timegate);
8636 Print("Amoeba speed: %d\n", level->amoeba_speed);
8639 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8640 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8641 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8642 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8643 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8644 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8650 for (i = 0; i < NUM_ENVELOPES; i++)
8652 char *text = level->envelope[i].text;
8653 int text_len = strlen(text);
8654 boolean has_text = FALSE;
8656 for (j = 0; j < text_len; j++)
8657 if (text[j] != ' ' && text[j] != '\n')
8663 Print("Envelope %d:\n'%s'\n", i + 1, text);
8671 void DumpLevels(void)
8673 static LevelDirTree *dumplevel_leveldir = NULL;
8675 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8676 global.dumplevel_leveldir);
8678 if (dumplevel_leveldir == NULL)
8679 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8681 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8682 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8683 Fail("no such level number: %d", global.dumplevel_level_nr);
8685 leveldir_current = dumplevel_leveldir;
8687 LoadLevel(global.dumplevel_level_nr);
8694 // ============================================================================
8695 // tape file functions
8696 // ============================================================================
8698 static void setTapeInfoToDefaults(void)
8702 // always start with reliable default values (empty tape)
8705 // default values (also for pre-1.2 tapes) with only the first player
8706 tape.player_participates[0] = TRUE;
8707 for (i = 1; i < MAX_PLAYERS; i++)
8708 tape.player_participates[i] = FALSE;
8710 // at least one (default: the first) player participates in every tape
8711 tape.num_participating_players = 1;
8713 tape.property_bits = TAPE_PROPERTY_NONE;
8715 tape.level_nr = level_nr;
8717 tape.changed = FALSE;
8718 tape.solved = FALSE;
8720 tape.recording = FALSE;
8721 tape.playing = FALSE;
8722 tape.pausing = FALSE;
8724 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8725 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8727 tape.no_info_chunk = TRUE;
8728 tape.no_valid_file = FALSE;
8731 static int getTapePosSize(struct TapeInfo *tape)
8733 int tape_pos_size = 0;
8735 if (tape->use_key_actions)
8736 tape_pos_size += tape->num_participating_players;
8738 if (tape->use_mouse_actions)
8739 tape_pos_size += 3; // x and y position and mouse button mask
8741 tape_pos_size += 1; // tape action delay value
8743 return tape_pos_size;
8746 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8748 tape->use_key_actions = FALSE;
8749 tape->use_mouse_actions = FALSE;
8751 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8752 tape->use_key_actions = TRUE;
8754 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8755 tape->use_mouse_actions = TRUE;
8758 static int getTapeActionValue(struct TapeInfo *tape)
8760 return (tape->use_key_actions &&
8761 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8762 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8763 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8764 TAPE_ACTIONS_DEFAULT);
8767 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8769 tape->file_version = getFileVersion(file);
8770 tape->game_version = getFileVersion(file);
8775 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8779 tape->random_seed = getFile32BitBE(file);
8780 tape->date = getFile32BitBE(file);
8781 tape->length = getFile32BitBE(file);
8783 // read header fields that are new since version 1.2
8784 if (tape->file_version >= FILE_VERSION_1_2)
8786 byte store_participating_players = getFile8Bit(file);
8789 // since version 1.2, tapes store which players participate in the tape
8790 tape->num_participating_players = 0;
8791 for (i = 0; i < MAX_PLAYERS; i++)
8793 tape->player_participates[i] = FALSE;
8795 if (store_participating_players & (1 << i))
8797 tape->player_participates[i] = TRUE;
8798 tape->num_participating_players++;
8802 setTapeActionFlags(tape, getFile8Bit(file));
8804 tape->property_bits = getFile8Bit(file);
8805 tape->solved = getFile8Bit(file);
8807 engine_version = getFileVersion(file);
8808 if (engine_version > 0)
8809 tape->engine_version = engine_version;
8811 tape->engine_version = tape->game_version;
8817 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8819 tape->scr_fieldx = getFile8Bit(file);
8820 tape->scr_fieldy = getFile8Bit(file);
8825 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8827 char *level_identifier = NULL;
8828 int level_identifier_size;
8831 tape->no_info_chunk = FALSE;
8833 level_identifier_size = getFile16BitBE(file);
8835 level_identifier = checked_malloc(level_identifier_size);
8837 for (i = 0; i < level_identifier_size; i++)
8838 level_identifier[i] = getFile8Bit(file);
8840 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8841 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8843 checked_free(level_identifier);
8845 tape->level_nr = getFile16BitBE(file);
8847 chunk_size = 2 + level_identifier_size + 2;
8852 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8855 int tape_pos_size = getTapePosSize(tape);
8856 int chunk_size_expected = tape_pos_size * tape->length;
8858 if (chunk_size_expected != chunk_size)
8860 ReadUnusedBytesFromFile(file, chunk_size);
8861 return chunk_size_expected;
8864 for (i = 0; i < tape->length; i++)
8866 if (i >= MAX_TAPE_LEN)
8868 Warn("tape truncated -- size exceeds maximum tape size %d",
8871 // tape too large; read and ignore remaining tape data from this chunk
8872 for (;i < tape->length; i++)
8873 ReadUnusedBytesFromFile(file, tape_pos_size);
8878 if (tape->use_key_actions)
8880 for (j = 0; j < MAX_PLAYERS; j++)
8882 tape->pos[i].action[j] = MV_NONE;
8884 if (tape->player_participates[j])
8885 tape->pos[i].action[j] = getFile8Bit(file);
8889 if (tape->use_mouse_actions)
8891 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8892 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8893 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8896 tape->pos[i].delay = getFile8Bit(file);
8898 if (tape->file_version == FILE_VERSION_1_0)
8900 // eliminate possible diagonal moves in old tapes
8901 // this is only for backward compatibility
8903 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8904 byte action = tape->pos[i].action[0];
8905 int k, num_moves = 0;
8907 for (k = 0; k < 4; k++)
8909 if (action & joy_dir[k])
8911 tape->pos[i + num_moves].action[0] = joy_dir[k];
8913 tape->pos[i + num_moves].delay = 0;
8922 tape->length += num_moves;
8925 else if (tape->file_version < FILE_VERSION_2_0)
8927 // convert pre-2.0 tapes to new tape format
8929 if (tape->pos[i].delay > 1)
8932 tape->pos[i + 1] = tape->pos[i];
8933 tape->pos[i + 1].delay = 1;
8936 for (j = 0; j < MAX_PLAYERS; j++)
8937 tape->pos[i].action[j] = MV_NONE;
8938 tape->pos[i].delay--;
8945 if (checkEndOfFile(file))
8949 if (i != tape->length)
8950 chunk_size = tape_pos_size * i;
8955 static void LoadTape_SokobanSolution(char *filename)
8958 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8960 if (!(file = openFile(filename, MODE_READ)))
8962 tape.no_valid_file = TRUE;
8967 while (!checkEndOfFile(file))
8969 unsigned char c = getByteFromFile(file);
8971 if (checkEndOfFile(file))
8978 tape.pos[tape.length].action[0] = MV_UP;
8979 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8985 tape.pos[tape.length].action[0] = MV_DOWN;
8986 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8992 tape.pos[tape.length].action[0] = MV_LEFT;
8993 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8999 tape.pos[tape.length].action[0] = MV_RIGHT;
9000 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9008 // ignore white-space characters
9012 tape.no_valid_file = TRUE;
9014 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9022 if (tape.no_valid_file)
9025 tape.length_frames = GetTapeLengthFrames();
9026 tape.length_seconds = GetTapeLengthSeconds();
9029 void LoadTapeFromFilename(char *filename)
9031 char cookie[MAX_LINE_LEN];
9032 char chunk_name[CHUNK_ID_LEN + 1];
9036 // always start with reliable default values
9037 setTapeInfoToDefaults();
9039 if (strSuffix(filename, ".sln"))
9041 LoadTape_SokobanSolution(filename);
9046 if (!(file = openFile(filename, MODE_READ)))
9048 tape.no_valid_file = TRUE;
9053 getFileChunkBE(file, chunk_name, NULL);
9054 if (strEqual(chunk_name, "RND1"))
9056 getFile32BitBE(file); // not used
9058 getFileChunkBE(file, chunk_name, NULL);
9059 if (!strEqual(chunk_name, "TAPE"))
9061 tape.no_valid_file = TRUE;
9063 Warn("unknown format of tape file '%s'", filename);
9070 else // check for pre-2.0 file format with cookie string
9072 strcpy(cookie, chunk_name);
9073 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9075 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9076 cookie[strlen(cookie) - 1] = '\0';
9078 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9080 tape.no_valid_file = TRUE;
9082 Warn("unknown format of tape file '%s'", filename);
9089 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9091 tape.no_valid_file = TRUE;
9093 Warn("unsupported version of tape file '%s'", filename);
9100 // pre-2.0 tape files have no game version, so use file version here
9101 tape.game_version = tape.file_version;
9104 if (tape.file_version < FILE_VERSION_1_2)
9106 // tape files from versions before 1.2.0 without chunk structure
9107 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9108 LoadTape_BODY(file, 2 * tape.length, &tape);
9116 int (*loader)(File *, int, struct TapeInfo *);
9120 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9121 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9122 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9123 { "INFO", -1, LoadTape_INFO },
9124 { "BODY", -1, LoadTape_BODY },
9128 while (getFileChunkBE(file, chunk_name, &chunk_size))
9132 while (chunk_info[i].name != NULL &&
9133 !strEqual(chunk_name, chunk_info[i].name))
9136 if (chunk_info[i].name == NULL)
9138 Warn("unknown chunk '%s' in tape file '%s'",
9139 chunk_name, filename);
9141 ReadUnusedBytesFromFile(file, chunk_size);
9143 else if (chunk_info[i].size != -1 &&
9144 chunk_info[i].size != chunk_size)
9146 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9147 chunk_size, chunk_name, filename);
9149 ReadUnusedBytesFromFile(file, chunk_size);
9153 // call function to load this tape chunk
9154 int chunk_size_expected =
9155 (chunk_info[i].loader)(file, chunk_size, &tape);
9157 // the size of some chunks cannot be checked before reading other
9158 // chunks first (like "HEAD" and "BODY") that contain some header
9159 // information, so check them here
9160 if (chunk_size_expected != chunk_size)
9162 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9163 chunk_size, chunk_name, filename);
9171 tape.length_frames = GetTapeLengthFrames();
9172 tape.length_seconds = GetTapeLengthSeconds();
9175 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9177 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9179 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9180 tape.engine_version);
9184 void LoadTape(int nr)
9186 char *filename = getTapeFilename(nr);
9188 LoadTapeFromFilename(filename);
9191 void LoadSolutionTape(int nr)
9193 char *filename = getSolutionTapeFilename(nr);
9195 LoadTapeFromFilename(filename);
9197 if (TAPE_IS_EMPTY(tape))
9199 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9200 level.native_bd_level->replay != NULL)
9201 CopyNativeTape_BD_to_RND(&level);
9202 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9203 level.native_sp_level->demo.is_available)
9204 CopyNativeTape_SP_to_RND(&level);
9208 void LoadScoreTape(char *score_tape_basename, int nr)
9210 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9212 LoadTapeFromFilename(filename);
9215 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9217 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9219 LoadTapeFromFilename(filename);
9222 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9224 // chunk required for team mode tapes with non-default screen size
9225 return (tape->num_participating_players > 1 &&
9226 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9227 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9230 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9232 putFileVersion(file, tape->file_version);
9233 putFileVersion(file, tape->game_version);
9236 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9239 byte store_participating_players = 0;
9241 // set bits for participating players for compact storage
9242 for (i = 0; i < MAX_PLAYERS; i++)
9243 if (tape->player_participates[i])
9244 store_participating_players |= (1 << i);
9246 putFile32BitBE(file, tape->random_seed);
9247 putFile32BitBE(file, tape->date);
9248 putFile32BitBE(file, tape->length);
9250 putFile8Bit(file, store_participating_players);
9252 putFile8Bit(file, getTapeActionValue(tape));
9254 putFile8Bit(file, tape->property_bits);
9255 putFile8Bit(file, tape->solved);
9257 putFileVersion(file, tape->engine_version);
9260 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9262 putFile8Bit(file, tape->scr_fieldx);
9263 putFile8Bit(file, tape->scr_fieldy);
9266 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9268 int level_identifier_size = strlen(tape->level_identifier) + 1;
9271 putFile16BitBE(file, level_identifier_size);
9273 for (i = 0; i < level_identifier_size; i++)
9274 putFile8Bit(file, tape->level_identifier[i]);
9276 putFile16BitBE(file, tape->level_nr);
9279 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9283 for (i = 0; i < tape->length; i++)
9285 if (tape->use_key_actions)
9287 for (j = 0; j < MAX_PLAYERS; j++)
9288 if (tape->player_participates[j])
9289 putFile8Bit(file, tape->pos[i].action[j]);
9292 if (tape->use_mouse_actions)
9294 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9295 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9296 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9299 putFile8Bit(file, tape->pos[i].delay);
9303 void SaveTapeToFilename(char *filename)
9307 int info_chunk_size;
9308 int body_chunk_size;
9310 if (!(file = fopen(filename, MODE_WRITE)))
9312 Warn("cannot save level recording file '%s'", filename);
9317 tape_pos_size = getTapePosSize(&tape);
9319 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9320 body_chunk_size = tape_pos_size * tape.length;
9322 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9323 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9325 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9326 SaveTape_VERS(file, &tape);
9328 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9329 SaveTape_HEAD(file, &tape);
9331 if (checkSaveTape_SCRN(&tape))
9333 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9334 SaveTape_SCRN(file, &tape);
9337 putFileChunkBE(file, "INFO", info_chunk_size);
9338 SaveTape_INFO(file, &tape);
9340 putFileChunkBE(file, "BODY", body_chunk_size);
9341 SaveTape_BODY(file, &tape);
9345 SetFilePermissions(filename, PERMS_PRIVATE);
9348 static void SaveTapeExt(char *filename)
9352 tape.file_version = FILE_VERSION_ACTUAL;
9353 tape.game_version = GAME_VERSION_ACTUAL;
9355 tape.num_participating_players = 0;
9357 // count number of participating players
9358 for (i = 0; i < MAX_PLAYERS; i++)
9359 if (tape.player_participates[i])
9360 tape.num_participating_players++;
9362 SaveTapeToFilename(filename);
9364 tape.changed = FALSE;
9367 void SaveTape(int nr)
9369 char *filename = getTapeFilename(nr);
9371 InitTapeDirectory(leveldir_current->subdir);
9373 SaveTapeExt(filename);
9376 void SaveScoreTape(int nr)
9378 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9380 // used instead of "leveldir_current->subdir" (for network games)
9381 InitScoreTapeDirectory(levelset.identifier, nr);
9383 SaveTapeExt(filename);
9386 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9387 unsigned int req_state_added)
9389 char *filename = getTapeFilename(nr);
9390 boolean new_tape = !fileExists(filename);
9391 boolean tape_saved = FALSE;
9393 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9398 Request(msg_saved, REQ_CONFIRM | req_state_added);
9406 boolean SaveTapeChecked(int nr)
9408 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9411 boolean SaveTapeChecked_LevelSolved(int nr)
9413 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9414 "Level solved! Tape saved!", REQ_STAY_OPEN);
9417 void DumpTape(struct TapeInfo *tape)
9419 int tape_frame_counter;
9422 if (tape->no_valid_file)
9424 Warn("cannot dump -- no valid tape file found");
9431 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9432 tape->level_nr, tape->file_version, tape->game_version);
9433 Print(" (effective engine version %08d)\n",
9434 tape->engine_version);
9435 Print("Level series identifier: '%s'\n", tape->level_identifier);
9437 Print("Solution tape: %s\n",
9438 tape->solved ? "yes" :
9439 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9441 Print("Special tape properties: ");
9442 if (tape->property_bits == TAPE_PROPERTY_NONE)
9444 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9445 Print("[em_random_bug]");
9446 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9447 Print("[game_speed]");
9448 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9450 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9451 Print("[single_step]");
9452 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9453 Print("[snapshot]");
9454 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9455 Print("[replayed]");
9456 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9457 Print("[tas_keys]");
9458 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9459 Print("[small_graphics]");
9462 int year2 = tape->date / 10000;
9463 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9464 int month_index_raw = (tape->date / 100) % 100;
9465 int month_index = month_index_raw % 12; // prevent invalid index
9466 int month = month_index + 1;
9467 int day = tape->date % 100;
9469 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9473 tape_frame_counter = 0;
9475 for (i = 0; i < tape->length; i++)
9477 if (i >= MAX_TAPE_LEN)
9482 for (j = 0; j < MAX_PLAYERS; j++)
9484 if (tape->player_participates[j])
9486 int action = tape->pos[i].action[j];
9488 Print("%d:%02x ", j, action);
9489 Print("[%c%c%c%c|%c%c] - ",
9490 (action & JOY_LEFT ? '<' : ' '),
9491 (action & JOY_RIGHT ? '>' : ' '),
9492 (action & JOY_UP ? '^' : ' '),
9493 (action & JOY_DOWN ? 'v' : ' '),
9494 (action & JOY_BUTTON_1 ? '1' : ' '),
9495 (action & JOY_BUTTON_2 ? '2' : ' '));
9499 Print("(%03d) ", tape->pos[i].delay);
9500 Print("[%05d]\n", tape_frame_counter);
9502 tape_frame_counter += tape->pos[i].delay;
9508 void DumpTapes(void)
9510 static LevelDirTree *dumptape_leveldir = NULL;
9512 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9513 global.dumptape_leveldir);
9515 if (dumptape_leveldir == NULL)
9516 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9518 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9519 global.dumptape_level_nr > dumptape_leveldir->last_level)
9520 Fail("no such level number: %d", global.dumptape_level_nr);
9522 leveldir_current = dumptape_leveldir;
9524 if (options.mytapes)
9525 LoadTape(global.dumptape_level_nr);
9527 LoadSolutionTape(global.dumptape_level_nr);
9535 // ============================================================================
9536 // score file functions
9537 // ============================================================================
9539 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9543 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9545 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9546 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9547 scores->entry[i].score = 0;
9548 scores->entry[i].time = 0;
9550 scores->entry[i].id = -1;
9551 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9552 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9553 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9554 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9555 strcpy(scores->entry[i].country_code, "??");
9558 scores->num_entries = 0;
9559 scores->last_added = -1;
9560 scores->last_added_local = -1;
9562 scores->updated = FALSE;
9563 scores->uploaded = FALSE;
9564 scores->tape_downloaded = FALSE;
9565 scores->force_last_added = FALSE;
9567 // The following values are intentionally not reset here:
9571 // - continue_playing
9572 // - continue_on_return
9575 static void setScoreInfoToDefaults(void)
9577 setScoreInfoToDefaultsExt(&scores);
9580 static void setServerScoreInfoToDefaults(void)
9582 setScoreInfoToDefaultsExt(&server_scores);
9585 static void LoadScore_OLD(int nr)
9588 char *filename = getScoreFilename(nr);
9589 char cookie[MAX_LINE_LEN];
9590 char line[MAX_LINE_LEN];
9594 if (!(file = fopen(filename, MODE_READ)))
9597 // check file identifier
9598 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9600 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9601 cookie[strlen(cookie) - 1] = '\0';
9603 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9605 Warn("unknown format of score file '%s'", filename);
9612 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9614 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9615 Warn("fscanf() failed; %s", strerror(errno));
9617 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9620 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9621 line[strlen(line) - 1] = '\0';
9623 for (line_ptr = line; *line_ptr; line_ptr++)
9625 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9627 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9628 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9637 static void ConvertScore_OLD(void)
9639 // only convert score to time for levels that rate playing time over score
9640 if (!level.rate_time_over_score)
9643 // convert old score to playing time for score-less levels (like Supaplex)
9644 int time_final_max = 999;
9647 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9649 int score = scores.entry[i].score;
9651 if (score > 0 && score < time_final_max)
9652 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9656 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9658 scores->file_version = getFileVersion(file);
9659 scores->game_version = getFileVersion(file);
9664 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9666 char *level_identifier = NULL;
9667 int level_identifier_size;
9670 level_identifier_size = getFile16BitBE(file);
9672 level_identifier = checked_malloc(level_identifier_size);
9674 for (i = 0; i < level_identifier_size; i++)
9675 level_identifier[i] = getFile8Bit(file);
9677 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9678 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9680 checked_free(level_identifier);
9682 scores->level_nr = getFile16BitBE(file);
9683 scores->num_entries = getFile16BitBE(file);
9685 chunk_size = 2 + level_identifier_size + 2 + 2;
9690 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9694 for (i = 0; i < scores->num_entries; i++)
9696 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9697 scores->entry[i].name[j] = getFile8Bit(file);
9699 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9702 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9707 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9711 for (i = 0; i < scores->num_entries; i++)
9712 scores->entry[i].score = getFile16BitBE(file);
9714 chunk_size = scores->num_entries * 2;
9719 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9723 for (i = 0; i < scores->num_entries; i++)
9724 scores->entry[i].score = getFile32BitBE(file);
9726 chunk_size = scores->num_entries * 4;
9731 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9735 for (i = 0; i < scores->num_entries; i++)
9736 scores->entry[i].time = getFile32BitBE(file);
9738 chunk_size = scores->num_entries * 4;
9743 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9747 for (i = 0; i < scores->num_entries; i++)
9749 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9750 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9752 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9755 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9760 void LoadScore(int nr)
9762 char *filename = getScoreFilename(nr);
9763 char cookie[MAX_LINE_LEN];
9764 char chunk_name[CHUNK_ID_LEN + 1];
9766 boolean old_score_file_format = FALSE;
9769 // always start with reliable default values
9770 setScoreInfoToDefaults();
9772 if (!(file = openFile(filename, MODE_READ)))
9775 getFileChunkBE(file, chunk_name, NULL);
9776 if (strEqual(chunk_name, "RND1"))
9778 getFile32BitBE(file); // not used
9780 getFileChunkBE(file, chunk_name, NULL);
9781 if (!strEqual(chunk_name, "SCOR"))
9783 Warn("unknown format of score file '%s'", filename);
9790 else // check for old file format with cookie string
9792 strcpy(cookie, chunk_name);
9793 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9795 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9796 cookie[strlen(cookie) - 1] = '\0';
9798 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9800 Warn("unknown format of score file '%s'", filename);
9807 old_score_file_format = TRUE;
9810 if (old_score_file_format)
9812 // score files from versions before 4.2.4.0 without chunk structure
9815 // convert score to time, if possible (mainly for Supaplex levels)
9824 int (*loader)(File *, int, struct ScoreInfo *);
9828 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9829 { "INFO", -1, LoadScore_INFO },
9830 { "NAME", -1, LoadScore_NAME },
9831 { "SCOR", -1, LoadScore_SCOR },
9832 { "SC4R", -1, LoadScore_SC4R },
9833 { "TIME", -1, LoadScore_TIME },
9834 { "TAPE", -1, LoadScore_TAPE },
9839 while (getFileChunkBE(file, chunk_name, &chunk_size))
9843 while (chunk_info[i].name != NULL &&
9844 !strEqual(chunk_name, chunk_info[i].name))
9847 if (chunk_info[i].name == NULL)
9849 Warn("unknown chunk '%s' in score file '%s'",
9850 chunk_name, filename);
9852 ReadUnusedBytesFromFile(file, chunk_size);
9854 else if (chunk_info[i].size != -1 &&
9855 chunk_info[i].size != chunk_size)
9857 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9858 chunk_size, chunk_name, filename);
9860 ReadUnusedBytesFromFile(file, chunk_size);
9864 // call function to load this score chunk
9865 int chunk_size_expected =
9866 (chunk_info[i].loader)(file, chunk_size, &scores);
9868 // the size of some chunks cannot be checked before reading other
9869 // chunks first (like "HEAD" and "BODY") that contain some header
9870 // information, so check them here
9871 if (chunk_size_expected != chunk_size)
9873 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9874 chunk_size, chunk_name, filename);
9883 #if ENABLE_HISTORIC_CHUNKS
9884 void SaveScore_OLD(int nr)
9887 char *filename = getScoreFilename(nr);
9890 // used instead of "leveldir_current->subdir" (for network games)
9891 InitScoreDirectory(levelset.identifier);
9893 if (!(file = fopen(filename, MODE_WRITE)))
9895 Warn("cannot save score for level %d", nr);
9900 fprintf(file, "%s\n\n", SCORE_COOKIE);
9902 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9903 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9907 SetFilePermissions(filename, PERMS_PRIVATE);
9911 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9913 putFileVersion(file, scores->file_version);
9914 putFileVersion(file, scores->game_version);
9917 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9919 int level_identifier_size = strlen(scores->level_identifier) + 1;
9922 putFile16BitBE(file, level_identifier_size);
9924 for (i = 0; i < level_identifier_size; i++)
9925 putFile8Bit(file, scores->level_identifier[i]);
9927 putFile16BitBE(file, scores->level_nr);
9928 putFile16BitBE(file, scores->num_entries);
9931 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9935 for (i = 0; i < scores->num_entries; i++)
9937 int name_size = strlen(scores->entry[i].name);
9939 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9940 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9944 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9948 for (i = 0; i < scores->num_entries; i++)
9949 putFile16BitBE(file, scores->entry[i].score);
9952 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9956 for (i = 0; i < scores->num_entries; i++)
9957 putFile32BitBE(file, scores->entry[i].score);
9960 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9964 for (i = 0; i < scores->num_entries; i++)
9965 putFile32BitBE(file, scores->entry[i].time);
9968 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9972 for (i = 0; i < scores->num_entries; i++)
9974 int size = strlen(scores->entry[i].tape_basename);
9976 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9977 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9981 static void SaveScoreToFilename(char *filename)
9984 int info_chunk_size;
9985 int name_chunk_size;
9986 int scor_chunk_size;
9987 int sc4r_chunk_size;
9988 int time_chunk_size;
9989 int tape_chunk_size;
9990 boolean has_large_score_values;
9993 if (!(file = fopen(filename, MODE_WRITE)))
9995 Warn("cannot save score file '%s'", filename);
10000 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10001 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10002 scor_chunk_size = scores.num_entries * 2;
10003 sc4r_chunk_size = scores.num_entries * 4;
10004 time_chunk_size = scores.num_entries * 4;
10005 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10007 has_large_score_values = FALSE;
10008 for (i = 0; i < scores.num_entries; i++)
10009 if (scores.entry[i].score > 0xffff)
10010 has_large_score_values = TRUE;
10012 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10013 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10015 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10016 SaveScore_VERS(file, &scores);
10018 putFileChunkBE(file, "INFO", info_chunk_size);
10019 SaveScore_INFO(file, &scores);
10021 putFileChunkBE(file, "NAME", name_chunk_size);
10022 SaveScore_NAME(file, &scores);
10024 if (has_large_score_values)
10026 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10027 SaveScore_SC4R(file, &scores);
10031 putFileChunkBE(file, "SCOR", scor_chunk_size);
10032 SaveScore_SCOR(file, &scores);
10035 putFileChunkBE(file, "TIME", time_chunk_size);
10036 SaveScore_TIME(file, &scores);
10038 putFileChunkBE(file, "TAPE", tape_chunk_size);
10039 SaveScore_TAPE(file, &scores);
10043 SetFilePermissions(filename, PERMS_PRIVATE);
10046 void SaveScore(int nr)
10048 char *filename = getScoreFilename(nr);
10051 // used instead of "leveldir_current->subdir" (for network games)
10052 InitScoreDirectory(levelset.identifier);
10054 scores.file_version = FILE_VERSION_ACTUAL;
10055 scores.game_version = GAME_VERSION_ACTUAL;
10057 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10058 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10059 scores.level_nr = level_nr;
10061 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10062 if (scores.entry[i].score == 0 &&
10063 scores.entry[i].time == 0 &&
10064 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10067 scores.num_entries = i;
10069 if (scores.num_entries == 0)
10072 SaveScoreToFilename(filename);
10075 static void LoadServerScoreFromCache(int nr)
10077 struct ScoreEntry score_entry;
10086 { &score_entry.score, FALSE, 0 },
10087 { &score_entry.time, FALSE, 0 },
10088 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10089 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10090 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10091 { &score_entry.id, FALSE, 0 },
10092 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10093 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10094 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10095 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10099 char *filename = getScoreCacheFilename(nr);
10100 SetupFileHash *score_hash = loadSetupFileHash(filename);
10103 server_scores.num_entries = 0;
10105 if (score_hash == NULL)
10108 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10110 score_entry = server_scores.entry[i];
10112 for (j = 0; score_mapping[j].value != NULL; j++)
10116 sprintf(token, "%02d.%d", i, j);
10118 char *value = getHashEntry(score_hash, token);
10123 if (score_mapping[j].is_string)
10125 char *score_value = (char *)score_mapping[j].value;
10126 int value_size = score_mapping[j].string_size;
10128 strncpy(score_value, value, value_size);
10129 score_value[value_size] = '\0';
10133 int *score_value = (int *)score_mapping[j].value;
10135 *score_value = atoi(value);
10138 server_scores.num_entries = i + 1;
10141 server_scores.entry[i] = score_entry;
10144 freeSetupFileHash(score_hash);
10147 void LoadServerScore(int nr, boolean download_score)
10149 if (!setup.use_api_server)
10152 // always start with reliable default values
10153 setServerScoreInfoToDefaults();
10155 // 1st step: load server scores from cache file (which may not exist)
10156 // (this should prevent reading it while the thread is writing to it)
10157 LoadServerScoreFromCache(nr);
10159 if (download_score && runtime.use_api_server)
10161 // 2nd step: download server scores from score server to cache file
10162 // (as thread, as it might time out if the server is not reachable)
10163 ApiGetScoreAsThread(nr);
10167 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10169 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10171 // if score tape not uploaded, ask for uploading missing tapes later
10172 if (!setup.has_remaining_tapes)
10173 setup.ask_for_remaining_tapes = TRUE;
10175 setup.provide_uploading_tapes = TRUE;
10176 setup.has_remaining_tapes = TRUE;
10178 SaveSetup_ServerSetup();
10181 void SaveServerScore(int nr, boolean tape_saved)
10183 if (!runtime.use_api_server)
10185 PrepareScoreTapesForUpload(leveldir_current->subdir);
10190 ApiAddScoreAsThread(nr, tape_saved, NULL);
10193 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10194 char *score_tape_filename)
10196 if (!runtime.use_api_server)
10199 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10202 void LoadLocalAndServerScore(int nr, boolean download_score)
10204 int last_added_local = scores.last_added_local;
10205 boolean force_last_added = scores.force_last_added;
10207 // needed if only showing server scores
10208 setScoreInfoToDefaults();
10210 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10213 // restore last added local score entry (before merging server scores)
10214 scores.last_added = scores.last_added_local = last_added_local;
10216 if (setup.use_api_server &&
10217 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10219 // load server scores from cache file and trigger update from server
10220 LoadServerScore(nr, download_score);
10222 // merge local scores with scores from server
10223 MergeServerScore();
10226 if (force_last_added)
10227 scores.force_last_added = force_last_added;
10231 // ============================================================================
10232 // setup file functions
10233 // ============================================================================
10235 #define TOKEN_STR_PLAYER_PREFIX "player_"
10238 static struct TokenInfo global_setup_tokens[] =
10242 &setup.player_name, "player_name"
10246 &setup.multiple_users, "multiple_users"
10250 &setup.sound, "sound"
10254 &setup.sound_loops, "repeating_sound_loops"
10258 &setup.sound_music, "background_music"
10262 &setup.sound_simple, "simple_sound_effects"
10266 &setup.toons, "toons"
10270 &setup.global_animations, "global_animations"
10274 &setup.scroll_delay, "scroll_delay"
10278 &setup.forced_scroll_delay, "forced_scroll_delay"
10282 &setup.scroll_delay_value, "scroll_delay_value"
10286 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10290 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10294 &setup.fade_screens, "fade_screens"
10298 &setup.autorecord, "automatic_tape_recording"
10302 &setup.autorecord_after_replay, "autorecord_after_replay"
10306 &setup.auto_pause_on_start, "auto_pause_on_start"
10310 &setup.show_titlescreen, "show_titlescreen"
10314 &setup.quick_doors, "quick_doors"
10318 &setup.team_mode, "team_mode"
10322 &setup.handicap, "handicap"
10326 &setup.skip_levels, "skip_levels"
10330 &setup.increment_levels, "increment_levels"
10334 &setup.auto_play_next_level, "auto_play_next_level"
10338 &setup.count_score_after_game, "count_score_after_game"
10342 &setup.show_scores_after_game, "show_scores_after_game"
10346 &setup.time_limit, "time_limit"
10350 &setup.fullscreen, "fullscreen"
10354 &setup.window_scaling_percent, "window_scaling_percent"
10358 &setup.window_scaling_quality, "window_scaling_quality"
10362 &setup.screen_rendering_mode, "screen_rendering_mode"
10366 &setup.vsync_mode, "vsync_mode"
10370 &setup.ask_on_escape, "ask_on_escape"
10374 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10378 &setup.ask_on_game_over, "ask_on_game_over"
10382 &setup.ask_on_quit_game, "ask_on_quit_game"
10386 &setup.ask_on_quit_program, "ask_on_quit_program"
10390 &setup.quick_switch, "quick_player_switch"
10394 &setup.input_on_focus, "input_on_focus"
10398 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10402 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10406 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10410 &setup.game_speed_extended, "game_speed_extended"
10414 &setup.game_frame_delay, "game_frame_delay"
10418 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10422 &setup.bd_skip_hatching, "bd_skip_hatching"
10426 &setup.bd_scroll_delay, "bd_scroll_delay"
10430 &setup.bd_smooth_movements, "bd_smooth_movements"
10434 &setup.sp_show_border_elements, "sp_show_border_elements"
10438 &setup.small_game_graphics, "small_game_graphics"
10442 &setup.show_load_save_buttons, "show_load_save_buttons"
10446 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10450 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10454 &setup.graphics_set, "graphics_set"
10458 &setup.sounds_set, "sounds_set"
10462 &setup.music_set, "music_set"
10466 &setup.override_level_graphics, "override_level_graphics"
10470 &setup.override_level_sounds, "override_level_sounds"
10474 &setup.override_level_music, "override_level_music"
10478 &setup.volume_simple, "volume_simple"
10482 &setup.volume_loops, "volume_loops"
10486 &setup.volume_music, "volume_music"
10490 &setup.network_mode, "network_mode"
10494 &setup.network_player_nr, "network_player"
10498 &setup.network_server_hostname, "network_server_hostname"
10502 &setup.touch.control_type, "touch.control_type"
10506 &setup.touch.move_distance, "touch.move_distance"
10510 &setup.touch.drop_distance, "touch.drop_distance"
10514 &setup.touch.transparency, "touch.transparency"
10518 &setup.touch.draw_outlined, "touch.draw_outlined"
10522 &setup.touch.draw_pressed, "touch.draw_pressed"
10526 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10530 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10534 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10538 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10542 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10546 static struct TokenInfo auto_setup_tokens[] =
10550 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10554 static struct TokenInfo server_setup_tokens[] =
10558 &setup.player_uuid, "player_uuid"
10562 &setup.player_version, "player_version"
10566 &setup.use_api_server, TEST_PREFIX "use_api_server"
10570 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10574 &setup.api_server_password, TEST_PREFIX "api_server_password"
10578 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10582 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10586 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10590 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10594 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10598 static struct TokenInfo editor_setup_tokens[] =
10602 &setup.editor.el_classic, "editor.el_classic"
10606 &setup.editor.el_custom, "editor.el_custom"
10610 &setup.editor.el_user_defined, "editor.el_user_defined"
10614 &setup.editor.el_dynamic, "editor.el_dynamic"
10618 &setup.editor.el_headlines, "editor.el_headlines"
10622 &setup.editor.show_element_token, "editor.show_element_token"
10626 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10630 static struct TokenInfo editor_cascade_setup_tokens[] =
10634 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10638 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10642 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10646 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10650 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10654 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10658 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10662 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10666 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10670 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10674 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10678 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10682 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10686 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10690 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10694 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10698 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10702 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10706 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10710 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10714 static struct TokenInfo shortcut_setup_tokens[] =
10718 &setup.shortcut.save_game, "shortcut.save_game"
10722 &setup.shortcut.load_game, "shortcut.load_game"
10726 &setup.shortcut.restart_game, "shortcut.restart_game"
10730 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10734 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10738 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10742 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10746 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10750 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10754 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10758 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10762 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10766 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10770 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10774 &setup.shortcut.tape_record, "shortcut.tape_record"
10778 &setup.shortcut.tape_play, "shortcut.tape_play"
10782 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10786 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10790 &setup.shortcut.sound_music, "shortcut.sound_music"
10794 &setup.shortcut.snap_left, "shortcut.snap_left"
10798 &setup.shortcut.snap_right, "shortcut.snap_right"
10802 &setup.shortcut.snap_up, "shortcut.snap_up"
10806 &setup.shortcut.snap_down, "shortcut.snap_down"
10810 static struct SetupInputInfo setup_input;
10811 static struct TokenInfo player_setup_tokens[] =
10815 &setup_input.use_joystick, ".use_joystick"
10819 &setup_input.joy.device_name, ".joy.device_name"
10823 &setup_input.joy.xleft, ".joy.xleft"
10827 &setup_input.joy.xmiddle, ".joy.xmiddle"
10831 &setup_input.joy.xright, ".joy.xright"
10835 &setup_input.joy.yupper, ".joy.yupper"
10839 &setup_input.joy.ymiddle, ".joy.ymiddle"
10843 &setup_input.joy.ylower, ".joy.ylower"
10847 &setup_input.joy.snap, ".joy.snap_field"
10851 &setup_input.joy.drop, ".joy.place_bomb"
10855 &setup_input.key.left, ".key.move_left"
10859 &setup_input.key.right, ".key.move_right"
10863 &setup_input.key.up, ".key.move_up"
10867 &setup_input.key.down, ".key.move_down"
10871 &setup_input.key.snap, ".key.snap_field"
10875 &setup_input.key.drop, ".key.place_bomb"
10879 static struct TokenInfo system_setup_tokens[] =
10883 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10887 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10891 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10895 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10899 static struct TokenInfo internal_setup_tokens[] =
10903 &setup.internal.program_title, "program_title"
10907 &setup.internal.program_version, "program_version"
10911 &setup.internal.program_author, "program_author"
10915 &setup.internal.program_email, "program_email"
10919 &setup.internal.program_website, "program_website"
10923 &setup.internal.program_copyright, "program_copyright"
10927 &setup.internal.program_company, "program_company"
10931 &setup.internal.program_icon_file, "program_icon_file"
10935 &setup.internal.default_graphics_set, "default_graphics_set"
10939 &setup.internal.default_sounds_set, "default_sounds_set"
10943 &setup.internal.default_music_set, "default_music_set"
10947 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10951 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10955 &setup.internal.fallback_music_file, "fallback_music_file"
10959 &setup.internal.default_level_series, "default_level_series"
10963 &setup.internal.default_window_width, "default_window_width"
10967 &setup.internal.default_window_height, "default_window_height"
10971 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10975 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10979 &setup.internal.create_user_levelset, "create_user_levelset"
10983 &setup.internal.info_screens_from_main, "info_screens_from_main"
10987 &setup.internal.menu_game, "menu_game"
10991 &setup.internal.menu_engines, "menu_engines"
10995 &setup.internal.menu_editor, "menu_editor"
10999 &setup.internal.menu_graphics, "menu_graphics"
11003 &setup.internal.menu_sound, "menu_sound"
11007 &setup.internal.menu_artwork, "menu_artwork"
11011 &setup.internal.menu_input, "menu_input"
11015 &setup.internal.menu_touch, "menu_touch"
11019 &setup.internal.menu_shortcuts, "menu_shortcuts"
11023 &setup.internal.menu_exit, "menu_exit"
11027 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
11031 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
11035 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
11039 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11043 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11047 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11051 &setup.internal.info_title, "info_title"
11055 &setup.internal.info_elements, "info_elements"
11059 &setup.internal.info_music, "info_music"
11063 &setup.internal.info_credits, "info_credits"
11067 &setup.internal.info_program, "info_program"
11071 &setup.internal.info_version, "info_version"
11075 &setup.internal.info_levelset, "info_levelset"
11079 &setup.internal.info_exit, "info_exit"
11083 static struct TokenInfo debug_setup_tokens[] =
11087 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11091 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11095 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11099 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11103 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11107 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11111 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11115 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11119 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11123 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11127 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11131 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11135 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11139 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11143 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11147 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11151 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11155 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11159 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11163 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11167 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11170 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11174 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11178 &setup.debug.xsn_mode, "debug.xsn_mode"
11182 &setup.debug.xsn_percent, "debug.xsn_percent"
11186 static struct TokenInfo options_setup_tokens[] =
11190 &setup.options.verbose, "options.verbose"
11194 &setup.options.debug, "options.debug"
11198 &setup.options.debug_mode, "options.debug_mode"
11202 static void setSetupInfoToDefaults(struct SetupInfo *si)
11206 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11208 si->multiple_users = TRUE;
11211 si->sound_loops = TRUE;
11212 si->sound_music = TRUE;
11213 si->sound_simple = TRUE;
11215 si->global_animations = TRUE;
11216 si->scroll_delay = TRUE;
11217 si->forced_scroll_delay = FALSE;
11218 si->scroll_delay_value = STD_SCROLL_DELAY;
11219 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11220 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11221 si->fade_screens = TRUE;
11222 si->autorecord = TRUE;
11223 si->autorecord_after_replay = TRUE;
11224 si->auto_pause_on_start = FALSE;
11225 si->show_titlescreen = TRUE;
11226 si->quick_doors = FALSE;
11227 si->team_mode = FALSE;
11228 si->handicap = TRUE;
11229 si->skip_levels = TRUE;
11230 si->increment_levels = TRUE;
11231 si->auto_play_next_level = TRUE;
11232 si->count_score_after_game = TRUE;
11233 si->show_scores_after_game = TRUE;
11234 si->time_limit = TRUE;
11235 si->fullscreen = FALSE;
11236 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11237 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11238 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11239 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11240 si->ask_on_escape = TRUE;
11241 si->ask_on_escape_editor = TRUE;
11242 si->ask_on_game_over = TRUE;
11243 si->ask_on_quit_game = TRUE;
11244 si->ask_on_quit_program = TRUE;
11245 si->quick_switch = FALSE;
11246 si->input_on_focus = FALSE;
11247 si->prefer_aga_graphics = TRUE;
11248 si->prefer_lowpass_sounds = FALSE;
11249 si->prefer_extra_panel_items = TRUE;
11250 si->game_speed_extended = FALSE;
11251 si->game_frame_delay = GAME_FRAME_DELAY;
11252 si->bd_skip_uncovering = FALSE;
11253 si->bd_skip_hatching = FALSE;
11254 si->bd_scroll_delay = TRUE;
11255 si->bd_smooth_movements = AUTO;
11256 si->sp_show_border_elements = FALSE;
11257 si->small_game_graphics = FALSE;
11258 si->show_load_save_buttons = FALSE;
11259 si->show_undo_redo_buttons = FALSE;
11260 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11262 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11263 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11264 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11266 si->override_level_graphics = FALSE;
11267 si->override_level_sounds = FALSE;
11268 si->override_level_music = FALSE;
11270 si->volume_simple = 100; // percent
11271 si->volume_loops = 100; // percent
11272 si->volume_music = 100; // percent
11274 si->network_mode = FALSE;
11275 si->network_player_nr = 0; // first player
11276 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11278 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11279 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11280 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11281 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11282 si->touch.draw_outlined = TRUE;
11283 si->touch.draw_pressed = TRUE;
11285 for (i = 0; i < 2; i++)
11287 char *default_grid_button[6][2] =
11293 { "111222", " vv " },
11294 { "111222", " vv " }
11296 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11297 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11298 int min_xsize = MIN(6, grid_xsize);
11299 int min_ysize = MIN(6, grid_ysize);
11300 int startx = grid_xsize - min_xsize;
11301 int starty = grid_ysize - min_ysize;
11304 // virtual buttons grid can only be set to defaults if video is initialized
11305 // (this will be repeated if virtual buttons are not loaded from setup file)
11306 if (video.initialized)
11308 si->touch.grid_xsize[i] = grid_xsize;
11309 si->touch.grid_ysize[i] = grid_ysize;
11313 si->touch.grid_xsize[i] = -1;
11314 si->touch.grid_ysize[i] = -1;
11317 for (x = 0; x < MAX_GRID_XSIZE; x++)
11318 for (y = 0; y < MAX_GRID_YSIZE; y++)
11319 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11321 for (x = 0; x < min_xsize; x++)
11322 for (y = 0; y < min_ysize; y++)
11323 si->touch.grid_button[i][x][starty + y] =
11324 default_grid_button[y][0][x];
11326 for (x = 0; x < min_xsize; x++)
11327 for (y = 0; y < min_ysize; y++)
11328 si->touch.grid_button[i][startx + x][starty + y] =
11329 default_grid_button[y][1][x];
11332 si->touch.grid_initialized = video.initialized;
11334 si->touch.overlay_buttons = FALSE;
11336 si->editor.el_boulderdash = TRUE;
11337 si->editor.el_boulderdash_native = TRUE;
11338 si->editor.el_boulderdash_effects = TRUE;
11339 si->editor.el_emerald_mine = TRUE;
11340 si->editor.el_emerald_mine_club = TRUE;
11341 si->editor.el_more = TRUE;
11342 si->editor.el_sokoban = TRUE;
11343 si->editor.el_supaplex = TRUE;
11344 si->editor.el_diamond_caves = TRUE;
11345 si->editor.el_dx_boulderdash = TRUE;
11347 si->editor.el_mirror_magic = TRUE;
11348 si->editor.el_deflektor = TRUE;
11350 si->editor.el_chars = TRUE;
11351 si->editor.el_steel_chars = TRUE;
11353 si->editor.el_classic = TRUE;
11354 si->editor.el_custom = TRUE;
11356 si->editor.el_user_defined = FALSE;
11357 si->editor.el_dynamic = TRUE;
11359 si->editor.el_headlines = TRUE;
11361 si->editor.show_element_token = FALSE;
11363 si->editor.show_read_only_warning = TRUE;
11365 si->editor.use_template_for_new_levels = TRUE;
11367 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11368 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11369 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11370 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11371 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11373 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11374 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11375 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11376 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11377 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11379 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11380 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11381 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11382 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11383 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11384 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11386 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11387 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11388 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11390 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11391 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11392 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11393 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11395 for (i = 0; i < MAX_PLAYERS; i++)
11397 si->input[i].use_joystick = FALSE;
11398 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11399 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11400 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11401 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11402 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11403 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11404 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11405 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11406 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11407 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11408 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11409 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11410 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11411 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11412 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11415 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11416 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11417 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11418 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11420 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11421 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11422 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11423 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11424 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11425 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11426 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11428 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11430 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11431 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11432 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11434 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11435 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11436 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11438 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11439 si->internal.choose_from_top_leveldir = FALSE;
11440 si->internal.show_scaling_in_title = TRUE;
11441 si->internal.create_user_levelset = TRUE;
11442 si->internal.info_screens_from_main = FALSE;
11444 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11445 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11447 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11448 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11449 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11450 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11451 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11452 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11453 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11454 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11455 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11456 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11458 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11459 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11460 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11461 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11462 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11463 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11464 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11465 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11466 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11467 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11469 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11470 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11472 si->debug.show_frames_per_second = FALSE;
11474 si->debug.xsn_mode = AUTO;
11475 si->debug.xsn_percent = 0;
11477 si->options.verbose = FALSE;
11478 si->options.debug = FALSE;
11479 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11481 #if defined(PLATFORM_ANDROID)
11482 si->fullscreen = TRUE;
11483 si->touch.overlay_buttons = TRUE;
11486 setHideSetupEntry(&setup.debug.xsn_mode);
11489 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11491 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11494 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11496 si->player_uuid = NULL; // (will be set later)
11497 si->player_version = 1; // (will be set later)
11499 si->use_api_server = TRUE;
11500 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11501 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11502 si->ask_for_uploading_tapes = TRUE;
11503 si->ask_for_remaining_tapes = FALSE;
11504 si->provide_uploading_tapes = TRUE;
11505 si->ask_for_using_api_server = TRUE;
11506 si->has_remaining_tapes = FALSE;
11509 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11511 si->editor_cascade.el_bd = TRUE;
11512 si->editor_cascade.el_bd_native = TRUE;
11513 si->editor_cascade.el_bd_effects = FALSE;
11514 si->editor_cascade.el_em = TRUE;
11515 si->editor_cascade.el_emc = TRUE;
11516 si->editor_cascade.el_rnd = TRUE;
11517 si->editor_cascade.el_sb = TRUE;
11518 si->editor_cascade.el_sp = TRUE;
11519 si->editor_cascade.el_dc = TRUE;
11520 si->editor_cascade.el_dx = TRUE;
11522 si->editor_cascade.el_mm = TRUE;
11523 si->editor_cascade.el_df = TRUE;
11525 si->editor_cascade.el_chars = FALSE;
11526 si->editor_cascade.el_steel_chars = FALSE;
11527 si->editor_cascade.el_ce = FALSE;
11528 si->editor_cascade.el_ge = FALSE;
11529 si->editor_cascade.el_es = FALSE;
11530 si->editor_cascade.el_ref = FALSE;
11531 si->editor_cascade.el_user = FALSE;
11532 si->editor_cascade.el_dynamic = FALSE;
11535 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11537 static char *getHideSetupToken(void *setup_value)
11539 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11541 if (setup_value != NULL)
11542 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11544 return hide_setup_token;
11547 void setHideSetupEntry(void *setup_value)
11549 char *hide_setup_token = getHideSetupToken(setup_value);
11551 if (hide_setup_hash == NULL)
11552 hide_setup_hash = newSetupFileHash();
11554 if (setup_value != NULL)
11555 setHashEntry(hide_setup_hash, hide_setup_token, "");
11558 void removeHideSetupEntry(void *setup_value)
11560 char *hide_setup_token = getHideSetupToken(setup_value);
11562 if (setup_value != NULL)
11563 removeHashEntry(hide_setup_hash, hide_setup_token);
11566 boolean hideSetupEntry(void *setup_value)
11568 char *hide_setup_token = getHideSetupToken(setup_value);
11570 return (setup_value != NULL &&
11571 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11574 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11575 struct TokenInfo *token_info,
11576 int token_nr, char *token_text)
11578 char *token_hide_text = getStringCat2(token_text, ".hide");
11579 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11581 // set the value of this setup option in the setup option structure
11582 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11584 // check if this setup option should be hidden in the setup menu
11585 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11586 setHideSetupEntry(token_info[token_nr].value);
11588 free(token_hide_text);
11591 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11592 struct TokenInfo *token_info,
11595 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11596 token_info[token_nr].text);
11599 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11603 if (!setup_file_hash)
11606 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11607 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11609 setup.touch.grid_initialized = TRUE;
11610 for (i = 0; i < 2; i++)
11612 int grid_xsize = setup.touch.grid_xsize[i];
11613 int grid_ysize = setup.touch.grid_ysize[i];
11616 // if virtual buttons are not loaded from setup file, repeat initializing
11617 // virtual buttons grid with default values later when video is initialized
11618 if (grid_xsize == -1 ||
11621 setup.touch.grid_initialized = FALSE;
11626 for (y = 0; y < grid_ysize; y++)
11628 char token_string[MAX_LINE_LEN];
11630 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11632 char *value_string = getHashEntry(setup_file_hash, token_string);
11634 if (value_string == NULL)
11637 for (x = 0; x < grid_xsize; x++)
11639 char c = value_string[x];
11641 setup.touch.grid_button[i][x][y] =
11642 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11647 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11648 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11650 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11651 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11653 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11657 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11659 setup_input = setup.input[pnr];
11660 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11662 char full_token[100];
11664 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11665 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11668 setup.input[pnr] = setup_input;
11671 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11672 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11674 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11675 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11677 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11678 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11680 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11681 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11683 setHideRelatedSetupEntries();
11686 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11690 if (!setup_file_hash)
11693 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11694 setSetupInfo(auto_setup_tokens, i,
11695 getHashEntry(setup_file_hash,
11696 auto_setup_tokens[i].text));
11699 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11703 if (!setup_file_hash)
11706 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11707 setSetupInfo(server_setup_tokens, i,
11708 getHashEntry(setup_file_hash,
11709 server_setup_tokens[i].text));
11712 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11716 if (!setup_file_hash)
11719 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11720 setSetupInfo(editor_cascade_setup_tokens, i,
11721 getHashEntry(setup_file_hash,
11722 editor_cascade_setup_tokens[i].text));
11725 void LoadUserNames(void)
11727 int last_user_nr = user.nr;
11730 if (global.user_names != NULL)
11732 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11733 checked_free(global.user_names[i]);
11735 checked_free(global.user_names);
11738 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11740 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11744 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11746 if (setup_file_hash)
11748 char *player_name = getHashEntry(setup_file_hash, "player_name");
11750 global.user_names[i] = getFixedUserName(player_name);
11752 freeSetupFileHash(setup_file_hash);
11755 if (global.user_names[i] == NULL)
11756 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11759 user.nr = last_user_nr;
11762 void LoadSetupFromFilename(char *filename)
11764 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11766 if (setup_file_hash)
11768 decodeSetupFileHash_Default(setup_file_hash);
11770 freeSetupFileHash(setup_file_hash);
11774 Debug("setup", "using default setup values");
11778 static void LoadSetup_SpecialPostProcessing(void)
11780 char *player_name_new;
11782 // needed to work around problems with fixed length strings
11783 player_name_new = getFixedUserName(setup.player_name);
11784 free(setup.player_name);
11785 setup.player_name = player_name_new;
11787 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11788 if (setup.scroll_delay == FALSE)
11790 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11791 setup.scroll_delay = TRUE; // now always "on"
11794 // make sure that scroll delay value stays inside valid range
11795 setup.scroll_delay_value =
11796 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11799 void LoadSetup_Default(void)
11803 // always start with reliable default values
11804 setSetupInfoToDefaults(&setup);
11806 // try to load setup values from default setup file
11807 filename = getDefaultSetupFilename();
11809 if (fileExists(filename))
11810 LoadSetupFromFilename(filename);
11812 // try to load setup values from platform setup file
11813 filename = getPlatformSetupFilename();
11815 if (fileExists(filename))
11816 LoadSetupFromFilename(filename);
11818 // try to load setup values from user setup file
11819 filename = getSetupFilename();
11821 LoadSetupFromFilename(filename);
11823 LoadSetup_SpecialPostProcessing();
11826 void LoadSetup_AutoSetup(void)
11828 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11829 SetupFileHash *setup_file_hash = NULL;
11831 // always start with reliable default values
11832 setSetupInfoToDefaults_AutoSetup(&setup);
11834 setup_file_hash = loadSetupFileHash(filename);
11836 if (setup_file_hash)
11838 decodeSetupFileHash_AutoSetup(setup_file_hash);
11840 freeSetupFileHash(setup_file_hash);
11846 void LoadSetup_ServerSetup(void)
11848 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11849 SetupFileHash *setup_file_hash = NULL;
11851 // always start with reliable default values
11852 setSetupInfoToDefaults_ServerSetup(&setup);
11854 setup_file_hash = loadSetupFileHash(filename);
11856 if (setup_file_hash)
11858 decodeSetupFileHash_ServerSetup(setup_file_hash);
11860 freeSetupFileHash(setup_file_hash);
11865 if (setup.player_uuid == NULL)
11867 // player UUID does not yet exist in setup file
11868 setup.player_uuid = getStringCopy(getUUID());
11869 setup.player_version = 2;
11871 SaveSetup_ServerSetup();
11875 void LoadSetup_EditorCascade(void)
11877 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11878 SetupFileHash *setup_file_hash = NULL;
11880 // always start with reliable default values
11881 setSetupInfoToDefaults_EditorCascade(&setup);
11883 setup_file_hash = loadSetupFileHash(filename);
11885 if (setup_file_hash)
11887 decodeSetupFileHash_EditorCascade(setup_file_hash);
11889 freeSetupFileHash(setup_file_hash);
11895 void LoadSetup(void)
11897 LoadSetup_Default();
11898 LoadSetup_AutoSetup();
11899 LoadSetup_ServerSetup();
11900 LoadSetup_EditorCascade();
11903 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11904 char *mapping_line)
11906 char mapping_guid[MAX_LINE_LEN];
11907 char *mapping_start, *mapping_end;
11909 // get GUID from game controller mapping line: copy complete line
11910 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11911 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11913 // get GUID from game controller mapping line: cut after GUID part
11914 mapping_start = strchr(mapping_guid, ',');
11915 if (mapping_start != NULL)
11916 *mapping_start = '\0';
11918 // cut newline from game controller mapping line
11919 mapping_end = strchr(mapping_line, '\n');
11920 if (mapping_end != NULL)
11921 *mapping_end = '\0';
11923 // add mapping entry to game controller mappings hash
11924 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11927 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11932 if (!(file = fopen(filename, MODE_READ)))
11934 Warn("cannot read game controller mappings file '%s'", filename);
11939 while (!feof(file))
11941 char line[MAX_LINE_LEN];
11943 if (!fgets(line, MAX_LINE_LEN, file))
11946 addGameControllerMappingToHash(mappings_hash, line);
11952 void SaveSetup_Default(void)
11954 char *filename = getSetupFilename();
11958 InitUserDataDirectory();
11960 if (!(file = fopen(filename, MODE_WRITE)))
11962 Warn("cannot write setup file '%s'", filename);
11967 fprintFileHeader(file, SETUP_FILENAME);
11969 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11971 // just to make things nicer :)
11972 if (global_setup_tokens[i].value == &setup.multiple_users ||
11973 global_setup_tokens[i].value == &setup.sound ||
11974 global_setup_tokens[i].value == &setup.graphics_set ||
11975 global_setup_tokens[i].value == &setup.volume_simple ||
11976 global_setup_tokens[i].value == &setup.network_mode ||
11977 global_setup_tokens[i].value == &setup.touch.control_type ||
11978 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11979 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11980 fprintf(file, "\n");
11982 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11985 for (i = 0; i < 2; i++)
11987 int grid_xsize = setup.touch.grid_xsize[i];
11988 int grid_ysize = setup.touch.grid_ysize[i];
11991 fprintf(file, "\n");
11993 for (y = 0; y < grid_ysize; y++)
11995 char token_string[MAX_LINE_LEN];
11996 char value_string[MAX_LINE_LEN];
11998 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12000 for (x = 0; x < grid_xsize; x++)
12002 char c = setup.touch.grid_button[i][x][y];
12004 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12007 value_string[grid_xsize] = '\0';
12009 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12013 fprintf(file, "\n");
12014 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12015 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12017 fprintf(file, "\n");
12018 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12019 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12021 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12025 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12026 fprintf(file, "\n");
12028 setup_input = setup.input[pnr];
12029 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12030 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12033 fprintf(file, "\n");
12034 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12035 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12037 // (internal setup values not saved to user setup file)
12039 fprintf(file, "\n");
12040 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12041 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12042 setup.debug.xsn_mode != AUTO)
12043 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12045 fprintf(file, "\n");
12046 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12047 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12051 SetFilePermissions(filename, PERMS_PRIVATE);
12054 void SaveSetup_AutoSetup(void)
12056 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12060 InitUserDataDirectory();
12062 if (!(file = fopen(filename, MODE_WRITE)))
12064 Warn("cannot write auto setup file '%s'", filename);
12071 fprintFileHeader(file, AUTOSETUP_FILENAME);
12073 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12074 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12078 SetFilePermissions(filename, PERMS_PRIVATE);
12083 void SaveSetup_ServerSetup(void)
12085 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12089 InitUserDataDirectory();
12091 if (!(file = fopen(filename, MODE_WRITE)))
12093 Warn("cannot write server setup file '%s'", filename);
12100 fprintFileHeader(file, SERVERSETUP_FILENAME);
12102 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12104 // just to make things nicer :)
12105 if (server_setup_tokens[i].value == &setup.use_api_server)
12106 fprintf(file, "\n");
12108 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12113 SetFilePermissions(filename, PERMS_PRIVATE);
12118 void SaveSetup_EditorCascade(void)
12120 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12124 InitUserDataDirectory();
12126 if (!(file = fopen(filename, MODE_WRITE)))
12128 Warn("cannot write editor cascade state file '%s'", filename);
12135 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12137 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12138 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12142 SetFilePermissions(filename, PERMS_PRIVATE);
12147 void SaveSetup(void)
12149 SaveSetup_Default();
12150 SaveSetup_AutoSetup();
12151 SaveSetup_ServerSetup();
12152 SaveSetup_EditorCascade();
12155 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12160 if (!(file = fopen(filename, MODE_WRITE)))
12162 Warn("cannot write game controller mappings file '%s'", filename);
12167 BEGIN_HASH_ITERATION(mappings_hash, itr)
12169 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12171 END_HASH_ITERATION(mappings_hash, itr)
12176 void SaveSetup_AddGameControllerMapping(char *mapping)
12178 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12179 SetupFileHash *mappings_hash = newSetupFileHash();
12181 InitUserDataDirectory();
12183 // load existing personal game controller mappings
12184 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12186 // add new mapping to personal game controller mappings
12187 addGameControllerMappingToHash(mappings_hash, mapping);
12189 // save updated personal game controller mappings
12190 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12192 freeSetupFileHash(mappings_hash);
12196 void LoadCustomElementDescriptions(void)
12198 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12199 SetupFileHash *setup_file_hash;
12202 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12204 if (element_info[i].custom_description != NULL)
12206 free(element_info[i].custom_description);
12207 element_info[i].custom_description = NULL;
12211 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12214 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12216 char *token = getStringCat2(element_info[i].token_name, ".name");
12217 char *value = getHashEntry(setup_file_hash, token);
12220 element_info[i].custom_description = getStringCopy(value);
12225 freeSetupFileHash(setup_file_hash);
12228 static int getElementFromToken(char *token)
12230 char *value = getHashEntry(element_token_hash, token);
12233 return atoi(value);
12235 Warn("unknown element token '%s'", token);
12237 return EL_UNDEFINED;
12240 void FreeGlobalAnimEventInfo(void)
12242 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12244 if (gaei->event_list == NULL)
12249 for (i = 0; i < gaei->num_event_lists; i++)
12251 checked_free(gaei->event_list[i]->event_value);
12252 checked_free(gaei->event_list[i]);
12255 checked_free(gaei->event_list);
12257 gaei->event_list = NULL;
12258 gaei->num_event_lists = 0;
12261 static int AddGlobalAnimEventList(void)
12263 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12264 int list_pos = gaei->num_event_lists++;
12266 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12267 sizeof(struct GlobalAnimEventListInfo *));
12269 gaei->event_list[list_pos] =
12270 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12272 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12274 gaeli->event_value = NULL;
12275 gaeli->num_event_values = 0;
12280 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12282 // do not add empty global animation events
12283 if (event_value == ANIM_EVENT_NONE)
12286 // if list position is undefined, create new list
12287 if (list_pos == ANIM_EVENT_UNDEFINED)
12288 list_pos = AddGlobalAnimEventList();
12290 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12291 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12292 int value_pos = gaeli->num_event_values++;
12294 gaeli->event_value = checked_realloc(gaeli->event_value,
12295 gaeli->num_event_values * sizeof(int *));
12297 gaeli->event_value[value_pos] = event_value;
12302 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12304 if (list_pos == ANIM_EVENT_UNDEFINED)
12305 return ANIM_EVENT_NONE;
12307 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12308 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12310 return gaeli->event_value[value_pos];
12313 int GetGlobalAnimEventValueCount(int list_pos)
12315 if (list_pos == ANIM_EVENT_UNDEFINED)
12318 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12319 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12321 return gaeli->num_event_values;
12324 // This function checks if a string <s> of the format "string1, string2, ..."
12325 // exactly contains a string <s_contained>.
12327 static boolean string_has_parameter(char *s, char *s_contained)
12331 if (s == NULL || s_contained == NULL)
12334 if (strlen(s_contained) > strlen(s))
12337 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12339 char next_char = s[strlen(s_contained)];
12341 // check if next character is delimiter or whitespace
12342 if (next_char == ',' || next_char == '\0' ||
12343 next_char == ' ' || next_char == '\t')
12347 // check if string contains another parameter string after a comma
12348 substring = strchr(s, ',');
12349 if (substring == NULL) // string does not contain a comma
12352 // advance string pointer to next character after the comma
12355 // skip potential whitespaces after the comma
12356 while (*substring == ' ' || *substring == '\t')
12359 return string_has_parameter(substring, s_contained);
12362 static int get_anim_parameter_value_ce(char *s)
12365 char *pattern_1 = "ce_change:custom_";
12366 char *pattern_2 = ".page_";
12367 int pattern_1_len = strlen(pattern_1);
12368 char *matching_char = strstr(s_ptr, pattern_1);
12369 int result = ANIM_EVENT_NONE;
12371 if (matching_char == NULL)
12372 return ANIM_EVENT_NONE;
12374 result = ANIM_EVENT_CE_CHANGE;
12376 s_ptr = matching_char + pattern_1_len;
12378 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12379 if (*s_ptr >= '0' && *s_ptr <= '9')
12381 int gic_ce_nr = (*s_ptr++ - '0');
12383 if (*s_ptr >= '0' && *s_ptr <= '9')
12385 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12387 if (*s_ptr >= '0' && *s_ptr <= '9')
12388 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12391 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12392 return ANIM_EVENT_NONE;
12394 // custom element stored as 0 to 255
12397 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12401 // invalid custom element number specified
12403 return ANIM_EVENT_NONE;
12406 // check for change page number ("page_X" or "page_XX") (optional)
12407 if (strPrefix(s_ptr, pattern_2))
12409 s_ptr += strlen(pattern_2);
12411 if (*s_ptr >= '0' && *s_ptr <= '9')
12413 int gic_page_nr = (*s_ptr++ - '0');
12415 if (*s_ptr >= '0' && *s_ptr <= '9')
12416 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12418 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12419 return ANIM_EVENT_NONE;
12421 // change page stored as 1 to 32 (0 means "all change pages")
12423 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12427 // invalid animation part number specified
12429 return ANIM_EVENT_NONE;
12433 // discard result if next character is neither delimiter nor whitespace
12434 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12435 *s_ptr == ' ' || *s_ptr == '\t'))
12436 return ANIM_EVENT_NONE;
12441 static int get_anim_parameter_value(char *s)
12443 int event_value[] =
12451 char *pattern_1[] =
12459 char *pattern_2 = ".part_";
12460 char *matching_char = NULL;
12462 int pattern_1_len = 0;
12463 int result = ANIM_EVENT_NONE;
12466 result = get_anim_parameter_value_ce(s);
12468 if (result != ANIM_EVENT_NONE)
12471 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12473 matching_char = strstr(s_ptr, pattern_1[i]);
12474 pattern_1_len = strlen(pattern_1[i]);
12475 result = event_value[i];
12477 if (matching_char != NULL)
12481 if (matching_char == NULL)
12482 return ANIM_EVENT_NONE;
12484 s_ptr = matching_char + pattern_1_len;
12486 // check for main animation number ("anim_X" or "anim_XX")
12487 if (*s_ptr >= '0' && *s_ptr <= '9')
12489 int gic_anim_nr = (*s_ptr++ - '0');
12491 if (*s_ptr >= '0' && *s_ptr <= '9')
12492 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12494 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12495 return ANIM_EVENT_NONE;
12497 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12501 // invalid main animation number specified
12503 return ANIM_EVENT_NONE;
12506 // check for animation part number ("part_X" or "part_XX") (optional)
12507 if (strPrefix(s_ptr, pattern_2))
12509 s_ptr += strlen(pattern_2);
12511 if (*s_ptr >= '0' && *s_ptr <= '9')
12513 int gic_part_nr = (*s_ptr++ - '0');
12515 if (*s_ptr >= '0' && *s_ptr <= '9')
12516 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12518 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12519 return ANIM_EVENT_NONE;
12521 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12525 // invalid animation part number specified
12527 return ANIM_EVENT_NONE;
12531 // discard result if next character is neither delimiter nor whitespace
12532 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12533 *s_ptr == ' ' || *s_ptr == '\t'))
12534 return ANIM_EVENT_NONE;
12539 static int get_anim_parameter_values(char *s)
12541 int list_pos = ANIM_EVENT_UNDEFINED;
12542 int event_value = ANIM_EVENT_DEFAULT;
12544 if (string_has_parameter(s, "any"))
12545 event_value |= ANIM_EVENT_ANY;
12547 if (string_has_parameter(s, "click:self") ||
12548 string_has_parameter(s, "click") ||
12549 string_has_parameter(s, "self"))
12550 event_value |= ANIM_EVENT_SELF;
12552 if (string_has_parameter(s, "unclick:any"))
12553 event_value |= ANIM_EVENT_UNCLICK_ANY;
12555 // if animation event found, add it to global animation event list
12556 if (event_value != ANIM_EVENT_NONE)
12557 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12561 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12562 event_value = get_anim_parameter_value(s);
12564 // if animation event found, add it to global animation event list
12565 if (event_value != ANIM_EVENT_NONE)
12566 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12568 // continue with next part of the string, starting with next comma
12569 s = strchr(s + 1, ',');
12575 static int get_anim_action_parameter_value(char *token)
12577 // check most common default case first to massively speed things up
12578 if (strEqual(token, ARG_UNDEFINED))
12579 return ANIM_EVENT_ACTION_NONE;
12581 int result = getImageIDFromToken(token);
12585 char *gfx_token = getStringCat2("gfx.", token);
12587 result = getImageIDFromToken(gfx_token);
12589 checked_free(gfx_token);
12594 Key key = getKeyFromX11KeyName(token);
12596 if (key != KSYM_UNDEFINED)
12597 result = -(int)key;
12604 result = get_hash_from_string(token); // unsigned int => int
12605 result = ABS(result); // may be negative now
12606 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12608 setHashEntry(anim_url_hash, int2str(result, 0), token);
12613 result = ANIM_EVENT_ACTION_NONE;
12618 int get_parameter_value(char *value_raw, char *suffix, int type)
12620 char *value = getStringToLower(value_raw);
12621 int result = 0; // probably a save default value
12623 if (strEqual(suffix, ".direction"))
12625 result = (strEqual(value, "left") ? MV_LEFT :
12626 strEqual(value, "right") ? MV_RIGHT :
12627 strEqual(value, "up") ? MV_UP :
12628 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12630 else if (strEqual(suffix, ".position"))
12632 result = (strEqual(value, "left") ? POS_LEFT :
12633 strEqual(value, "right") ? POS_RIGHT :
12634 strEqual(value, "top") ? POS_TOP :
12635 strEqual(value, "upper") ? POS_UPPER :
12636 strEqual(value, "middle") ? POS_MIDDLE :
12637 strEqual(value, "lower") ? POS_LOWER :
12638 strEqual(value, "bottom") ? POS_BOTTOM :
12639 strEqual(value, "any") ? POS_ANY :
12640 strEqual(value, "ce") ? POS_CE :
12641 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12642 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12644 else if (strEqual(suffix, ".align"))
12646 result = (strEqual(value, "left") ? ALIGN_LEFT :
12647 strEqual(value, "right") ? ALIGN_RIGHT :
12648 strEqual(value, "center") ? ALIGN_CENTER :
12649 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12651 else if (strEqual(suffix, ".valign"))
12653 result = (strEqual(value, "top") ? VALIGN_TOP :
12654 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12655 strEqual(value, "middle") ? VALIGN_MIDDLE :
12656 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12658 else if (strEqual(suffix, ".anim_mode"))
12660 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12661 string_has_parameter(value, "loop") ? ANIM_LOOP :
12662 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12663 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12664 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12665 string_has_parameter(value, "random") ? ANIM_RANDOM :
12666 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12667 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12668 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12669 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12670 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12671 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12672 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12673 string_has_parameter(value, "all") ? ANIM_ALL :
12674 string_has_parameter(value, "tiled") ? ANIM_TILED :
12675 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12678 if (string_has_parameter(value, "once"))
12679 result |= ANIM_ONCE;
12681 if (string_has_parameter(value, "reverse"))
12682 result |= ANIM_REVERSE;
12684 if (string_has_parameter(value, "opaque_player"))
12685 result |= ANIM_OPAQUE_PLAYER;
12687 if (string_has_parameter(value, "static_panel"))
12688 result |= ANIM_STATIC_PANEL;
12690 else if (strEqual(suffix, ".init_event") ||
12691 strEqual(suffix, ".anim_event"))
12693 result = get_anim_parameter_values(value);
12695 else if (strEqual(suffix, ".init_delay_action") ||
12696 strEqual(suffix, ".anim_delay_action") ||
12697 strEqual(suffix, ".post_delay_action") ||
12698 strEqual(suffix, ".init_event_action") ||
12699 strEqual(suffix, ".anim_event_action"))
12701 result = get_anim_action_parameter_value(value_raw);
12703 else if (strEqual(suffix, ".class"))
12705 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12706 get_hash_from_string(value));
12708 else if (strEqual(suffix, ".style"))
12710 result = STYLE_DEFAULT;
12712 if (string_has_parameter(value, "accurate_borders"))
12713 result |= STYLE_ACCURATE_BORDERS;
12715 if (string_has_parameter(value, "inner_corners"))
12716 result |= STYLE_INNER_CORNERS;
12718 if (string_has_parameter(value, "reverse"))
12719 result |= STYLE_REVERSE;
12721 if (string_has_parameter(value, "leftmost_position"))
12722 result |= STYLE_LEFTMOST_POSITION;
12724 if (string_has_parameter(value, "block_clicks"))
12725 result |= STYLE_BLOCK;
12727 if (string_has_parameter(value, "passthrough_clicks"))
12728 result |= STYLE_PASSTHROUGH;
12730 if (string_has_parameter(value, "multiple_actions"))
12731 result |= STYLE_MULTIPLE_ACTIONS;
12733 if (string_has_parameter(value, "consume_ce_event"))
12734 result |= STYLE_CONSUME_CE_EVENT;
12736 else if (strEqual(suffix, ".fade_mode"))
12738 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12739 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12740 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12741 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12742 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12743 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12744 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12745 FADE_MODE_DEFAULT);
12747 else if (strEqual(suffix, ".auto_delay_unit"))
12749 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12750 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12751 AUTO_DELAY_UNIT_DEFAULT);
12753 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12755 result = gfx.get_font_from_token_function(value);
12757 else // generic parameter of type integer or boolean
12759 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12760 type == TYPE_INTEGER ? get_integer_from_string(value) :
12761 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12762 ARG_UNDEFINED_VALUE);
12770 static int get_token_parameter_value(char *token, char *value_raw)
12774 if (token == NULL || value_raw == NULL)
12775 return ARG_UNDEFINED_VALUE;
12777 suffix = strrchr(token, '.');
12778 if (suffix == NULL)
12781 if (strEqual(suffix, ".element"))
12782 return getElementFromToken(value_raw);
12784 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12785 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12788 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12789 boolean ignore_defaults)
12793 for (i = 0; image_config_vars[i].token != NULL; i++)
12795 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12797 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12798 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12802 *image_config_vars[i].value =
12803 get_token_parameter_value(image_config_vars[i].token, value);
12807 void InitMenuDesignSettings_Static(void)
12809 // always start with reliable default values from static default config
12810 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12813 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12817 // the following initializes hierarchical values from static configuration
12819 // special case: initialize "ARG_DEFAULT" values in static default config
12820 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12821 titlescreen_initial_first_default.fade_mode =
12822 title_initial_first_default.fade_mode;
12823 titlescreen_initial_first_default.fade_delay =
12824 title_initial_first_default.fade_delay;
12825 titlescreen_initial_first_default.post_delay =
12826 title_initial_first_default.post_delay;
12827 titlescreen_initial_first_default.auto_delay =
12828 title_initial_first_default.auto_delay;
12829 titlescreen_initial_first_default.auto_delay_unit =
12830 title_initial_first_default.auto_delay_unit;
12831 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12832 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12833 titlescreen_first_default.post_delay = title_first_default.post_delay;
12834 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12835 titlescreen_first_default.auto_delay_unit =
12836 title_first_default.auto_delay_unit;
12837 titlemessage_initial_first_default.fade_mode =
12838 title_initial_first_default.fade_mode;
12839 titlemessage_initial_first_default.fade_delay =
12840 title_initial_first_default.fade_delay;
12841 titlemessage_initial_first_default.post_delay =
12842 title_initial_first_default.post_delay;
12843 titlemessage_initial_first_default.auto_delay =
12844 title_initial_first_default.auto_delay;
12845 titlemessage_initial_first_default.auto_delay_unit =
12846 title_initial_first_default.auto_delay_unit;
12847 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12848 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12849 titlemessage_first_default.post_delay = title_first_default.post_delay;
12850 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12851 titlemessage_first_default.auto_delay_unit =
12852 title_first_default.auto_delay_unit;
12854 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12855 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12856 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12857 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12858 titlescreen_initial_default.auto_delay_unit =
12859 title_initial_default.auto_delay_unit;
12860 titlescreen_default.fade_mode = title_default.fade_mode;
12861 titlescreen_default.fade_delay = title_default.fade_delay;
12862 titlescreen_default.post_delay = title_default.post_delay;
12863 titlescreen_default.auto_delay = title_default.auto_delay;
12864 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12865 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12866 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12867 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12868 titlemessage_initial_default.auto_delay_unit =
12869 title_initial_default.auto_delay_unit;
12870 titlemessage_default.fade_mode = title_default.fade_mode;
12871 titlemessage_default.fade_delay = title_default.fade_delay;
12872 titlemessage_default.post_delay = title_default.post_delay;
12873 titlemessage_default.auto_delay = title_default.auto_delay;
12874 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12876 // special case: initialize "ARG_DEFAULT" values in static default config
12877 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12878 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12880 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12881 titlescreen_first[i] = titlescreen_first_default;
12882 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12883 titlemessage_first[i] = titlemessage_first_default;
12885 titlescreen_initial[i] = titlescreen_initial_default;
12886 titlescreen[i] = titlescreen_default;
12887 titlemessage_initial[i] = titlemessage_initial_default;
12888 titlemessage[i] = titlemessage_default;
12891 // special case: initialize "ARG_DEFAULT" values in static default config
12892 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12893 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12895 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12898 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12899 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12900 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12903 // special case: initialize "ARG_DEFAULT" values in static default config
12904 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12905 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12907 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12908 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12909 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12911 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12914 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12918 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12922 struct XY *dst, *src;
12924 game_buttons_xy[] =
12926 { &game.button.save, &game.button.stop },
12927 { &game.button.pause2, &game.button.pause },
12928 { &game.button.load, &game.button.play },
12929 { &game.button.undo, &game.button.stop },
12930 { &game.button.redo, &game.button.play },
12936 // special case: initialize later added SETUP list size from LEVELS value
12937 if (menu.list_size[GAME_MODE_SETUP] == -1)
12938 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12940 // set default position for snapshot buttons to stop/pause/play buttons
12941 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12942 if ((*game_buttons_xy[i].dst).x == -1 &&
12943 (*game_buttons_xy[i].dst).y == -1)
12944 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12946 // --------------------------------------------------------------------------
12947 // dynamic viewports (including playfield margins, borders and alignments)
12948 // --------------------------------------------------------------------------
12950 // dynamic viewports currently only supported for landscape mode
12951 int display_width = MAX(video.display_width, video.display_height);
12952 int display_height = MIN(video.display_width, video.display_height);
12954 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12956 struct RectWithBorder *vp_window = &viewport.window[i];
12957 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12958 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12959 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12960 boolean dynamic_window_width = (vp_window->min_width != -1);
12961 boolean dynamic_window_height = (vp_window->min_height != -1);
12962 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12963 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12965 // adjust window size if min/max width/height is specified
12967 if (vp_window->min_width != -1)
12969 int window_width = display_width;
12971 // when using static window height, use aspect ratio of display
12972 if (vp_window->min_height == -1)
12973 window_width = vp_window->height * display_width / display_height;
12975 vp_window->width = MAX(vp_window->min_width, window_width);
12978 if (vp_window->min_height != -1)
12980 int window_height = display_height;
12982 // when using static window width, use aspect ratio of display
12983 if (vp_window->min_width == -1)
12984 window_height = vp_window->width * display_height / display_width;
12986 vp_window->height = MAX(vp_window->min_height, window_height);
12989 if (vp_window->max_width != -1)
12990 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12992 if (vp_window->max_height != -1)
12993 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12995 int playfield_width = vp_window->width;
12996 int playfield_height = vp_window->height;
12998 // adjust playfield size and position according to specified margins
13000 playfield_width -= vp_playfield->margin_left;
13001 playfield_width -= vp_playfield->margin_right;
13003 playfield_height -= vp_playfield->margin_top;
13004 playfield_height -= vp_playfield->margin_bottom;
13006 // adjust playfield size if min/max width/height is specified
13008 if (vp_playfield->min_width != -1)
13009 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13011 if (vp_playfield->min_height != -1)
13012 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13014 if (vp_playfield->max_width != -1)
13015 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13017 if (vp_playfield->max_height != -1)
13018 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13020 // adjust playfield position according to specified alignment
13022 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13023 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13024 else if (vp_playfield->align == ALIGN_CENTER)
13025 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13026 else if (vp_playfield->align == ALIGN_RIGHT)
13027 vp_playfield->x += playfield_width - vp_playfield->width;
13029 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13030 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13031 else if (vp_playfield->valign == VALIGN_MIDDLE)
13032 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13033 else if (vp_playfield->valign == VALIGN_BOTTOM)
13034 vp_playfield->y += playfield_height - vp_playfield->height;
13036 vp_playfield->x += vp_playfield->margin_left;
13037 vp_playfield->y += vp_playfield->margin_top;
13039 // adjust individual playfield borders if only default border is specified
13041 if (vp_playfield->border_left == -1)
13042 vp_playfield->border_left = vp_playfield->border_size;
13043 if (vp_playfield->border_right == -1)
13044 vp_playfield->border_right = vp_playfield->border_size;
13045 if (vp_playfield->border_top == -1)
13046 vp_playfield->border_top = vp_playfield->border_size;
13047 if (vp_playfield->border_bottom == -1)
13048 vp_playfield->border_bottom = vp_playfield->border_size;
13050 // set dynamic playfield borders if borders are specified as undefined
13051 // (but only if window size was dynamic and playfield size was static)
13053 if (dynamic_window_width && !dynamic_playfield_width)
13055 if (vp_playfield->border_left == -1)
13057 vp_playfield->border_left = (vp_playfield->x -
13058 vp_playfield->margin_left);
13059 vp_playfield->x -= vp_playfield->border_left;
13060 vp_playfield->width += vp_playfield->border_left;
13063 if (vp_playfield->border_right == -1)
13065 vp_playfield->border_right = (vp_window->width -
13067 vp_playfield->width -
13068 vp_playfield->margin_right);
13069 vp_playfield->width += vp_playfield->border_right;
13073 if (dynamic_window_height && !dynamic_playfield_height)
13075 if (vp_playfield->border_top == -1)
13077 vp_playfield->border_top = (vp_playfield->y -
13078 vp_playfield->margin_top);
13079 vp_playfield->y -= vp_playfield->border_top;
13080 vp_playfield->height += vp_playfield->border_top;
13083 if (vp_playfield->border_bottom == -1)
13085 vp_playfield->border_bottom = (vp_window->height -
13087 vp_playfield->height -
13088 vp_playfield->margin_bottom);
13089 vp_playfield->height += vp_playfield->border_bottom;
13093 // adjust playfield size to be a multiple of a defined alignment tile size
13095 int align_size = vp_playfield->align_size;
13096 int playfield_xtiles = vp_playfield->width / align_size;
13097 int playfield_ytiles = vp_playfield->height / align_size;
13098 int playfield_width_corrected = playfield_xtiles * align_size;
13099 int playfield_height_corrected = playfield_ytiles * align_size;
13100 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13101 i == GFX_SPECIAL_ARG_EDITOR);
13103 if (is_playfield_mode &&
13104 dynamic_playfield_width &&
13105 vp_playfield->width != playfield_width_corrected)
13107 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13109 vp_playfield->width = playfield_width_corrected;
13111 if (vp_playfield->align == ALIGN_LEFT)
13113 vp_playfield->border_left += playfield_xdiff;
13115 else if (vp_playfield->align == ALIGN_RIGHT)
13117 vp_playfield->border_right += playfield_xdiff;
13119 else if (vp_playfield->align == ALIGN_CENTER)
13121 int border_left_diff = playfield_xdiff / 2;
13122 int border_right_diff = playfield_xdiff - border_left_diff;
13124 vp_playfield->border_left += border_left_diff;
13125 vp_playfield->border_right += border_right_diff;
13129 if (is_playfield_mode &&
13130 dynamic_playfield_height &&
13131 vp_playfield->height != playfield_height_corrected)
13133 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13135 vp_playfield->height = playfield_height_corrected;
13137 if (vp_playfield->valign == VALIGN_TOP)
13139 vp_playfield->border_top += playfield_ydiff;
13141 else if (vp_playfield->align == VALIGN_BOTTOM)
13143 vp_playfield->border_right += playfield_ydiff;
13145 else if (vp_playfield->align == VALIGN_MIDDLE)
13147 int border_top_diff = playfield_ydiff / 2;
13148 int border_bottom_diff = playfield_ydiff - border_top_diff;
13150 vp_playfield->border_top += border_top_diff;
13151 vp_playfield->border_bottom += border_bottom_diff;
13155 // adjust door positions according to specified alignment
13157 for (j = 0; j < 2; j++)
13159 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13161 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13162 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13163 else if (vp_door->align == ALIGN_CENTER)
13164 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13165 else if (vp_door->align == ALIGN_RIGHT)
13166 vp_door->x += vp_window->width - vp_door->width;
13168 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13169 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13170 else if (vp_door->valign == VALIGN_MIDDLE)
13171 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13172 else if (vp_door->valign == VALIGN_BOTTOM)
13173 vp_door->y += vp_window->height - vp_door->height;
13178 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13182 struct XYTileSize *dst, *src;
13185 editor_buttons_xy[] =
13188 &editor.button.element_left, &editor.palette.element_left,
13189 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13192 &editor.button.element_middle, &editor.palette.element_middle,
13193 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13196 &editor.button.element_right, &editor.palette.element_right,
13197 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13204 // set default position for element buttons to element graphics
13205 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13207 if ((*editor_buttons_xy[i].dst).x == -1 &&
13208 (*editor_buttons_xy[i].dst).y == -1)
13210 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13212 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13214 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13218 // adjust editor palette rows and columns if specified to be dynamic
13220 if (editor.palette.cols == -1)
13222 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13223 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13224 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13226 editor.palette.cols = (vp_width - sc_width) / bt_width;
13228 if (editor.palette.x == -1)
13230 int palette_width = editor.palette.cols * bt_width + sc_width;
13232 editor.palette.x = (vp_width - palette_width) / 2;
13236 if (editor.palette.rows == -1)
13238 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13239 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13240 int tx_height = getFontHeight(FONT_TEXT_2);
13242 editor.palette.rows = (vp_height - tx_height) / bt_height;
13244 if (editor.palette.y == -1)
13246 int palette_height = editor.palette.rows * bt_height + tx_height;
13248 editor.palette.y = (vp_height - palette_height) / 2;
13253 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13254 boolean initialize)
13256 // special case: check if network and preview player positions are redefined,
13257 // to compare this later against the main menu level preview being redefined
13258 struct TokenIntPtrInfo menu_config_players[] =
13260 { "main.network_players.x", &menu.main.network_players.redefined },
13261 { "main.network_players.y", &menu.main.network_players.redefined },
13262 { "main.preview_players.x", &menu.main.preview_players.redefined },
13263 { "main.preview_players.y", &menu.main.preview_players.redefined },
13264 { "preview.x", &preview.redefined },
13265 { "preview.y", &preview.redefined }
13271 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13272 *menu_config_players[i].value = FALSE;
13276 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13277 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13278 *menu_config_players[i].value = TRUE;
13282 static void InitMenuDesignSettings_PreviewPlayers(void)
13284 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13287 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13289 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13292 static void LoadMenuDesignSettingsFromFilename(char *filename)
13294 static struct TitleFadingInfo tfi;
13295 static struct TitleMessageInfo tmi;
13296 static struct TokenInfo title_tokens[] =
13298 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13299 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13300 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13301 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13302 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13306 static struct TokenInfo titlemessage_tokens[] =
13308 { TYPE_INTEGER, &tmi.x, ".x" },
13309 { TYPE_INTEGER, &tmi.y, ".y" },
13310 { TYPE_INTEGER, &tmi.width, ".width" },
13311 { TYPE_INTEGER, &tmi.height, ".height" },
13312 { TYPE_INTEGER, &tmi.chars, ".chars" },
13313 { TYPE_INTEGER, &tmi.lines, ".lines" },
13314 { TYPE_INTEGER, &tmi.align, ".align" },
13315 { TYPE_INTEGER, &tmi.valign, ".valign" },
13316 { TYPE_INTEGER, &tmi.font, ".font" },
13317 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13318 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13319 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13320 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13321 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13322 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13323 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13324 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13325 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13331 struct TitleFadingInfo *info;
13336 // initialize first titles from "enter screen" definitions, if defined
13337 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13338 { &title_first_default, "menu.enter_screen.TITLE" },
13340 // initialize title screens from "next screen" definitions, if defined
13341 { &title_initial_default, "menu.next_screen.TITLE" },
13342 { &title_default, "menu.next_screen.TITLE" },
13348 struct TitleMessageInfo *array;
13351 titlemessage_arrays[] =
13353 // initialize first titles from "enter screen" definitions, if defined
13354 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13355 { titlescreen_first, "menu.enter_screen.TITLE" },
13356 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13357 { titlemessage_first, "menu.enter_screen.TITLE" },
13359 // initialize titles from "next screen" definitions, if defined
13360 { titlescreen_initial, "menu.next_screen.TITLE" },
13361 { titlescreen, "menu.next_screen.TITLE" },
13362 { titlemessage_initial, "menu.next_screen.TITLE" },
13363 { titlemessage, "menu.next_screen.TITLE" },
13365 // overwrite titles with title definitions, if defined
13366 { titlescreen_initial_first, "[title_initial]" },
13367 { titlescreen_first, "[title]" },
13368 { titlemessage_initial_first, "[title_initial]" },
13369 { titlemessage_first, "[title]" },
13371 { titlescreen_initial, "[title_initial]" },
13372 { titlescreen, "[title]" },
13373 { titlemessage_initial, "[title_initial]" },
13374 { titlemessage, "[title]" },
13376 // overwrite titles with title screen/message definitions, if defined
13377 { titlescreen_initial_first, "[titlescreen_initial]" },
13378 { titlescreen_first, "[titlescreen]" },
13379 { titlemessage_initial_first, "[titlemessage_initial]" },
13380 { titlemessage_first, "[titlemessage]" },
13382 { titlescreen_initial, "[titlescreen_initial]" },
13383 { titlescreen, "[titlescreen]" },
13384 { titlemessage_initial, "[titlemessage_initial]" },
13385 { titlemessage, "[titlemessage]" },
13389 SetupFileHash *setup_file_hash;
13392 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13395 // the following initializes hierarchical values from dynamic configuration
13397 // special case: initialize with default values that may be overwritten
13398 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13399 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13401 struct TokenIntPtrInfo menu_config[] =
13403 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13404 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13405 { "menu.list_size", &menu.list_size[i] }
13408 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13410 char *token = menu_config[j].token;
13411 char *value = getHashEntry(setup_file_hash, token);
13414 *menu_config[j].value = get_integer_from_string(value);
13418 // special case: initialize with default values that may be overwritten
13419 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13420 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13422 struct TokenIntPtrInfo menu_config[] =
13424 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13425 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13426 { "menu.list_size.INFO", &menu.list_size_info[i] },
13427 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13428 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13431 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13433 char *token = menu_config[j].token;
13434 char *value = getHashEntry(setup_file_hash, token);
13437 *menu_config[j].value = get_integer_from_string(value);
13441 // special case: initialize with default values that may be overwritten
13442 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13443 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13445 struct TokenIntPtrInfo menu_config[] =
13447 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13448 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13451 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13453 char *token = menu_config[j].token;
13454 char *value = getHashEntry(setup_file_hash, token);
13457 *menu_config[j].value = get_integer_from_string(value);
13461 // special case: initialize with default values that may be overwritten
13462 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13463 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13465 struct TokenIntPtrInfo menu_config[] =
13467 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13468 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13469 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13470 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13471 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13472 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13473 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13474 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13475 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13476 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13479 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13481 char *token = menu_config[j].token;
13482 char *value = getHashEntry(setup_file_hash, token);
13485 *menu_config[j].value = get_integer_from_string(value);
13489 // special case: initialize with default values that may be overwritten
13490 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13491 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13493 struct TokenIntPtrInfo menu_config[] =
13495 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13496 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13497 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13498 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13499 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13500 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13501 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13502 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13503 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13506 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13508 char *token = menu_config[j].token;
13509 char *value = getHashEntry(setup_file_hash, token);
13512 *menu_config[j].value = get_token_parameter_value(token, value);
13516 // special case: initialize with default values that may be overwritten
13517 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13518 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13522 char *token_prefix;
13523 struct RectWithBorder *struct_ptr;
13527 { "viewport.window", &viewport.window[i] },
13528 { "viewport.playfield", &viewport.playfield[i] },
13529 { "viewport.door_1", &viewport.door_1[i] },
13530 { "viewport.door_2", &viewport.door_2[i] }
13533 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13535 struct TokenIntPtrInfo vp_config[] =
13537 { ".x", &vp_struct[j].struct_ptr->x },
13538 { ".y", &vp_struct[j].struct_ptr->y },
13539 { ".width", &vp_struct[j].struct_ptr->width },
13540 { ".height", &vp_struct[j].struct_ptr->height },
13541 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13542 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13543 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13544 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13545 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13546 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13547 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13548 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13549 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13550 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13551 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13552 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13553 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13554 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13555 { ".align", &vp_struct[j].struct_ptr->align },
13556 { ".valign", &vp_struct[j].struct_ptr->valign }
13559 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13561 char *token = getStringCat2(vp_struct[j].token_prefix,
13562 vp_config[k].token);
13563 char *value = getHashEntry(setup_file_hash, token);
13566 *vp_config[k].value = get_token_parameter_value(token, value);
13573 // special case: initialize with default values that may be overwritten
13574 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13575 for (i = 0; title_info[i].info != NULL; i++)
13577 struct TitleFadingInfo *info = title_info[i].info;
13578 char *base_token = title_info[i].text;
13580 for (j = 0; title_tokens[j].type != -1; j++)
13582 char *token = getStringCat2(base_token, title_tokens[j].text);
13583 char *value = getHashEntry(setup_file_hash, token);
13587 int parameter_value = get_token_parameter_value(token, value);
13591 *(int *)title_tokens[j].value = (int)parameter_value;
13600 // special case: initialize with default values that may be overwritten
13601 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13602 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13604 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13605 char *base_token = titlemessage_arrays[i].text;
13607 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13609 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13610 char *value = getHashEntry(setup_file_hash, token);
13614 int parameter_value = get_token_parameter_value(token, value);
13616 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13620 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13621 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13623 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13633 // read (and overwrite with) values that may be specified in config file
13634 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13636 // special case: check if network and preview player positions are redefined
13637 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13639 freeSetupFileHash(setup_file_hash);
13642 void LoadMenuDesignSettings(void)
13644 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13646 InitMenuDesignSettings_Static();
13647 InitMenuDesignSettings_SpecialPreProcessing();
13648 InitMenuDesignSettings_PreviewPlayers();
13650 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13652 // first look for special settings configured in level series config
13653 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13655 if (fileExists(filename_base))
13656 LoadMenuDesignSettingsFromFilename(filename_base);
13659 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13661 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13662 LoadMenuDesignSettingsFromFilename(filename_local);
13664 InitMenuDesignSettings_SpecialPostProcessing();
13667 void LoadMenuDesignSettings_AfterGraphics(void)
13669 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13672 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13673 boolean ignore_defaults)
13677 for (i = 0; sound_config_vars[i].token != NULL; i++)
13679 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13681 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13682 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13686 *sound_config_vars[i].value =
13687 get_token_parameter_value(sound_config_vars[i].token, value);
13691 void InitSoundSettings_Static(void)
13693 // always start with reliable default values from static default config
13694 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13697 static void LoadSoundSettingsFromFilename(char *filename)
13699 SetupFileHash *setup_file_hash;
13701 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13704 // read (and overwrite with) values that may be specified in config file
13705 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13707 freeSetupFileHash(setup_file_hash);
13710 void LoadSoundSettings(void)
13712 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13714 InitSoundSettings_Static();
13716 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13718 // first look for special settings configured in level series config
13719 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13721 if (fileExists(filename_base))
13722 LoadSoundSettingsFromFilename(filename_base);
13725 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13727 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13728 LoadSoundSettingsFromFilename(filename_local);
13731 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13733 char *filename = getEditorSetupFilename();
13734 SetupFileList *setup_file_list, *list;
13735 SetupFileHash *element_hash;
13736 int num_unknown_tokens = 0;
13739 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13742 element_hash = newSetupFileHash();
13744 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13745 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13747 // determined size may be larger than needed (due to unknown elements)
13749 for (list = setup_file_list; list != NULL; list = list->next)
13752 // add space for up to 3 more elements for padding that may be needed
13753 *num_elements += 3;
13755 // free memory for old list of elements, if needed
13756 checked_free(*elements);
13758 // allocate memory for new list of elements
13759 *elements = checked_malloc(*num_elements * sizeof(int));
13762 for (list = setup_file_list; list != NULL; list = list->next)
13764 char *value = getHashEntry(element_hash, list->token);
13766 if (value == NULL) // try to find obsolete token mapping
13768 char *mapped_token = get_mapped_token(list->token);
13770 if (mapped_token != NULL)
13772 value = getHashEntry(element_hash, mapped_token);
13774 free(mapped_token);
13780 (*elements)[(*num_elements)++] = atoi(value);
13784 if (num_unknown_tokens == 0)
13787 Warn("unknown token(s) found in config file:");
13788 Warn("- config file: '%s'", filename);
13790 num_unknown_tokens++;
13793 Warn("- token: '%s'", list->token);
13797 if (num_unknown_tokens > 0)
13800 while (*num_elements % 4) // pad with empty elements, if needed
13801 (*elements)[(*num_elements)++] = EL_EMPTY;
13803 freeSetupFileList(setup_file_list);
13804 freeSetupFileHash(element_hash);
13807 for (i = 0; i < *num_elements; i++)
13808 Debug("editor", "element '%s' [%d]\n",
13809 element_info[(*elements)[i]].token_name, (*elements)[i]);
13813 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13816 SetupFileHash *setup_file_hash = NULL;
13817 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13818 char *filename_music, *filename_prefix, *filename_info;
13824 token_to_value_ptr[] =
13826 { "title_header", &tmp_music_file_info.title_header },
13827 { "artist_header", &tmp_music_file_info.artist_header },
13828 { "album_header", &tmp_music_file_info.album_header },
13829 { "year_header", &tmp_music_file_info.year_header },
13830 { "played_header", &tmp_music_file_info.played_header },
13832 { "title", &tmp_music_file_info.title },
13833 { "artist", &tmp_music_file_info.artist },
13834 { "album", &tmp_music_file_info.album },
13835 { "year", &tmp_music_file_info.year },
13836 { "played", &tmp_music_file_info.played },
13842 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13843 getCustomMusicFilename(basename));
13845 if (filename_music == NULL)
13848 // ---------- try to replace file extension ----------
13850 filename_prefix = getStringCopy(filename_music);
13851 if (strrchr(filename_prefix, '.') != NULL)
13852 *strrchr(filename_prefix, '.') = '\0';
13853 filename_info = getStringCat2(filename_prefix, ".txt");
13855 if (fileExists(filename_info))
13856 setup_file_hash = loadSetupFileHash(filename_info);
13858 free(filename_prefix);
13859 free(filename_info);
13861 if (setup_file_hash == NULL)
13863 // ---------- try to add file extension ----------
13865 filename_prefix = getStringCopy(filename_music);
13866 filename_info = getStringCat2(filename_prefix, ".txt");
13868 if (fileExists(filename_info))
13869 setup_file_hash = loadSetupFileHash(filename_info);
13871 free(filename_prefix);
13872 free(filename_info);
13875 if (setup_file_hash == NULL)
13878 // ---------- music file info found ----------
13880 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13882 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13884 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13886 *token_to_value_ptr[i].value_ptr =
13887 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13890 tmp_music_file_info.basename = getStringCopy(basename);
13891 tmp_music_file_info.music = music;
13892 tmp_music_file_info.is_sound = is_sound;
13894 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13895 *new_music_file_info = tmp_music_file_info;
13897 return new_music_file_info;
13900 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13902 return get_music_file_info_ext(basename, music, FALSE);
13905 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13907 return get_music_file_info_ext(basename, sound, TRUE);
13910 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13911 char *basename, boolean is_sound)
13913 for (; list != NULL; list = list->next)
13914 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13920 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13922 return music_info_listed_ext(list, basename, FALSE);
13925 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13927 return music_info_listed_ext(list, basename, TRUE);
13930 void LoadMusicInfo(void)
13932 int num_music_noconf = getMusicListSize_NoConf();
13933 int num_music = getMusicListSize();
13934 int num_sounds = getSoundListSize();
13935 struct FileInfo *music, *sound;
13936 struct MusicFileInfo *next, **new;
13940 while (music_file_info != NULL)
13942 next = music_file_info->next;
13944 checked_free(music_file_info->basename);
13946 checked_free(music_file_info->title_header);
13947 checked_free(music_file_info->artist_header);
13948 checked_free(music_file_info->album_header);
13949 checked_free(music_file_info->year_header);
13950 checked_free(music_file_info->played_header);
13952 checked_free(music_file_info->title);
13953 checked_free(music_file_info->artist);
13954 checked_free(music_file_info->album);
13955 checked_free(music_file_info->year);
13956 checked_free(music_file_info->played);
13958 free(music_file_info);
13960 music_file_info = next;
13963 new = &music_file_info;
13965 // get (configured or unconfigured) music file info for all levels
13966 for (i = leveldir_current->first_level;
13967 i <= leveldir_current->last_level; i++)
13971 if (levelset.music[i] != MUS_UNDEFINED)
13973 // get music file info for configured level music
13974 music_nr = levelset.music[i];
13976 else if (num_music_noconf > 0)
13978 // get music file info for unconfigured level music
13979 int level_pos = i - leveldir_current->first_level;
13981 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13988 char *basename = getMusicInfoEntryFilename(music_nr);
13990 if (basename == NULL)
13993 if (!music_info_listed(music_file_info, basename))
13995 *new = get_music_file_info(basename, music_nr);
13998 new = &(*new)->next;
14002 // get music file info for all remaining configured music files
14003 for (i = 0; i < num_music; i++)
14005 music = getMusicListEntry(i);
14007 if (music->filename == NULL)
14010 if (strEqual(music->filename, UNDEFINED_FILENAME))
14013 // a configured file may be not recognized as music
14014 if (!FileIsMusic(music->filename))
14017 if (!music_info_listed(music_file_info, music->filename))
14019 *new = get_music_file_info(music->filename, i);
14022 new = &(*new)->next;
14026 // get sound file info for all configured sound files
14027 for (i = 0; i < num_sounds; i++)
14029 sound = getSoundListEntry(i);
14031 if (sound->filename == NULL)
14034 if (strEqual(sound->filename, UNDEFINED_FILENAME))
14037 // a configured file may be not recognized as sound
14038 if (!FileIsSound(sound->filename))
14041 if (!sound_info_listed(music_file_info, sound->filename))
14043 *new = get_sound_file_info(sound->filename, i);
14045 new = &(*new)->next;
14049 // add pointers to previous list nodes
14051 struct MusicFileInfo *node = music_file_info;
14053 while (node != NULL)
14056 node->next->prev = node;
14062 static void add_helpanim_entry(int element, int action, int direction,
14063 int delay, int *num_list_entries)
14065 struct HelpAnimInfo *new_list_entry;
14066 (*num_list_entries)++;
14069 checked_realloc(helpanim_info,
14070 *num_list_entries * sizeof(struct HelpAnimInfo));
14071 new_list_entry = &helpanim_info[*num_list_entries - 1];
14073 new_list_entry->element = element;
14074 new_list_entry->action = action;
14075 new_list_entry->direction = direction;
14076 new_list_entry->delay = delay;
14079 static void print_unknown_token(char *filename, char *token, int token_nr)
14084 Warn("unknown token(s) found in config file:");
14085 Warn("- config file: '%s'", filename);
14088 Warn("- token: '%s'", token);
14091 static void print_unknown_token_end(int token_nr)
14097 void LoadHelpAnimInfo(void)
14099 char *filename = getHelpAnimFilename();
14100 SetupFileList *setup_file_list = NULL, *list;
14101 SetupFileHash *element_hash, *action_hash, *direction_hash;
14102 int num_list_entries = 0;
14103 int num_unknown_tokens = 0;
14106 if (fileExists(filename))
14107 setup_file_list = loadSetupFileList(filename);
14109 if (setup_file_list == NULL)
14111 // use reliable default values from static configuration
14112 SetupFileList *insert_ptr;
14114 insert_ptr = setup_file_list =
14115 newSetupFileList(helpanim_config[0].token,
14116 helpanim_config[0].value);
14118 for (i = 1; helpanim_config[i].token; i++)
14119 insert_ptr = addListEntry(insert_ptr,
14120 helpanim_config[i].token,
14121 helpanim_config[i].value);
14124 element_hash = newSetupFileHash();
14125 action_hash = newSetupFileHash();
14126 direction_hash = newSetupFileHash();
14128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14129 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14131 for (i = 0; i < NUM_ACTIONS; i++)
14132 setHashEntry(action_hash, element_action_info[i].suffix,
14133 i_to_a(element_action_info[i].value));
14135 // do not store direction index (bit) here, but direction value!
14136 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14137 setHashEntry(direction_hash, element_direction_info[i].suffix,
14138 i_to_a(1 << element_direction_info[i].value));
14140 for (list = setup_file_list; list != NULL; list = list->next)
14142 char *element_token, *action_token, *direction_token;
14143 char *element_value, *action_value, *direction_value;
14144 int delay = atoi(list->value);
14146 if (strEqual(list->token, "end"))
14148 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14153 /* first try to break element into element/action/direction parts;
14154 if this does not work, also accept combined "element[.act][.dir]"
14155 elements (like "dynamite.active"), which are unique elements */
14157 if (strchr(list->token, '.') == NULL) // token contains no '.'
14159 element_value = getHashEntry(element_hash, list->token);
14160 if (element_value != NULL) // element found
14161 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14162 &num_list_entries);
14165 // no further suffixes found -- this is not an element
14166 print_unknown_token(filename, list->token, num_unknown_tokens++);
14172 // token has format "<prefix>.<something>"
14174 action_token = strchr(list->token, '.'); // suffix may be action ...
14175 direction_token = action_token; // ... or direction
14177 element_token = getStringCopy(list->token);
14178 *strchr(element_token, '.') = '\0';
14180 element_value = getHashEntry(element_hash, element_token);
14182 if (element_value == NULL) // this is no element
14184 element_value = getHashEntry(element_hash, list->token);
14185 if (element_value != NULL) // combined element found
14186 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14187 &num_list_entries);
14189 print_unknown_token(filename, list->token, num_unknown_tokens++);
14191 free(element_token);
14196 action_value = getHashEntry(action_hash, action_token);
14198 if (action_value != NULL) // action found
14200 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14201 &num_list_entries);
14203 free(element_token);
14208 direction_value = getHashEntry(direction_hash, direction_token);
14210 if (direction_value != NULL) // direction found
14212 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14213 &num_list_entries);
14215 free(element_token);
14220 if (strchr(action_token + 1, '.') == NULL)
14222 // no further suffixes found -- this is not an action nor direction
14224 element_value = getHashEntry(element_hash, list->token);
14225 if (element_value != NULL) // combined element found
14226 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14227 &num_list_entries);
14229 print_unknown_token(filename, list->token, num_unknown_tokens++);
14231 free(element_token);
14236 // token has format "<prefix>.<suffix>.<something>"
14238 direction_token = strchr(action_token + 1, '.');
14240 action_token = getStringCopy(action_token);
14241 *strchr(action_token + 1, '.') = '\0';
14243 action_value = getHashEntry(action_hash, action_token);
14245 if (action_value == NULL) // this is no action
14247 element_value = getHashEntry(element_hash, list->token);
14248 if (element_value != NULL) // combined element found
14249 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14250 &num_list_entries);
14252 print_unknown_token(filename, list->token, num_unknown_tokens++);
14254 free(element_token);
14255 free(action_token);
14260 direction_value = getHashEntry(direction_hash, direction_token);
14262 if (direction_value != NULL) // direction found
14264 add_helpanim_entry(atoi(element_value), atoi(action_value),
14265 atoi(direction_value), delay, &num_list_entries);
14267 free(element_token);
14268 free(action_token);
14273 // this is no direction
14275 element_value = getHashEntry(element_hash, list->token);
14276 if (element_value != NULL) // combined element found
14277 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14278 &num_list_entries);
14280 print_unknown_token(filename, list->token, num_unknown_tokens++);
14282 free(element_token);
14283 free(action_token);
14286 print_unknown_token_end(num_unknown_tokens);
14288 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14289 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14291 freeSetupFileList(setup_file_list);
14292 freeSetupFileHash(element_hash);
14293 freeSetupFileHash(action_hash);
14294 freeSetupFileHash(direction_hash);
14297 for (i = 0; i < num_list_entries; i++)
14298 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14299 EL_NAME(helpanim_info[i].element),
14300 helpanim_info[i].element,
14301 helpanim_info[i].action,
14302 helpanim_info[i].direction,
14303 helpanim_info[i].delay);
14307 void LoadHelpTextInfo(void)
14309 char *filename = getHelpTextFilename();
14312 if (helptext_info != NULL)
14314 freeSetupFileHash(helptext_info);
14315 helptext_info = NULL;
14318 if (fileExists(filename))
14319 helptext_info = loadSetupFileHash(filename);
14321 if (helptext_info == NULL)
14323 // use reliable default values from static configuration
14324 helptext_info = newSetupFileHash();
14326 for (i = 0; helptext_config[i].token; i++)
14327 setHashEntry(helptext_info,
14328 helptext_config[i].token,
14329 helptext_config[i].value);
14333 BEGIN_HASH_ITERATION(helptext_info, itr)
14335 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14336 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14338 END_HASH_ITERATION(hash, itr)
14343 // ----------------------------------------------------------------------------
14345 // ----------------------------------------------------------------------------
14347 #define MAX_NUM_CONVERT_LEVELS 1000
14349 void ConvertLevels(void)
14351 static LevelDirTree *convert_leveldir = NULL;
14352 static int convert_level_nr = -1;
14353 static int num_levels_handled = 0;
14354 static int num_levels_converted = 0;
14355 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14358 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14359 global.convert_leveldir);
14361 if (convert_leveldir == NULL)
14362 Fail("no such level identifier: '%s'", global.convert_leveldir);
14364 leveldir_current = convert_leveldir;
14366 if (global.convert_level_nr != -1)
14368 convert_leveldir->first_level = global.convert_level_nr;
14369 convert_leveldir->last_level = global.convert_level_nr;
14372 convert_level_nr = convert_leveldir->first_level;
14374 PrintLine("=", 79);
14375 Print("Converting levels\n");
14376 PrintLine("-", 79);
14377 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14378 Print("Level series name: '%s'\n", convert_leveldir->name);
14379 Print("Level series author: '%s'\n", convert_leveldir->author);
14380 Print("Number of levels: %d\n", convert_leveldir->levels);
14381 PrintLine("=", 79);
14384 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14385 levels_failed[i] = FALSE;
14387 while (convert_level_nr <= convert_leveldir->last_level)
14389 char *level_filename;
14392 level_nr = convert_level_nr++;
14394 Print("Level %03d: ", level_nr);
14396 LoadLevel(level_nr);
14397 if (level.no_level_file || level.no_valid_file)
14399 Print("(no level)\n");
14403 Print("converting level ... ");
14406 // special case: conversion of some EMC levels as requested by ACME
14407 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14410 level_filename = getDefaultLevelFilename(level_nr);
14411 new_level = !fileExists(level_filename);
14415 SaveLevel(level_nr);
14417 num_levels_converted++;
14419 Print("converted.\n");
14423 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14424 levels_failed[level_nr] = TRUE;
14426 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14429 num_levels_handled++;
14433 PrintLine("=", 79);
14434 Print("Number of levels handled: %d\n", num_levels_handled);
14435 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14436 (num_levels_handled ?
14437 num_levels_converted * 100 / num_levels_handled : 0));
14438 PrintLine("-", 79);
14439 Print("Summary (for automatic parsing by scripts):\n");
14440 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14441 convert_leveldir->identifier, num_levels_converted,
14442 num_levels_handled,
14443 (num_levels_handled ?
14444 num_levels_converted * 100 / num_levels_handled : 0));
14446 if (num_levels_handled != num_levels_converted)
14448 Print(", FAILED:");
14449 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14450 if (levels_failed[i])
14455 PrintLine("=", 79);
14457 CloseAllAndExit(0);
14461 // ----------------------------------------------------------------------------
14462 // create and save images for use in level sketches (raw BMP format)
14463 // ----------------------------------------------------------------------------
14465 void CreateLevelSketchImages(void)
14471 InitElementPropertiesGfxElement();
14473 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14474 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14476 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14478 int element = getMappedElement(i);
14479 char basename1[16];
14480 char basename2[16];
14484 sprintf(basename1, "%04d.bmp", i);
14485 sprintf(basename2, "%04ds.bmp", i);
14487 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14488 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14490 DrawSizedElement(0, 0, element, TILESIZE);
14491 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14493 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14494 Fail("cannot save level sketch image file '%s'", filename1);
14496 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14497 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14499 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14500 Fail("cannot save level sketch image file '%s'", filename2);
14505 // create corresponding SQL statements (for normal and small images)
14508 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14509 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14512 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14513 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14515 // optional: create content for forum level sketch demonstration post
14517 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14520 FreeBitmap(bitmap1);
14521 FreeBitmap(bitmap2);
14524 fprintf(stderr, "\n");
14526 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14528 CloseAllAndExit(0);
14532 // ----------------------------------------------------------------------------
14533 // create and save images for element collecting animations (raw BMP format)
14534 // ----------------------------------------------------------------------------
14536 static boolean createCollectImage(int element)
14538 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14541 void CreateCollectElementImages(void)
14545 int anim_frames = num_steps - 1;
14546 int tile_size = TILESIZE;
14547 int anim_width = tile_size * anim_frames;
14548 int anim_height = tile_size;
14549 int num_collect_images = 0;
14550 int pos_collect_images = 0;
14552 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14553 if (createCollectImage(i))
14554 num_collect_images++;
14556 Info("Creating %d element collecting animation images ...",
14557 num_collect_images);
14559 int dst_width = anim_width * 2;
14560 int dst_height = anim_height * num_collect_images / 2;
14561 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14562 char *basename_bmp = "RocksCollect.bmp";
14563 char *basename_png = "RocksCollect.png";
14564 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14565 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14566 int len_filename_bmp = strlen(filename_bmp);
14567 int len_filename_png = strlen(filename_png);
14568 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14569 char cmd_convert[max_command_len];
14571 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14575 // force using RGBA surface for destination bitmap
14576 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14577 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14579 dst_bitmap->surface =
14580 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14582 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14584 if (!createCollectImage(i))
14587 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14588 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14589 int graphic = el2img(i);
14590 char *token_name = element_info[i].token_name;
14591 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14592 Bitmap *src_bitmap;
14595 Info("- creating collecting image for '%s' ...", token_name);
14597 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14599 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14600 tile_size, tile_size, 0, 0);
14602 // force using RGBA surface for temporary bitmap (using transparent black)
14603 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14604 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14606 tmp_bitmap->surface =
14607 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14609 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14611 for (j = 0; j < anim_frames; j++)
14613 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14614 int frame_size = frame_size_final * num_steps;
14615 int offset = (tile_size - frame_size_final) / 2;
14616 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14618 while (frame_size > frame_size_final)
14622 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14624 FreeBitmap(frame_bitmap);
14626 frame_bitmap = half_bitmap;
14629 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14630 frame_size_final, frame_size_final,
14631 dst_x + j * tile_size + offset, dst_y + offset);
14633 FreeBitmap(frame_bitmap);
14636 tmp_bitmap->surface_masked = NULL;
14638 FreeBitmap(tmp_bitmap);
14640 pos_collect_images++;
14643 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14644 Fail("cannot save element collecting image file '%s'", filename_bmp);
14646 FreeBitmap(dst_bitmap);
14648 Info("Converting image file from BMP to PNG ...");
14650 if (system(cmd_convert) != 0)
14651 Fail("converting image file failed");
14653 unlink(filename_bmp);
14657 CloseAllAndExit(0);
14661 // ----------------------------------------------------------------------------
14662 // create and save images for custom and group elements (raw BMP format)
14663 // ----------------------------------------------------------------------------
14665 void CreateCustomElementImages(char *directory)
14667 char *src_basename = "RocksCE-template.ilbm";
14668 char *dst_basename = "RocksCE.bmp";
14669 char *src_filename = getPath2(directory, src_basename);
14670 char *dst_filename = getPath2(directory, dst_basename);
14671 Bitmap *src_bitmap;
14673 int yoffset_ce = 0;
14674 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14677 InitVideoDefaults();
14679 ReCreateBitmap(&backbuffer, video.width, video.height);
14681 src_bitmap = LoadImage(src_filename);
14683 bitmap = CreateBitmap(TILEX * 16 * 2,
14684 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14687 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14694 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14695 TILEX * x, TILEY * y + yoffset_ce);
14697 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14699 TILEX * x + TILEX * 16,
14700 TILEY * y + yoffset_ce);
14702 for (j = 2; j >= 0; j--)
14706 BlitBitmap(src_bitmap, bitmap,
14707 TILEX + c * 7, 0, 6, 10,
14708 TILEX * x + 6 + j * 7,
14709 TILEY * y + 11 + yoffset_ce);
14711 BlitBitmap(src_bitmap, bitmap,
14712 TILEX + c * 8, TILEY, 6, 10,
14713 TILEX * 16 + TILEX * x + 6 + j * 8,
14714 TILEY * y + 10 + yoffset_ce);
14720 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14727 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14728 TILEX * x, TILEY * y + yoffset_ge);
14730 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14732 TILEX * x + TILEX * 16,
14733 TILEY * y + yoffset_ge);
14735 for (j = 1; j >= 0; j--)
14739 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14740 TILEX * x + 6 + j * 10,
14741 TILEY * y + 11 + yoffset_ge);
14743 BlitBitmap(src_bitmap, bitmap,
14744 TILEX + c * 8, TILEY + 12, 6, 10,
14745 TILEX * 16 + TILEX * x + 10 + j * 8,
14746 TILEY * y + 10 + yoffset_ge);
14752 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14753 Fail("cannot save CE graphics file '%s'", dst_filename);
14755 FreeBitmap(bitmap);
14757 CloseAllAndExit(0);